From 112a0c60d2fefb777e88d9868cb4d8a495474ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Fri, 14 Feb 2025 20:46:52 +0100 Subject: [PATCH 01/17] chore: redstone contract wip --- Forc.lock | 6 ++ abis/market_abi/src/abi.sw | 3 + contracts/market/Forc.toml | 5 +- contracts/market/src/lib.sw | 53 ++++++++++++ contracts/market/src/main.sw | 155 ++++++++++++++++++++++++++--------- fetchRedstone.ts | 41 +++++++++ package.json | 3 + 7 files changed, 223 insertions(+), 43 deletions(-) create mode 100644 contracts/market/src/lib.sw create mode 100644 fetchRedstone.ts diff --git a/Forc.lock b/Forc.lock index b5cdbf67..81883f0a 100644 --- a/Forc.lock +++ b/Forc.lock @@ -8,6 +8,7 @@ source = "member" dependencies = [ "market_abi", "pyth_interface", + "redstone", "standards git+https://github.com/FuelLabs/sway-standards?tag=v0.6.1#792639cdf391565e6e6a02482ea8a46d9604a6f5", "std", "sway_libs", @@ -38,6 +39,11 @@ dependencies = [ "std", ] +[[package]] +name = "redstone" +source = "git+https://github.com/redstone-finance/redstone-fuel-sdk?tag=testnet-0.65.2#dc1a0a3550a005db05a2664fac87524ab8acf216" +dependencies = ["std"] + [[package]] name = "src-20" source = "member" diff --git a/abis/market_abi/src/abi.sw b/abis/market_abi/src/abi.sw index f8bb36be..5997dbc0 100644 --- a/abis/market_abi/src/abi.sw +++ b/abis/market_abi/src/abi.sw @@ -101,6 +101,9 @@ abi Market { #[storage(read)] fn get_collateral_reserves(asset_id: AssetId) -> I256; + #[storage(read)] + fn get_redstone_price(feed_ids: Vec, payload_bytes: Bytes) -> (Vec, u64); + // # 8. Pause management #[storage(write)] fn pause(config: PauseConfiguration); diff --git a/contracts/market/Forc.toml b/contracts/market/Forc.toml index c66c7a6f..ee8f1a8e 100644 --- a/contracts/market/Forc.toml +++ b/contracts/market/Forc.toml @@ -5,11 +5,12 @@ name = "market" # address will be the addres of the proxy contract [proxy] -enabled = true address = "0x657ab45a6eb98a4893a99fd104347179151e8b3828fd8f2a108cc09770d1ebae" +enabled = true [dependencies] market_abi = { path = "../../abis/market_abi" } pyth_interface = { git = "https://github.com/pyth-network/pyth-crosschain", rev = "7de4ce3ec8defd97641c1d25302abe117bf5092b" } -sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.24.0" } +redstone = { git = "https://github.com/redstone-finance/redstone-fuel-sdk", tag = "testnet-0.65.2" } standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.6.1" } +sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.24.0" } diff --git a/contracts/market/src/lib.sw b/contracts/market/src/lib.sw new file mode 100644 index 00000000..27e7cc3e --- /dev/null +++ b/contracts/market/src/lib.sw @@ -0,0 +1,53 @@ +library; + +pub trait ArrWrap { + fn _len(self) -> u64; + fn _get(self, i: u64) -> b256; +} + +impl ArrWrap for [b256; 5] { + fn _len(self) -> u64 { + 5 + } + + fn _get(self, i: u64) -> b256 { + self[i] + } +} + +pub trait ToVec: ArrWrap { + fn to_vec(self) -> Vec; +} + +impl ToVec for T +where + T: ArrWrap, +{ + fn to_vec(self) -> Vec { + let mut result = Vec::new(); + let mut i = 0; + while (i < self._len()) { + result.push(self._get(i)); + i += 1; + } + + result + } +} + +impl Vec +where + T: Eq, +{ + pub fn contains(self, value: T) -> bool { + let mut i = 0; + while (i < self.len()) { + if value == self.get(i).unwrap() { + return true; + } + i += 1; + } + + false + } +} diff --git a/contracts/market/src/main.sw b/contracts/market/src/main.sw index 0a47ba4f..54fe2b84 100644 --- a/contracts/market/src/main.sw +++ b/contracts/market/src/main.sw @@ -9,8 +9,13 @@ contract; // mod events; +mod lib; use events::*; +use lib::{ArrWrap, ToVec}; + +use std::{block::timestamp, bytes::Bytes}; +use redstone::{core::config::Config, core::processor::process_input, utils::vec::*,}; use pyth_interface::{data_structures::price::{Price, PriceFeedId}, PythCore}; use market_abi::{Market, structs::*,}; @@ -25,7 +30,6 @@ use std::revert::require; use std::storage::storage_vec::*; use std::u128::U128; use std::vec::Vec; -use std::bytes::Bytes; use std::convert::TryFrom; use sway_libs::reentrancy::reentrancy_guard; use standards::src5::{SRC5, State}; @@ -38,11 +42,18 @@ const VERSION: u8 = 5_u8; // pyth oracle configuration params const ORACLE_MAX_STALENESS: u64 = 60; // 60 seconds const ORACLE_MAX_AHEADNESS: u64 = 60; // 60 seconds -const ORACLE_MAX_CONF_WIDTH: u256 = 300; // 300 / 10000 = 3.0 % - +const ORACLE_MAX_CONF_WIDTH: u256 = 300; // 300 / 10000 = 3.0 % // This is set during deployment of the contract configurable { DEBUG_STEP: u64 = 0, + SIGNER_COUNT_THRESHOLD: u64 = 1, + ALLOWED_SIGNERS: [b256; 5] = [ + 0x0000000000000000000000008BB8F32Df04c8b654987DAaeD53D6B6091e3B774, + 0x000000000000000000000000dEB22f54738d54976C4c0fe5ce6d408E40d88499, + 0x00000000000000000000000051Ce04Be4b3E32572C4Ec9135221d0691Ba7d202, + 0x000000000000000000000000DD682daEC5A90dD295d14DA4b0bec9281017b5bE, + 0x0000000000000000000000009c5AE89C4Af6aA32cE58588DBaF90d18a855B6de, + ], } storage { @@ -204,7 +215,7 @@ impl Market for Contract { /// /// # Number of Storage Accesses /// * Writes: `1` - /// * Reads: `1` + /// * Reads: `1` #[storage(write)] fn pause_collateral_asset(asset_id: AssetId) { // Only owner can pause collateral asset @@ -465,7 +476,6 @@ impl Market for Contract { /// /// # Returns /// * [u64] - The total collateral ammount. - /// # Number of Storage Accesses /// * Writes: `1` #[storage(read)] @@ -670,7 +680,7 @@ impl Market for Contract { /// # Returns /// * [u256] - The amount of base asset supplied by the user. /// * [u256] - The amount of base asset borrowed by the user. - /// + /// /// # Number of Storage Accesses /// * Reads: `3` #[storage(read)] @@ -711,7 +721,11 @@ impl Market for Contract { continue; } - let price = get_price_internal(collateral_configuration.price_feed_id, PricePosition::LowerBound); // decimals: price.exponent + let price = get_price_internal( + collateral_configuration + .price_feed_id, + PricePosition::LowerBound, + ); // decimals: price.exponent let price_exponent = price.exponent; let price_scale = u256::from(10_u64).pow(price.exponent); let price = u256::from(price.price); // decimals: price.exponent @@ -724,12 +738,14 @@ impl Market for Contract { }; // Get the base token price - let base_price = get_price_internal(market_configuration.base_token_price_feed_id, PricePosition::Middle); // decimals: base_price.exponent + let base_price = get_price_internal( + market_configuration + .base_token_price_feed_id, + PricePosition::Middle, + ); // decimals: base_price.exponent let base_price_scale = u256::from(10_u64).pow(base_price.exponent); let base_price = u256::from(base_price.price); // decimals: base_price.exponent - let borrow_limit = borrow_limit * base_price_scale / base_price; // decimals: base_token_decimals - if borrow_limit < borrow { u256::zero() } else { @@ -880,14 +896,22 @@ impl Market for Contract { let market_configuration = storage.market_configuration.read(); // Get the collateral asset price - let asset_price = get_price_internal(collateral_configuration.price_feed_id, PricePosition::UpperBound); // decimals: asset_price.exponent + let asset_price = get_price_internal( + collateral_configuration + .price_feed_id, + PricePosition::UpperBound, + ); // decimals: asset_price.exponent let asset_price_scale = u256::from(10_u64).pow(asset_price.exponent); let asset_price = u256::from(asset_price.price); // decimals: asset_price.exponent let discount_factor: u256 = market_configuration.store_front_price_factor * (FACTOR_SCALE_18 - collateral_configuration.liquidation_penalty) / FACTOR_SCALE_18; // decimals: 18 let asset_price_discounted: u256 = asset_price * (FACTOR_SCALE_18 - discount_factor) / FACTOR_SCALE_18; // decimals: asset_price.exponent // Get the base token price - let base_price = get_price_internal(market_configuration.base_token_price_feed_id, PricePosition::Middle); // decimals: base_price.exponent + let base_price = get_price_internal( + market_configuration + .base_token_price_feed_id, + PricePosition::Middle, + ); // decimals: base_price.exponent let base_price_scale = u256::from(10_u64).pow(base_price.exponent); let base_price = u256::from(base_price.price); // decimals: base_price.exponent let collateral_scale = u256::from(10_u64).pow(collateral_configuration.decimals); @@ -920,6 +944,20 @@ impl Market for Contract { quote_collateral_internal(asset_id, base_amount) } + #[storage(read)] + fn get_redstone_price(feed_ids: Vec, payload_bytes: Bytes) -> (Vec, u64) { + let signer_count_threshold = SIGNER_COUNT_THRESHOLD; + let config = Config { + feed_ids, + // be careful with this array, check xarr.sw for implemented trait + signers: ALLOWED_SIGNERS.to_vec(), + signer_count_threshold, + block_timestamp: timestamp() - (10 + (1 << 62)), + }; + + process_input(payload_bytes, config) + } + // ## 7. Reserves management // ## 7.1 Get reserves @@ -1171,7 +1209,7 @@ impl Market for Contract { } // ## 9.10 Get borrow rate for a given utilization - /// This function calculates the borrow rate based on the market's utilization. + /// This function calculates the borrow rate based on the market's utilization. /// It uses different rates depending on whether the utilization is below or above the kink point. /// /// # Arguments @@ -1364,7 +1402,10 @@ impl SRC5 for Contract { /// # Number of Storage Accesses /// * Reads: `1` #[storage(read)] -fn get_price_internal(price_feed_id: PriceFeedId, price_position: PricePosition) -> Price { +fn get_price_internal( + pyth_price_feed_id: PriceFeedId, + price_position: PricePosition, +) -> Price { let contract_id = storage.pyth_contract_id.read(); require( contract_id != ContractId::zero(), @@ -1372,7 +1413,7 @@ fn get_price_internal(price_feed_id: PriceFeedId, price_position: PricePosition) ); let oracle = abi(PythCore, contract_id.bits()); - let mut price = oracle.price(price_feed_id); + let mut price = oracle.price(pyth_price_feed_id); // validate values if price.publish_time < std::block::timestamp() { @@ -1591,7 +1632,7 @@ fn present_value(principal: I256) -> I256 { } } -/// Calculates the principal value based on the given present value. +/// Calculates the principal value based on the given present value. /// It determines whether the present value is positive or negative to calculate /// the corresponding principal value from either supply or borrow. /// @@ -1680,7 +1721,7 @@ fn get_supply_rate_internal(utilization: u256) -> u256 { } } -/// This function calculates the borrow rate based on the market's utilization. +/// This function calculates the borrow rate based on the market's utilization. /// It uses different rates depending on whether the utilization is below or above the kink point. /// /// # Arguments @@ -1701,8 +1742,8 @@ fn get_borrow_rate_internal(utilization: u256) -> u256 { } } -/// This function calculates the user's supply and borrow amounts for the base asset. -/// It retrieves the user's principal and computes the supply and borrow based on the +/// This function calculates the user's supply and borrow amounts for the base asset. +/// It retrieves the user's principal and computes the supply and borrow based on the /// accrued interest indices. /// /// # Arguments @@ -1728,8 +1769,8 @@ fn get_user_supply_borrow_internal(account: Identity) -> (u256, u256) { } } -/// This function calculates the accrued interest indices for base token supply and borrows. -/// It updates the base supply and borrow indices based on the current timestamp and the +/// This function calculates the accrued interest indices for base token supply and borrows. +/// It updates the base supply and borrow indices based on the current timestamp and the /// timestamp of the last accrual. /// /// # Arguments @@ -1767,12 +1808,11 @@ fn accrued_interest_indices(now: u256, last_accrual_time: u256) -> (u256, u256) base_supply_index += base_supply_index_delta; base_borrow_index += base_borrow_index_delta; } - (base_supply_index, base_borrow_index) } -/// This function checks if the dollar value of the user's collateral, multiplied by the borrow -/// collateral factor, is greater than the planned loan amount. +/// This function checks if the dollar value of the user's collateral, multiplied by the borrow +/// collateral factor, is greater than the planned loan amount. /// It determines whether the user's collateral is sufficient to cover the desired borrow amount. /// /// # Arguments @@ -1786,7 +1826,6 @@ fn accrued_interest_indices(now: u256, last_accrual_time: u256) -> (u256, u256) #[storage(read)] fn is_borrow_collateralized(account: Identity) -> bool { let principal = storage.user_basic.get(account).try_read().unwrap_or(UserBasic::default()).principal; // decimals: base_asset_decimal - if principal >= I256::zero() { return true }; @@ -1801,13 +1840,16 @@ fn is_borrow_collateralized(account: Identity) -> bool { let collateral_configuration = storage.collateral_configurations.get(storage.collateral_configurations_keys.get(index).unwrap().read()).read(); let balance: u256 = storage.user_collateral.get((account, collateral_configuration.asset_id)).try_read().unwrap_or(0).into(); // decimals: collateral_configuration.decimals - if balance == 0 { index += 1; continue; } - let price = get_price_internal(collateral_configuration.price_feed_id, PricePosition::LowerBound); // decimals: price.exponent decimals + let price = get_price_internal( + collateral_configuration + .price_feed_id, + PricePosition::LowerBound, + ); // decimals: price.exponent decimals let price_scale = u256::from(10_u64).pow(price.exponent); let price = u256::from(price.price); // decimals: price.exponent let collateral_scale = u256::from(10_u64).pow(collateral_configuration.decimals); @@ -1818,11 +1860,16 @@ fn is_borrow_collateralized(account: Identity) -> bool { index += 1; } - let base_token_price = get_price_internal(storage.market_configuration.read().base_token_price_feed_id, PricePosition::Middle); // decimals: base_token_price.exponent + let base_token_price = get_price_internal( + storage + .market_configuration + .read() + .base_token_price_feed_id, + PricePosition::Middle, + ); // decimals: base_token_price.exponent let base_token_price_scale = u256::from(10_u64).pow(base_token_price.exponent); let base_token_price = u256::from(base_token_price.price); let borrow_amount = u256::try_from(present.wrapping_neg()).unwrap() * base_token_price / base_token_price_scale; // decimals: base_token_decimals - borrow_amount <= borrow_limit } @@ -1845,7 +1892,6 @@ fn is_liquidatable_internal(account: Identity, present: I256) -> bool { }; let present: u256 = present.wrapping_neg().try_into().unwrap(); // decimals: base_token_decimals - let mut liquidation_treshold: u256 = 0; let mut index = 0; @@ -1855,13 +1901,16 @@ fn is_liquidatable_internal(account: Identity, present: I256) -> bool { let collateral_configuration = storage.collateral_configurations.get(storage.collateral_configurations_keys.get(index).unwrap().read()).read(); let balance: u256 = storage.user_collateral.get((account, collateral_configuration.asset_id)).try_read().unwrap_or(0).into(); // decimals: collateral_configuration.decimals - if balance == 0 { index += 1; continue; } - let price = get_price_internal(collateral_configuration.price_feed_id, PricePosition::LowerBound); // decimals: price.exponent + let price = get_price_internal( + collateral_configuration + .price_feed_id, + PricePosition::LowerBound, + ); // decimals: price.exponent let price_scale = u256::from(10.pow(price.exponent)); let price = u256::from(price.price); // decimals: price.exponent let collateral_scale = u256::from(10_u64).pow(collateral_configuration.decimals); @@ -1872,11 +1921,16 @@ fn is_liquidatable_internal(account: Identity, present: I256) -> bool { index += 1; } - let base_token_price = get_price_internal(storage.market_configuration.read().base_token_price_feed_id, PricePosition::Middle); // decimals: base_token_price.exponent + let base_token_price = get_price_internal( + storage + .market_configuration + .read() + .base_token_price_feed_id, + PricePosition::Middle, + ); // decimals: base_token_price.exponent let base_token_price_scale = u256::from(10_u64).pow(base_token_price.exponent); let base_token_price = u256::from(base_token_price.price); // decimals: base_token_price.exponent let borrow_amount = present * base_token_price / base_token_price_scale; // decimals: base_token_decimals - borrow_amount > liquidation_treshold } @@ -1897,7 +1951,7 @@ fn get_collateral_reserves_internal(asset_id: AssetId) -> I256 { } /// This function calculates and returns the total amount of protocol reserves of the base asset. -/// It takes into account the current balance, the total supply, and the total borrow values, +/// It takes into account the current balance, the total supply, and the total borrow values, /// adjusted by the accrued interest indices. /// /// # Returns @@ -2095,12 +2149,20 @@ fn quote_collateral_internal(asset_id: AssetId, base_amount: u64) -> u64 { let market_configuration = storage.market_configuration.read(); // Get the asset price - let asset_price = get_price_internal(collateral_configuration.price_feed_id, PricePosition::UpperBound); // decimals: asset_price.exponent + let asset_price = get_price_internal( + collateral_configuration + .price_feed_id, + PricePosition::UpperBound, + ); // decimals: asset_price.exponent let asset_price_scale = u256::from(10_u64).pow(asset_price.exponent); let asset_price = u256::from(asset_price.price); // decimals: asset_price.exponent // Get the base token price - let base_price = get_price_internal(market_configuration.base_token_price_feed_id, PricePosition::Middle); // decimals: base_price.exponent + let base_price = get_price_internal( + market_configuration + .base_token_price_feed_id, + PricePosition::Middle, + ); // decimals: base_price.exponent let base_price_scale = u256::from(10_u64).pow(base_price.exponent); let base_price = u256::from(base_price.price); // decimals: base_price.exponent let discount_factor: u256 = market_configuration.store_front_price_factor * (FACTOR_SCALE_18 - collateral_configuration.liquidation_penalty) / FACTOR_SCALE_18; // decimals: 18 @@ -2165,9 +2227,12 @@ fn absorb_internal(account: Identity) { let user_basic = storage.user_basic.get(account).try_read().unwrap_or(UserBasic::default()); let old_principal = user_basic.principal; let old_balance = present_value(old_principal); // decimals: base_token_decimals - + // Check that the account is liquidatable - require(is_liquidatable_internal(account, old_balance), Error::NotLiquidatable); + require( + is_liquidatable_internal(account, old_balance), + Error::NotLiquidatable, + ); let mut delta_value: u256 = 0; // decimals: 18 let market_configuration = storage.market_configuration.read(); @@ -2205,7 +2270,11 @@ fn absorb_internal(account: Identity) { ); // Get price of the collateral asset - let price = get_price_internal(collateral_configuration.price_feed_id, PricePosition::LowerBound); // decimals: price.exponent + let price = get_price_internal( + collateral_configuration + .price_feed_id, + PricePosition::LowerBound, + ); // decimals: price.exponent let price_exponent = price.exponent; let price_scale = u256::from(10_u64).pow(price.exponent); let price = u256::from(price.price); // decimals: price.exponent @@ -2230,7 +2299,11 @@ fn absorb_internal(account: Identity) { } // Get the base token price - let base_price = get_price_internal(market_configuration.base_token_price_feed_id, PricePosition::Middle); // decimals: base_token_price.exponent + let base_price = get_price_internal( + market_configuration + .base_token_price_feed_id, + PricePosition::Middle, + ); // decimals: base_token_price.exponent let base_price_exponent = base_price.exponent; let base_price_scale = u256::from(10_u64).pow(base_price.exponent); let base_price = u256::from(base_price.price); // decimals: base_token_price.exponent diff --git a/fetchRedstone.ts b/fetchRedstone.ts new file mode 100644 index 00000000..8d0c425b --- /dev/null +++ b/fetchRedstone.ts @@ -0,0 +1,41 @@ +import { ContractParamsProvider } from '@redstone-finance/sdk'; +import http from 'node:http'; +import https from 'node:https'; + +const originalHttpRequest = http.request; +http.request = function (...args) { + console.log('🚀 ~ http.request ~ args: ', args); + console.log( + 'HTTP Request:', + `${args[0]?.protocol}//${args[0]?.hostname}${args[0]?.path}` + ); + return originalHttpRequest.apply(this, args); +}; + +const originalHttpsRequest = https.request; +https.request = function (...args) { + console.log('🚀 ~ http.request ~ args: ', args); + console.log( + 'HTTPS Request:', + `${args[0]?.protocol}//${args[0]?.hostname}${args[0]?.path}` + ); + return originalHttpsRequest.apply(this, args); +}; + +async function main() { + const cpp: ContractParamsProvider = new ContractParamsProvider({ + dataServiceId: 'redstone-primary-prod', + dataPackagesIds: ['BTC', 'ETH'], + uniqueSignersCount: 2, + }); + const dataPackages = await cpp.requestDataPackages(); + const payloadHex = await cpp.getPayloadData(); + const hexlified = cpp.getHexlifiedFeedIds(); + const dataFeedIds = cpp.getDataFeedIds(); + console.log('🚀 ~ main ~ dataPackages: ', dataPackages); + console.log('🚀 ~ main ~ payloadBytes: ', payloadHex); + console.log('🚀 ~ main ~ hexlified: ', hexlified); + console.log('🚀 ~ main ~ feedIds: ', dataFeedIds); +} + +main().catch(console.error); diff --git a/package.json b/package.json index 70ac88ae..ab552c24 100644 --- a/package.json +++ b/package.json @@ -36,5 +36,8 @@ "@fuel-ts/versions": "0.96.1", "@fuel-ts/program": "0.96.1" } + }, + "dependencies": { + "@redstone-finance/sdk": "0.7.5" } } From 8cf68e46b2dac11e9befd631eac0861785c29699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Mon, 17 Feb 2025 20:30:26 +0100 Subject: [PATCH 02/17] feat: add redstone integration to contracts --- abis/market_abi/src/abi.sw | 26 +-- abis/market_abi/src/structs.sw | 6 + contracts/market/src/main.sw | 184 +++++++++++++----- .../tests/local_tests/main_test_eth_base.rs | 9 +- .../market/tests/local_tests/main_test_uni.rs | 9 +- .../main_test_uni_no_debug_mode.rs | 8 +- .../tests/local_tests/main_test_usdt.rs | 9 +- .../local_tests/scenarios/configuration.rs | 1 + .../local_tests/scenarios/liquidation.rs | 16 +- .../multicall_absorb_buy_collateral.rs | 15 +- .../scenarios/multicall_withdraw_supply.rs | 8 +- .../tests/local_tests/scenarios/owner.rs | 1 + .../scenarios/update_curve_parameters.rs | 9 +- contracts/market/tests/market-config.json | 1 + contracts/market/tests/utils/mod.rs | 2 +- fetchRedstone.ts | 2 +- libs/market_sdk/src/market_utils.rs | 56 ++++-- libs/token_sdk/src/token_utils.rs | 5 +- scripts/src/utils.rs | 2 + 19 files changed, 279 insertions(+), 90 deletions(-) diff --git a/abis/market_abi/src/abi.sw b/abis/market_abi/src/abi.sw index 5997dbc0..fdb5c286 100644 --- a/abis/market_abi/src/abi.sw +++ b/abis/market_abi/src/abi.sw @@ -45,7 +45,12 @@ abi Market { fn supply_collateral(); // Payment is required: any collateral asset #[payable, storage(write)] - fn withdraw_collateral(asset_id: AssetId, amount: u64, price_data_update: PriceDataUpdate); + fn withdraw_collateral( + asset_id: AssetId, + amount: u64, + price_data_update: PriceDataUpdate, + redstone_payload: Bytes, + ); #[storage(read)] fn get_user_collateral(account: Identity, asset_id: AssetId) -> u64; @@ -65,31 +70,31 @@ abi Market { fn supply_base(); // Payment is required: base asset (USDC) #[payable, storage(write)] - fn withdraw_base(amount: u64, price_data_update: PriceDataUpdate); + fn withdraw_base(amount: u64, price_data_update: PriceDataUpdate, redstone_payload: Bytes); #[storage(read)] fn get_user_supply_borrow(account: Identity) -> (u256, u256); #[storage(read)] - fn available_to_borrow(account: Identity) -> u256; + fn available_to_borrow(account: Identity, redstone_payload: Bytes) -> u256; // # 5. Liquidation management // Liquidates the user if there is insufficient collateral for the borrowing. #[payable, storage(write)] - fn absorb(accounts: Vec, price_data_update: PriceDataUpdate); + fn absorb(accounts: Vec, price_data_update: PriceDataUpdate, redstone_payload: Bytes); #[storage(read)] - fn is_liquidatable(account: Identity) -> bool; + fn is_liquidatable(account: Identity, redstone_payload: Bytes) -> bool; // # 6. Protocol collateral management #[payable, storage(read)] - fn buy_collateral(asset_id: AssetId, min_amount: u64, recipient: Identity); // Payment is required: base asset (USDC) + fn buy_collateral(asset_id: AssetId, min_amount: u64, recipient: Identity, redstone_payload: Bytes); // Payment is required: base asset (USDC) #[storage(read)] - fn collateral_value_to_sell(asset_id: AssetId, collateral_amount: u64) -> u64; + fn collateral_value_to_sell(asset_id: AssetId, collateral_amount: u64, redstone_payload: Bytes) -> u64; #[storage(read)] - fn quote_collateral(asset_id: AssetId, base_amount: u64) -> u64; + fn quote_collateral(asset_id: AssetId, base_amount: u64, redstone_payload: Bytes) -> u64; // ## 7. Reserves management #[storage(read)] @@ -101,9 +106,6 @@ abi Market { #[storage(read)] fn get_collateral_reserves(asset_id: AssetId) -> I256; - #[storage(read)] - fn get_redstone_price(feed_ids: Vec, payload_bytes: Bytes) -> (Vec, u64); - // # 8. Pause management #[storage(write)] fn pause(config: PauseConfiguration); @@ -147,7 +149,7 @@ abi Market { fn get_pyth_contract_id() -> ContractId; #[storage(read)] - fn get_price(price_feed_id: PriceFeedId) -> Price; + fn get_price(price_feed_id: PriceFeedId, redstone_feed_id: u256, redstone_payload: Bytes) -> Price; #[storage(read)] fn update_fee(update_data: Vec) -> u64; diff --git a/abis/market_abi/src/structs.sw b/abis/market_abi/src/structs.sw index 4004341e..71ef1b49 100644 --- a/abis/market_abi/src/structs.sw +++ b/abis/market_abi/src/structs.sw @@ -8,6 +8,7 @@ pub const BASE_ACCRUAL_SCALE: u256 = 1_000_000; // 1e6 pub const BASE_INDEX_SCALE_15: u256 = 1_000_000_000_000_000; // 1e15 pub const FACTOR_SCALE_18: u256 = 1_000_000_000_000_000_000; // 1e18 pub const ORACLE_CONF_BASIS_POINTS: u256 = 10_000; // 1e4 +pub const TAI64_UNIX_ADJUSTMENT = (10 + (1 << 62)); /// This struct contains the configuration details for collateral management. pub struct CollateralConfiguration { @@ -15,6 +16,8 @@ pub struct CollateralConfiguration { pub asset_id: AssetId, /// This field holds the price feed ID for the asset. pub price_feed_id: b256, + /// This field holds the redstone feed ID for the asset. + pub redstone_feed_id: u256, /// This field represents the number of decimals for the asset. pub decimals: u32, /// This field represents the collateral factor for borrowing. @@ -37,6 +40,8 @@ pub struct MarketConfiguration { pub base_token_decimals: u32, /// This field holds the price feed ID for the base token. pub base_token_price_feed_id: b256, + /// This field holds the redstone feed ID for the base token. + pub base_token_redstone_feed_id: u256, /// This field represents the supply kink. pub supply_kink: u256, // decimals: 18 /// This field represents the borrow kink. @@ -75,6 +80,7 @@ impl MarketConfiguration { base_token: AssetId::zero(), base_token_decimals: 0, base_token_price_feed_id: b256::zero(), + base_token_redstone_feed_id: 0, supply_kink: 0, borrow_kink: 0, supply_per_second_interest_rate_slope_low: 0, diff --git a/contracts/market/src/main.sw b/contracts/market/src/main.sw index 54fe2b84..a268301f 100644 --- a/contracts/market/src/main.sw +++ b/contracts/market/src/main.sw @@ -41,8 +41,10 @@ const VERSION: u8 = 5_u8; // pyth oracle configuration params const ORACLE_MAX_STALENESS: u64 = 60; // 60 seconds +const ORACLE_DOWNTIME_THRESHOLD: u64 = 300; // 5 minutes const ORACLE_MAX_AHEADNESS: u64 = 60; // 60 seconds const ORACLE_MAX_CONF_WIDTH: u256 = 300; // 300 / 10000 = 3.0 % +const REDSTONE_PRICE_EXPONENT: u32 = 8; // This is set during deployment of the contract configurable { DEBUG_STEP: u64 = 0, @@ -391,6 +393,7 @@ impl Market for Contract { asset_id: AssetId, amount: u64, price_data_update: PriceDataUpdate, + redstone_payload: Bytes, ) { reentrancy_guard(); @@ -413,7 +416,10 @@ impl Market for Contract { // Note: no accrue interest, BorrowCollateralFactor < LiquidationCollateralFactor covers small changes // Check if the user is borrow collateralized - require(is_borrow_collateralized(caller), Error::NotCollateralized); + require( + is_borrow_collateralized(caller, redstone_payload), + Error::NotCollateralized, + ); transfer(caller, asset_id, amount); @@ -598,7 +604,11 @@ impl Market for Contract { /// * Writes: `3` /// * Reads: `5` #[payable, storage(write)] - fn withdraw_base(amount: u64, price_data_update: PriceDataUpdate) { + fn withdraw_base( + amount: u64, + price_data_update: PriceDataUpdate, + redstone_payload: Bytes, + ) { reentrancy_guard(); // Only allow withdrawing if paused flag is not set @@ -657,7 +667,10 @@ impl Market for Contract { update_price_feeds_if_necessary_internal(price_data_update); // Check that the user is borrow collateralized - require(is_borrow_collateralized(caller), Error::NotCollateralized); + require( + is_borrow_collateralized(caller, redstone_payload), + Error::NotCollateralized, + ); } // Transfer base asset to the caller @@ -700,7 +713,7 @@ impl Market for Contract { /// # Number of Storage Accesses /// * Reads: `4 + storage.collateral_configurations_keys.len() * 5` #[storage(read)] - fn available_to_borrow(account: Identity) -> u256 { + fn available_to_borrow(account: Identity, redstone_payload: Bytes) -> u256 { // Get user's supply and borrow let (_, borrow) = get_user_supply_borrow_internal(account); @@ -724,6 +737,9 @@ impl Market for Contract { let price = get_price_internal( collateral_configuration .price_feed_id, + collateral_configuration + .redstone_feed_id, + redstone_payload, PricePosition::LowerBound, ); // decimals: price.exponent let price_exponent = price.exponent; @@ -741,6 +757,9 @@ impl Market for Contract { let base_price = get_price_internal( market_configuration .base_token_price_feed_id, + market_configuration + .base_token_redstone_feed_id, + redstone_payload, PricePosition::Middle, ); // decimals: base_price.exponent let base_price_scale = u256::from(10_u64).pow(base_price.exponent); @@ -767,7 +786,11 @@ impl Market for Contract { /// * Writes: `2 + accounts.len() * 4` /// * Reads: `5 + accounts.len() * 5` #[payable, storage(write)] - fn absorb(accounts: Vec, price_data_update: PriceDataUpdate) { + fn absorb( + accounts: Vec, + price_data_update: PriceDataUpdate, + redstone_payload: Bytes, + ) { reentrancy_guard(); // Check that the pause flag is not set @@ -782,7 +805,7 @@ impl Market for Contract { let mut index = 0; // Loop and absorb each account while index < accounts.len() { - absorb_internal(accounts.get(index).unwrap()); + absorb_internal(accounts.get(index).unwrap(), redstone_payload); index += 1; } } @@ -799,9 +822,9 @@ impl Market for Contract { /// # Number of Storage Accesses /// * Reads: 1 #[storage(read)] - fn is_liquidatable(account: Identity) -> bool { + fn is_liquidatable(account: Identity, redstone_payload: Bytes) -> bool { let present = get_user_balance_with_interest_internal(account); - is_liquidatable_internal(account, present) + is_liquidatable_internal(account, present, redstone_payload) } // # 6. Protocol collateral management @@ -824,7 +847,12 @@ impl Market for Contract { /// # Number of Storage Accesses /// * Reads: `8` #[payable, storage(read)] - fn buy_collateral(asset_id: AssetId, min_amount: u64, recipient: Identity) { + fn buy_collateral( + asset_id: AssetId, + min_amount: u64, + recipient: Identity, + redstone_payload: Bytes, + ) { reentrancy_guard(); // Only allow buying collateral if paused flag is not set @@ -852,7 +880,7 @@ impl Market for Contract { let reserves = get_collateral_reserves_internal(asset_id); // Calculate the quote for a collateral asset in exchange for an amount of the base asset - let collateral_amount = quote_collateral_internal(asset_id, payment_amount); + let collateral_amount = quote_collateral_internal(asset_id, payment_amount, redstone_payload); // Check that the quote is greater than or equal to the minimum requested amount require(collateral_amount >= min_amount, Error::TooMuchSlippage); @@ -891,7 +919,11 @@ impl Market for Contract { /// # Number of Storage Accesses /// * Reads: `5` #[storage(read)] - fn collateral_value_to_sell(asset_id: AssetId, collateral_amount: u64) -> u64 { // decimals: base_token_decimals + fn collateral_value_to_sell( + asset_id: AssetId, + collateral_amount: u64, + redstone_payload: Bytes, + ) -> u64 { // decimals: base_token_decimals let collateral_configuration = storage.collateral_configurations.get(asset_id).read(); let market_configuration = storage.market_configuration.read(); @@ -899,6 +931,9 @@ impl Market for Contract { let asset_price = get_price_internal( collateral_configuration .price_feed_id, + collateral_configuration + .redstone_feed_id, + redstone_payload, PricePosition::UpperBound, ); // decimals: asset_price.exponent let asset_price_scale = u256::from(10_u64).pow(asset_price.exponent); @@ -910,6 +945,9 @@ impl Market for Contract { let base_price = get_price_internal( market_configuration .base_token_price_feed_id, + market_configuration + .base_token_redstone_feed_id, + redstone_payload, PricePosition::Middle, ); // decimals: base_price.exponent let base_price_scale = u256::from(10_u64).pow(base_price.exponent); @@ -940,22 +978,8 @@ impl Market for Contract { /// # Number of Storage Accesses /// * Reads: `2` #[storage(read)] - fn quote_collateral(asset_id: AssetId, base_amount: u64) -> u64 { - quote_collateral_internal(asset_id, base_amount) - } - - #[storage(read)] - fn get_redstone_price(feed_ids: Vec, payload_bytes: Bytes) -> (Vec, u64) { - let signer_count_threshold = SIGNER_COUNT_THRESHOLD; - let config = Config { - feed_ids, - // be careful with this array, check xarr.sw for implemented trait - signers: ALLOWED_SIGNERS.to_vec(), - signer_count_threshold, - block_timestamp: timestamp() - (10 + (1 << 62)), - }; - - process_input(payload_bytes, config) + fn quote_collateral(asset_id: AssetId, base_amount: u64, redstone_payload: Bytes) -> u64 { + quote_collateral_internal(asset_id, base_amount, redstone_payload) } // ## 7. Reserves management @@ -1275,8 +1299,17 @@ impl Market for Contract { /// # Number of Storage Accesses /// * Reads: `1` #[storage(read)] - fn get_price(price_feed_id: PriceFeedId) -> Price { - get_price_internal(price_feed_id, PricePosition::Middle) + fn get_price( + price_feed_id: PriceFeedId, + redstone_feed_id: u256, + redstone_payload: Bytes, + ) -> Price { + get_price_internal( + price_feed_id, + redstone_feed_id, + redstone_payload, + PricePosition::Middle, + ) } /// This function interacts with an external oracle to obtain the update fee and ensures that the contract ID is valid. @@ -1385,6 +1418,21 @@ impl SRC5 for Contract { } } +#[storage(read)] +fn get_redstone_price_internal(feed_ids: Vec, payload_bytes: Bytes) -> (u256, u64) { + let signer_count_threshold = SIGNER_COUNT_THRESHOLD; + let config = Config { + feed_ids: feed_ids, + // be careful with this array, check xarr.sw for implemented trait + signers: ALLOWED_SIGNERS.to_vec(), + signer_count_threshold, + block_timestamp: timestamp() - TAI64_UNIX_ADJUSTMENT, + }; + + let (price, timestamp) = process_input(payload_bytes, config); + (price.get(0).unwrap(), timestamp) +} + /// This function ensures that the price data is fresh and meets the required validation criteria. /// /// # Arguments @@ -1401,9 +1449,12 @@ impl SRC5 for Contract { /// /// # Number of Storage Accesses /// * Reads: `1` +// TODO: finish this function #[storage(read)] fn get_price_internal( pyth_price_feed_id: PriceFeedId, + redstone_feed_id: u256, + redstone_payload: Bytes, price_position: PricePosition, ) -> Price { let contract_id = storage.pyth_contract_id.read(); @@ -1414,20 +1465,31 @@ fn get_price_internal( let oracle = abi(PythCore, contract_id.bits()); let mut price = oracle.price(pyth_price_feed_id); - - // validate values - if price.publish_time < std::block::timestamp() { - let staleness = std::block::timestamp() - price.publish_time; - require( - staleness <= ORACLE_MAX_STALENESS, - Error::OraclePriceValidationError, - ); + // Validate values + let now = std::block::timestamp(); + let time_diff = if price.publish_time < now { + now - price.publish_time } else { - let aheadness = price.publish_time - std::block::timestamp(); - require( - aheadness <= ORACLE_MAX_AHEADNESS, - Error::OraclePriceValidationError, - ); + price.publish_time - now + }; + + require( + time_diff < ORACLE_DOWNTIME_THRESHOLD, + Error::OraclePriceValidationError, + ); + + if time_diff > ORACLE_MAX_STALENESS + || time_diff > ORACLE_MAX_AHEADNESS + || price.price == 0 + { + let mut price_feeds: Vec = Vec::new(); + price_feeds.push(redstone_feed_id); + let (redstone_price, _) = get_redstone_price_internal(price_feeds, redstone_payload); + let price_from_u256: Option = >::try_from(redstone_price); + price.price = price_from_u256.unwrap(); + price.exponent = REDSTONE_PRICE_EXPONENT; + price.confidence = 0; + price.publish_time = now; } require(price.price != 0, Error::OraclePriceValidationError); @@ -1824,7 +1886,7 @@ fn accrued_interest_indices(now: u256, last_accrual_time: u256) -> (u256, u256) /// # Number of Storage Accesses /// * Reads: `4 + storage.collateral_configurations_keys.len() * 4` #[storage(read)] -fn is_borrow_collateralized(account: Identity) -> bool { +fn is_borrow_collateralized(account: Identity, redstone_payload: Bytes) -> bool { let principal = storage.user_basic.get(account).try_read().unwrap_or(UserBasic::default()).principal; // decimals: base_asset_decimal if principal >= I256::zero() { return true @@ -1848,6 +1910,9 @@ fn is_borrow_collateralized(account: Identity) -> bool { let price = get_price_internal( collateral_configuration .price_feed_id, + collateral_configuration + .redstone_feed_id, + redstone_payload, PricePosition::LowerBound, ); // decimals: price.exponent decimals let price_scale = u256::from(10_u64).pow(price.exponent); @@ -1865,6 +1930,11 @@ fn is_borrow_collateralized(account: Identity) -> bool { .market_configuration .read() .base_token_price_feed_id, + storage + .market_configuration + .read() + .base_token_redstone_feed_id, + redstone_payload, PricePosition::Middle, ); // decimals: base_token_price.exponent let base_token_price_scale = u256::from(10_u64).pow(base_token_price.exponent); @@ -1886,7 +1956,7 @@ fn is_borrow_collateralized(account: Identity) -> bool { /// # Number of Storage Accesses /// * Reads: `4 + storage.collateral_configurations_keys.len() * 4` #[storage(read)] -fn is_liquidatable_internal(account: Identity, present: I256) -> bool { +fn is_liquidatable_internal(account: Identity, present: I256, redstone_payload: Bytes) -> bool { if present >= I256::zero() { return false }; @@ -1909,6 +1979,9 @@ fn is_liquidatable_internal(account: Identity, present: I256) -> bool { let price = get_price_internal( collateral_configuration .price_feed_id, + collateral_configuration + .redstone_feed_id, + redstone_payload, PricePosition::LowerBound, ); // decimals: price.exponent let price_scale = u256::from(10.pow(price.exponent)); @@ -1926,6 +1999,11 @@ fn is_liquidatable_internal(account: Identity, present: I256) -> bool { .market_configuration .read() .base_token_price_feed_id, + storage + .market_configuration + .read() + .base_token_redstone_feed_id, + redstone_payload, PricePosition::Middle, ); // decimals: base_token_price.exponent let base_token_price_scale = u256::from(10_u64).pow(base_token_price.exponent); @@ -2144,7 +2222,7 @@ fn withdraw_and_borrow_amount(old_principal: I256, new_principal: I256) -> (u256 /// # Number of Storage Accesses /// * Reads: `2` #[storage(read)] -fn quote_collateral_internal(asset_id: AssetId, base_amount: u64) -> u64 { +fn quote_collateral_internal(asset_id: AssetId, base_amount: u64, redstone_payload: Bytes) -> u64 { let collateral_configuration = storage.collateral_configurations.get(asset_id).read(); let market_configuration = storage.market_configuration.read(); @@ -2152,6 +2230,9 @@ fn quote_collateral_internal(asset_id: AssetId, base_amount: u64) -> u64 { let asset_price = get_price_internal( collateral_configuration .price_feed_id, + collateral_configuration + .redstone_feed_id, + redstone_payload, PricePosition::UpperBound, ); // decimals: asset_price.exponent let asset_price_scale = u256::from(10_u64).pow(asset_price.exponent); @@ -2161,6 +2242,9 @@ fn quote_collateral_internal(asset_id: AssetId, base_amount: u64) -> u64 { let base_price = get_price_internal( market_configuration .base_token_price_feed_id, + market_configuration + .base_token_redstone_feed_id, + redstone_payload, PricePosition::Middle, ); // decimals: base_price.exponent let base_price_scale = u256::from(10_u64).pow(base_price.exponent); @@ -2222,7 +2306,7 @@ fn get_user_balance_with_interest_internal(account: Identity) -> I256 { /// * Reads: `8 + storage.collateral_configurations_keys.len() * 5` /// * Writes: `2 + storage.collateral_configurations_keys.len() * 2` #[storage(write)] -fn absorb_internal(account: Identity) { +fn absorb_internal(account: Identity, redstone_payload: Bytes) { // Get the user's basic information let user_basic = storage.user_basic.get(account).try_read().unwrap_or(UserBasic::default()); let old_principal = user_basic.principal; @@ -2230,7 +2314,7 @@ fn absorb_internal(account: Identity) { // Check that the account is liquidatable require( - is_liquidatable_internal(account, old_balance), + is_liquidatable_internal(account, old_balance, redstone_payload), Error::NotLiquidatable, ); @@ -2273,6 +2357,9 @@ fn absorb_internal(account: Identity) { let price = get_price_internal( collateral_configuration .price_feed_id, + collateral_configuration + .redstone_feed_id, + redstone_payload, PricePosition::LowerBound, ); // decimals: price.exponent let price_exponent = price.exponent; @@ -2302,6 +2389,9 @@ fn absorb_internal(account: Identity) { let base_price = get_price_internal( market_configuration .base_token_price_feed_id, + market_configuration + .base_token_redstone_feed_id, + redstone_payload, PricePosition::Middle, ); // decimals: base_token_price.exponent let base_price_exponent = base_price.exponent; diff --git a/contracts/market/tests/local_tests/main_test_eth_base.rs b/contracts/market/tests/local_tests/main_test_eth_base.rs index 34cca23f..5b70ed9f 100644 --- a/contracts/market/tests/local_tests/main_test_eth_base.rs +++ b/contracts/market/tests/local_tests/main_test_eth_base.rs @@ -7,7 +7,7 @@ use fuels::{ calls::{CallHandler, CallParameters}, responses::CallResponse, }, - types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy}, + types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy, Bytes}, }; use market::PriceDataUpdate; use market_sdk::{convert_i256_to_u64, is_i256_negative, parse_units}; @@ -428,7 +428,12 @@ async fn main_test() { let buy_collateral_call = market .instance .methods() - .buy_collateral(usdt.asset_id, 1u64.into(), bob_account) + .buy_collateral( + usdt.asset_id, + 1u64.into(), + bob_account, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_contracts(&[&oracle.instance]) .with_tx_policies(tx_policies) .call_params(call_params_base_asset) diff --git a/contracts/market/tests/local_tests/main_test_uni.rs b/contracts/market/tests/local_tests/main_test_uni.rs index 6eb554dc..b723031b 100644 --- a/contracts/market/tests/local_tests/main_test_uni.rs +++ b/contracts/market/tests/local_tests/main_test_uni.rs @@ -6,7 +6,7 @@ use fuels::{ calls::{CallHandler, CallParameters}, responses::CallResponse, }, - types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy}, + types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy, Bytes}, }; use market::PriceDataUpdate; use market_sdk::{convert_i256_to_u64, is_i256_negative, parse_units}; @@ -446,7 +446,12 @@ async fn main_test() { let buy_collateral_call = market .instance .methods() - .buy_collateral(uni.asset_id, 1u64.into(), bob_account) + .buy_collateral( + uni.asset_id, + 1u64.into(), + bob_account, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_contracts(&[&oracle.instance]) .with_tx_policies(tx_policies) .call_params(call_params_base_asset) diff --git a/contracts/market/tests/local_tests/main_test_uni_no_debug_mode.rs b/contracts/market/tests/local_tests/main_test_uni_no_debug_mode.rs index c1993afb..bddce421 100644 --- a/contracts/market/tests/local_tests/main_test_uni_no_debug_mode.rs +++ b/contracts/market/tests/local_tests/main_test_uni_no_debug_mode.rs @@ -5,6 +5,7 @@ use fuels::programs::calls::{CallHandler, CallParameters}; use fuels::programs::responses::CallResponse; use fuels::types::transaction::TxPolicies; use fuels::types::transaction_builders::VariableOutputPolicy; +use fuels::types::Bytes; use market::PriceDataUpdate; use market_sdk::{convert_i256_to_u64, is_i256_negative, parse_units}; @@ -433,7 +434,12 @@ async fn main_test_no_debug() { let buy_collateral_call = market .instance .methods() - .buy_collateral(uni.asset_id, 1u64.into(), bob_account) + .buy_collateral( + uni.asset_id, + 1u64.into(), + bob_account, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_contracts(&[&oracle.instance]) .with_tx_policies(tx_policies) .call_params(call_params_base_asset) diff --git a/contracts/market/tests/local_tests/main_test_usdt.rs b/contracts/market/tests/local_tests/main_test_usdt.rs index a45cde04..fcb988b9 100644 --- a/contracts/market/tests/local_tests/main_test_usdt.rs +++ b/contracts/market/tests/local_tests/main_test_usdt.rs @@ -6,7 +6,7 @@ use fuels::{ calls::{CallHandler, CallParameters}, responses::CallResponse, }, - types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy}, + types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy, Bytes}, }; use market::PriceDataUpdate; use market_sdk::{convert_i256_to_u64, is_i256_negative, parse_units}; @@ -406,7 +406,12 @@ async fn main_test() { let buy_collateral_call = market .instance .methods() - .buy_collateral(usdt.asset_id, 1u64.into(), bob_account) + .buy_collateral( + usdt.asset_id, + 1u64.into(), + bob_account, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_contracts(&[&oracle.instance]) .with_tx_policies(tx_policies) .call_params(call_params_base_asset) diff --git a/contracts/market/tests/local_tests/scenarios/configuration.rs b/contracts/market/tests/local_tests/scenarios/configuration.rs index 71ca52ad..f197160f 100644 --- a/contracts/market/tests/local_tests/scenarios/configuration.rs +++ b/contracts/market/tests/local_tests/scenarios/configuration.rs @@ -182,6 +182,7 @@ async fn market_configuration_test() { base_tracking_borrow_speed: 1.into(), base_min_for_rewards: 2000000000.into(), base_borrow_min: 2000.into(), + base_token_redstone_feed_id: U256::from(usdc.symbol.as_bytes()), target_reserves: 2000000000000u64.into(), base_token: usdc.asset_id, base_token_decimals: usdc.decimals.try_into().unwrap(), diff --git a/contracts/market/tests/local_tests/scenarios/liquidation.rs b/contracts/market/tests/local_tests/scenarios/liquidation.rs index bb1d90b3..72502ece 100644 --- a/contracts/market/tests/local_tests/scenarios/liquidation.rs +++ b/contracts/market/tests/local_tests/scenarios/liquidation.rs @@ -6,7 +6,7 @@ use fuels::{ calls::{CallHandler, CallParameters}, responses::CallResponse, }, - types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy}, + types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy, Bytes}, }; use market::PriceDataUpdate; use market_sdk::{ @@ -266,7 +266,12 @@ async fn absorb_and_liquidate() { let buy_collateral_call = market .instance .methods() - .buy_collateral(eth.asset_id, 1u64.into(), alice_account) + .buy_collateral( + eth.asset_id, + 1u64.into(), + alice_account, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_contracts(&[&oracle.instance]) .with_tx_policies(tx_policies) .call_params(call_params_base_asset) @@ -550,7 +555,12 @@ async fn all_assets_liquidated() { let buy_collateral_call = market .instance .methods() - .buy_collateral(eth.asset_id, 1u64.into(), alice_account) + .buy_collateral( + eth.asset_id, + 1u64.into(), + alice_account, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_contracts(&[&oracle.instance]) .with_tx_policies(tx_policies) .call_params(call_params_base_asset) diff --git a/contracts/market/tests/local_tests/scenarios/multicall_absorb_buy_collateral.rs b/contracts/market/tests/local_tests/scenarios/multicall_absorb_buy_collateral.rs index cf141b11..a14dacac 100644 --- a/contracts/market/tests/local_tests/scenarios/multicall_absorb_buy_collateral.rs +++ b/contracts/market/tests/local_tests/scenarios/multicall_absorb_buy_collateral.rs @@ -6,7 +6,7 @@ use fuels::{ calls::{CallHandler, CallParameters}, responses::CallResponse, }, - types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy}, + types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy, Bytes}, }; use market::PriceDataUpdate; use market_sdk::{is_i256_negative, parse_units}; @@ -167,7 +167,11 @@ async fn multicall_absorb_buy_collateral_test() { let absorb_call = market .instance .methods() - .absorb(vec![bob_account], price_data_update.clone()) + .absorb( + vec![bob_account], + price_data_update.clone(), + Bytes::from_hex_str("0x00").unwrap(), + ) .with_contracts(&[&oracle.instance]) .call_params(CallParameters::default().with_amount(price_data_update.update_fee)) .unwrap(); @@ -196,7 +200,12 @@ async fn multicall_absorb_buy_collateral_test() { let buy_collateral_call = market .instance .methods() - .buy_collateral(eth.asset_id, amount, chad_account) + .buy_collateral( + eth.asset_id, + amount, + chad_account, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_contracts(&[&oracle.instance]) .call_params( CallParameters::default() diff --git a/contracts/market/tests/local_tests/scenarios/multicall_withdraw_supply.rs b/contracts/market/tests/local_tests/scenarios/multicall_withdraw_supply.rs index da072184..bcebc12b 100644 --- a/contracts/market/tests/local_tests/scenarios/multicall_withdraw_supply.rs +++ b/contracts/market/tests/local_tests/scenarios/multicall_withdraw_supply.rs @@ -5,7 +5,7 @@ use fuels::{ calls::{CallHandler, CallParameters}, responses::CallResponse, }, - types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy}, + types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy, Bytes}, }; use market::PriceDataUpdate; use market_sdk::parse_units; @@ -118,7 +118,11 @@ async fn multicall_withdraw_supply_test() { let withdraw_base_call = market .instance .methods() - .withdraw_base(bob_withdraw_amount.into(), price_data_update.clone()) + .withdraw_base( + bob_withdraw_amount.into(), + price_data_update.clone(), + Bytes::from_hex_str("0x00").unwrap(), + ) .with_contracts(&[&oracle.instance]) .with_tx_policies(tx_policies) .call_params(CallParameters::default().with_amount(price_data_update.update_fee)) diff --git a/contracts/market/tests/local_tests/scenarios/owner.rs b/contracts/market/tests/local_tests/scenarios/owner.rs index 70780190..8b03bdc2 100644 --- a/contracts/market/tests/local_tests/scenarios/owner.rs +++ b/contracts/market/tests/local_tests/scenarios/owner.rs @@ -38,6 +38,7 @@ async fn owner_test() { let mock_collateral_config = CollateralConfiguration { asset_id: assets["USDC"].asset_id.into(), price_feed_id: assets["USDC"].price_feed_id, + redstone_feed_id: U256::from(usdc.symbol.as_bytes()), decimals: assets["USDC"].decimals.try_into().unwrap(), borrow_collateral_factor: U256::from(18), // decimals: 18 liquidate_collateral_factor: U256::from(18), // decimals: 18 diff --git a/contracts/market/tests/local_tests/scenarios/update_curve_parameters.rs b/contracts/market/tests/local_tests/scenarios/update_curve_parameters.rs index 82f233cb..accce003 100644 --- a/contracts/market/tests/local_tests/scenarios/update_curve_parameters.rs +++ b/contracts/market/tests/local_tests/scenarios/update_curve_parameters.rs @@ -6,7 +6,7 @@ use fuels::{ calls::{CallHandler, CallParameters}, responses::CallResponse, }, - types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy, U256}, + types::{transaction::TxPolicies, transaction_builders::VariableOutputPolicy, Bytes, U256}, }; use market::PriceDataUpdate; use market_sdk::{convert_i256_to_u64, is_i256_negative, parse_units}; @@ -514,7 +514,12 @@ async fn main_test() { let buy_collateral_call = market .instance .methods() - .buy_collateral(uni.asset_id, 1u64.into(), bob_account) + .buy_collateral( + uni.asset_id, + 1u64.into(), + bob_account, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_contracts(&[&oracle.instance]) .with_tx_policies(tx_policies) .call_params(call_params_base_asset) diff --git a/contracts/market/tests/market-config.json b/contracts/market/tests/market-config.json index 5b06068c..81182eb3 100644 --- a/contracts/market/tests/market-config.json +++ b/contracts/market/tests/market-config.json @@ -13,5 +13,6 @@ "base_tracking_borrow_speed": 821759259259, "base_min_for_rewards": 1000000000, "base_borrow_min": 10000000, + "base_token_symbol": "USDC", "target_reserves": 1000000000000 } diff --git a/contracts/market/tests/utils/mod.rs b/contracts/market/tests/utils/mod.rs index c95a6756..0bdb7c30 100644 --- a/contracts/market/tests/utils/mod.rs +++ b/contracts/market/tests/utils/mod.rs @@ -1,7 +1,7 @@ use chrono::Utc; use fuels::accounts::wallet::WalletUnlocked; use fuels::test_helpers::{ - launch_custom_provider_and_get_wallets, NodeConfig, Trigger, WalletsConfig + launch_custom_provider_and_get_wallets, NodeConfig, Trigger, WalletsConfig, }; use fuels::types::{Bits256, ContractId, Identity}; use market_sdk::{get_market_config, Market}; diff --git a/fetchRedstone.ts b/fetchRedstone.ts index 8d0c425b..06162b05 100644 --- a/fetchRedstone.ts +++ b/fetchRedstone.ts @@ -26,7 +26,7 @@ async function main() { const cpp: ContractParamsProvider = new ContractParamsProvider({ dataServiceId: 'redstone-primary-prod', dataPackagesIds: ['BTC', 'ETH'], - uniqueSignersCount: 2, + uniqueSignersCount: 1, }); const dataPackages = await cpp.requestDataPackages(); const payloadHex = await cpp.getPayloadData(); diff --git a/libs/market_sdk/src/market_utils.rs b/libs/market_sdk/src/market_utils.rs index 21f5bcd8..450e3acb 100644 --- a/libs/market_sdk/src/market_utils.rs +++ b/libs/market_sdk/src/market_utils.rs @@ -9,7 +9,7 @@ use fuels::{ types::{ bech32::Bech32ContractId, transaction::TxPolicies, transaction_builders::VariableOutputPolicy, AssetId, Bits256, Bytes, Bytes32, ContractId, - Identity, + Identity, U256, }, }; use market::*; @@ -24,7 +24,7 @@ pub struct Market { pub instance: MarketContract, } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] struct MarketConfig { supply_kink: u64, borrow_kink: u64, @@ -41,6 +41,7 @@ struct MarketConfig { base_min_for_rewards: u64, // decimals base_token_decimals base_borrow_min: u64, // decimals base_token_decimals target_reserves: u64, + base_token_symbol: String, } pub fn get_market_config( @@ -52,11 +53,13 @@ pub fn get_market_config( .join("contracts/market/tests/market-config.json"); let config_json_str = std::fs::read_to_string(config_json_path)?; let config: MarketConfig = serde_json::from_str(&config_json_str)?; + println!("🚀 ~ get_market_config ~ config: {:?}", config); Ok(MarketConfiguration { base_token, base_token_decimals, base_token_price_feed_id, + base_token_redstone_feed_id: U256::from(config.base_token_symbol.as_bytes()), supply_kink: config.supply_kink.into(), borrow_kink: config.borrow_kink.into(), supply_per_second_interest_rate_slope_low: config @@ -279,7 +282,12 @@ impl Market { Ok(self .instance .methods() - .withdraw_collateral(asset_id, amount, price_data_update.clone()) + .withdraw_collateral( + asset_id, + amount, + price_data_update.clone(), + Bytes::from_hex_str("0x00").unwrap(), + ) .with_tx_policies(tx_policies) .call_params(call_params)? .with_contracts(contract_ids) @@ -375,7 +383,11 @@ impl Market { Ok(self .instance .methods() - .withdraw_base(amount.into(), price_data_update.clone()) + .withdraw_base( + amount.into(), + price_data_update.clone(), + Bytes::from_hex_str("0x00").unwrap(), + ) .with_variable_output_policy(VariableOutputPolicy::Exactly(1)) .with_contracts(contract_ids) .with_tx_policies(tx_policies) @@ -409,7 +421,7 @@ impl Market { let res = self .instance .methods() - .available_to_borrow(account) + .available_to_borrow(account, Bytes::from_hex_str("0x00").unwrap()) .with_tx_policies(tx_policies) .with_contracts(contract_ids) .call() @@ -432,7 +444,11 @@ impl Market { Ok(self .instance .methods() - .absorb(accounts, price_data_update.clone()) + .absorb( + accounts, + price_data_update.clone(), + Bytes::from_hex_str("0x00").unwrap(), + ) .with_tx_policies(tx_policies) .with_contracts(contract_ids) .call_params(call_params)? @@ -450,7 +466,7 @@ impl Market { Ok(self .instance .methods() - .is_liquidatable(account) + .is_liquidatable(account, Bytes::from_hex_str("0x00").unwrap()) .with_tx_policies(tx_policies) .with_contracts(contract_ids) .call() @@ -476,7 +492,12 @@ impl Market { Ok(self .instance .methods() - .buy_collateral(asset_id, min_amount.into(), recipient) + .buy_collateral( + asset_id, + min_amount.into(), + recipient, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_tx_policies(tx_policies) .with_contracts(contract_ids) .call_params(call_params_base_asset)? @@ -496,7 +517,11 @@ impl Market { Ok(self .instance .methods() - .collateral_value_to_sell(asset_id, collateral_amount) + .collateral_value_to_sell( + asset_id, + collateral_amount, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_tx_policies(tx_policies) .with_contracts(contract_ids) .call() @@ -514,7 +539,11 @@ impl Market { Ok(self .instance .methods() - .quote_collateral(asset_id.into(), base_amount) + .quote_collateral( + asset_id.into(), + base_amount, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_tx_policies(tx_policies) .with_contracts(contract_ids) .call() @@ -726,13 +755,18 @@ impl Market { &self, contract_ids: &[&dyn ContractDependency], price_feed_id: Bits256, + redstone_feed_id: U256, ) -> anyhow::Result> { let tx_policies = TxPolicies::default().with_script_gas_limit(DEFAULT_GAS_LIMIT); Ok(self .instance .methods() - .get_price(price_feed_id) + .get_price( + price_feed_id, + redstone_feed_id, + Bytes::from_hex_str("0x00").unwrap(), + ) .with_contracts(contract_ids) .with_tx_policies(tx_policies) .call() diff --git a/libs/token_sdk/src/token_utils.rs b/libs/token_sdk/src/token_utils.rs index 1624b442..dea2fc93 100644 --- a/libs/token_sdk/src/token_utils.rs +++ b/libs/token_sdk/src/token_utils.rs @@ -6,7 +6,7 @@ use fuels::accounts::wallet::WalletUnlocked; use fuels::programs::contract::{Contract, LoadConfiguration}; use fuels::types::bech32::Bech32ContractId; use fuels::types::transaction::TxPolicies; -use fuels::types::{AssetId, Bits256, Bytes32, ContractId}; +use fuels::types::{AssetId, Bits256, Bytes32, ContractId, U256}; use serde::Deserialize; use std::collections::HashMap; use std::path::PathBuf; @@ -121,6 +121,7 @@ impl TokenContract { asset_configs.push(CollateralConfiguration { asset_id: asset_id.into(), price_feed_id: Bits256::from_hex_str(config.price_feed_id.as_str()).unwrap(), + redstone_feed_id: U256::from(symbol.as_bytes()), decimals: config.decimals, borrow_collateral_factor: config.borrow_collateral_factor.unwrap().into(), // decimals: 18 liquidate_collateral_factor: config.liquidate_collateral_factor.unwrap().into(), // decimals: 18 @@ -129,6 +130,7 @@ impl TokenContract { paused: false, }); } + println!("redstone_feed_id: {:?}", U256::from(symbol.as_bytes())); assets.insert( symbol.clone(), @@ -189,6 +191,7 @@ impl TokenContract { asset_id: asset_id.into(), decimals: config.decimals, price_feed_id: Bits256::from_hex_str(config.price_feed_id.as_str()).unwrap(), + redstone_feed_id: U256::from(symbol.as_bytes()), borrow_collateral_factor: config.borrow_collateral_factor.unwrap().into(), // decimals: 18 liquidate_collateral_factor: config.liquidate_collateral_factor.unwrap().into(), // decimals: 18 liquidation_penalty: config.liquidation_penalty.unwrap().into(), // decimals: 18 diff --git a/scripts/src/utils.rs b/scripts/src/utils.rs index ef6c1dc1..9c639bc4 100644 --- a/scripts/src/utils.rs +++ b/scripts/src/utils.rs @@ -135,6 +135,7 @@ impl From for MarketConfiguration { base_tracking_borrow_speed: config.base_tracking_borrow_speed.into(), base_min_for_rewards: config.base_min_for_rewards.into(), base_borrow_min: config.base_borrow_min.into(), + base_token_redstone_feed_id: U256::from(config.base_asset.symbol.as_bytes()), target_reserves: config.target_reserves.into(), base_token: AssetId::from_str(config.base_asset.asset_id.as_str()).unwrap(), base_token_decimals: config.base_asset.decimals, @@ -162,6 +163,7 @@ impl From for CollateralConfiguration { CollateralConfiguration { asset_id: AssetId::from_str(value.asset_id.as_str()).unwrap(), price_feed_id: Bits256::from_hex_str(value.price_feed_id.as_str()).unwrap(), + redstone_feed_id: U256::from(value.symbol.as_bytes()), decimals: value.decimals, borrow_collateral_factor: value.borrow_collateral_factor.into(), liquidate_collateral_factor: value.liquidate_collateral_factor.into(), From 0cf4eb0fc51bec9c5adf490c2281c9be72a0ed7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Mon, 17 Feb 2025 20:52:24 +0100 Subject: [PATCH 03/17] chore: remove debug print statement from token_utils --- libs/token_sdk/src/token_utils.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/token_sdk/src/token_utils.rs b/libs/token_sdk/src/token_utils.rs index dea2fc93..6d8a8fba 100644 --- a/libs/token_sdk/src/token_utils.rs +++ b/libs/token_sdk/src/token_utils.rs @@ -130,7 +130,6 @@ impl TokenContract { paused: false, }); } - println!("redstone_feed_id: {:?}", U256::from(symbol.as_bytes())); assets.insert( symbol.clone(), From 3184c7452807d4465deefbc16d04802e6f92aee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Tue, 18 Feb 2025 10:54:21 +0100 Subject: [PATCH 04/17] fix: oracle staleness --- contracts/market/src/main.sw | 44 ++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/contracts/market/src/main.sw b/contracts/market/src/main.sw index a268301f..164bc753 100644 --- a/contracts/market/src/main.sw +++ b/contracts/market/src/main.sw @@ -1465,31 +1465,27 @@ fn get_price_internal( let oracle = abi(PythCore, contract_id.bits()); let mut price = oracle.price(pyth_price_feed_id); - // Validate values - let now = std::block::timestamp(); - let time_diff = if price.publish_time < now { - now - price.publish_time + // validate values + if price.publish_time < std::block::timestamp() { + let staleness = std::block::timestamp() - price.publish_time; + if staleness > ORACLE_MAX_STALENESS + && staleness > ORACLE_DOWNTIME_THRESHOLD + { + let mut price_feeds: Vec = Vec::new(); + price_feeds.push(redstone_feed_id); + let (redstone_price, _) = get_redstone_price_internal(price_feeds, redstone_payload); + let price_from_u256: Option = >::try_from(redstone_price); + price.price = price_from_u256.unwrap(); + price.exponent = REDSTONE_PRICE_EXPONENT; + price.confidence = 0; + price.publish_time = std::block::timestamp(); + } } else { - price.publish_time - now - }; - - require( - time_diff < ORACLE_DOWNTIME_THRESHOLD, - Error::OraclePriceValidationError, - ); - - if time_diff > ORACLE_MAX_STALENESS - || time_diff > ORACLE_MAX_AHEADNESS - || price.price == 0 - { - let mut price_feeds: Vec = Vec::new(); - price_feeds.push(redstone_feed_id); - let (redstone_price, _) = get_redstone_price_internal(price_feeds, redstone_payload); - let price_from_u256: Option = >::try_from(redstone_price); - price.price = price_from_u256.unwrap(); - price.exponent = REDSTONE_PRICE_EXPONENT; - price.confidence = 0; - price.publish_time = now; + let aheadness = price.publish_time - std::block::timestamp(); + require( + aheadness <= ORACLE_MAX_AHEADNESS, + Error::OraclePriceValidationError, + ); } require(price.price != 0, Error::OraclePriceValidationError); From 1a8810e23e6beab4d87c3fcf27903a34408ed4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Tue, 18 Feb 2025 10:57:20 +0100 Subject: [PATCH 05/17] fix: add require statement for getting the price --- contracts/market/src/main.sw | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/market/src/main.sw b/contracts/market/src/main.sw index 164bc753..1b73fc17 100644 --- a/contracts/market/src/main.sw +++ b/contracts/market/src/main.sw @@ -1480,6 +1480,10 @@ fn get_price_internal( price.confidence = 0; price.publish_time = std::block::timestamp(); } + require( + staleness <= ORACLE_MAX_STALENESS, + Error::OraclePriceValidationError, + ); } else { let aheadness = price.publish_time - std::block::timestamp(); require( From f1094e721fb2675f8ce92b25e97e3e3421763d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Tue, 18 Feb 2025 11:07:17 +0100 Subject: [PATCH 06/17] fix: validate staleness condition for price retrieval --- contracts/market/src/main.sw | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contracts/market/src/main.sw b/contracts/market/src/main.sw index 1b73fc17..1a8f2e8f 100644 --- a/contracts/market/src/main.sw +++ b/contracts/market/src/main.sw @@ -1479,11 +1479,12 @@ fn get_price_internal( price.exponent = REDSTONE_PRICE_EXPONENT; price.confidence = 0; price.publish_time = std::block::timestamp(); + } else { + require( + staleness <= ORACLE_MAX_STALENESS, + Error::OraclePriceValidationError, + ); } - require( - staleness <= ORACLE_MAX_STALENESS, - Error::OraclePriceValidationError, - ); } else { let aheadness = price.publish_time - std::block::timestamp(); require( From 4383f6ea59445a9f572e0bf3254458446b895f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Tue, 18 Feb 2025 11:09:59 +0100 Subject: [PATCH 07/17] fix: adjust staleness validation logic in price feed --- contracts/market/src/main.sw | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/market/src/main.sw b/contracts/market/src/main.sw index 1a8f2e8f..16380493 100644 --- a/contracts/market/src/main.sw +++ b/contracts/market/src/main.sw @@ -1468,8 +1468,7 @@ fn get_price_internal( // validate values if price.publish_time < std::block::timestamp() { let staleness = std::block::timestamp() - price.publish_time; - if staleness > ORACLE_MAX_STALENESS - && staleness > ORACLE_DOWNTIME_THRESHOLD + if staleness > ORACLE_DOWNTIME_THRESHOLD { let mut price_feeds: Vec = Vec::new(); price_feeds.push(redstone_feed_id); From c51910b6c8e85f7cfa4d17fd2834d7069653b842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Tue, 18 Feb 2025 11:15:51 +0100 Subject: [PATCH 08/17] chore: fmt --- contracts/market/src/main.sw | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/market/src/main.sw b/contracts/market/src/main.sw index 16380493..72efe5c8 100644 --- a/contracts/market/src/main.sw +++ b/contracts/market/src/main.sw @@ -1468,8 +1468,7 @@ fn get_price_internal( // validate values if price.publish_time < std::block::timestamp() { let staleness = std::block::timestamp() - price.publish_time; - if staleness > ORACLE_DOWNTIME_THRESHOLD - { + if staleness > ORACLE_DOWNTIME_THRESHOLD { let mut price_feeds: Vec = Vec::new(); price_feeds.push(redstone_feed_id); let (redstone_price, _) = get_redstone_price_internal(price_feeds, redstone_payload); From 0751ee064571dcacc2a627b1c8859da224652672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Tue, 18 Feb 2025 15:17:23 +0100 Subject: [PATCH 09/17] feat: redstone price fetching and tests --- contracts/market/src/main.sw | 19 +++- .../market/tests/local_tests/scenarios/mod.rs | 1 + .../tests/local_tests/scenarios/redstone.rs | 86 +++++++++++++++++++ fetchRedstone.ts | 14 ++- libs/market_sdk/src/market_utils.rs | 8 +- libs/token_sdk/src/token_utils.rs | 3 + 6 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 contracts/market/tests/local_tests/scenarios/redstone.rs diff --git a/contracts/market/src/main.sw b/contracts/market/src/main.sw index 72efe5c8..9b26f2fc 100644 --- a/contracts/market/src/main.sw +++ b/contracts/market/src/main.sw @@ -1421,12 +1421,20 @@ impl SRC5 for Contract { #[storage(read)] fn get_redstone_price_internal(feed_ids: Vec, payload_bytes: Bytes) -> (u256, u64) { let signer_count_threshold = SIGNER_COUNT_THRESHOLD; + let timestamp = timestamp(); + let mut block_timestamp = timestamp; + if timestamp > TAI64_UNIX_ADJUSTMENT { + block_timestamp = timestamp - TAI64_UNIX_ADJUSTMENT; + } else if DEBUG_STEP != 0 { + block_timestamp = 173988088000 / 100 + } + let config = Config { feed_ids: feed_ids, // be careful with this array, check xarr.sw for implemented trait signers: ALLOWED_SIGNERS.to_vec(), signer_count_threshold, - block_timestamp: timestamp() - TAI64_UNIX_ADJUSTMENT, + block_timestamp, }; let (price, timestamp) = process_input(payload_bytes, config); @@ -1467,16 +1475,21 @@ fn get_price_internal( let mut price = oracle.price(pyth_price_feed_id); // validate values if price.publish_time < std::block::timestamp() { + log(price.publish_time); + log(std::block::timestamp()); let staleness = std::block::timestamp() - price.publish_time; if staleness > ORACLE_DOWNTIME_THRESHOLD { let mut price_feeds: Vec = Vec::new(); price_feeds.push(redstone_feed_id); - let (redstone_price, _) = get_redstone_price_internal(price_feeds, redstone_payload); + let (redstone_price, redstone_timestamp) = get_redstone_price_internal(price_feeds, redstone_payload); let price_from_u256: Option = >::try_from(redstone_price); price.price = price_from_u256.unwrap(); price.exponent = REDSTONE_PRICE_EXPONENT; price.confidence = 0; - price.publish_time = std::block::timestamp(); + price.publish_time = redstone_timestamp; + if DEBUG_STEP != 0 { + price.publish_time = 1739880880000; + } } else { require( staleness <= ORACLE_MAX_STALENESS, diff --git a/contracts/market/tests/local_tests/scenarios/mod.rs b/contracts/market/tests/local_tests/scenarios/mod.rs index 5976f4e5..58552e5a 100644 --- a/contracts/market/tests/local_tests/scenarios/mod.rs +++ b/contracts/market/tests/local_tests/scenarios/mod.rs @@ -7,6 +7,7 @@ mod negative_reserves; mod owner; mod pause; mod price_changes; +mod redstone; mod reserves; mod rewards; mod supply_withdraw; diff --git a/contracts/market/tests/local_tests/scenarios/redstone.rs b/contracts/market/tests/local_tests/scenarios/redstone.rs new file mode 100644 index 00000000..063fe1ea --- /dev/null +++ b/contracts/market/tests/local_tests/scenarios/redstone.rs @@ -0,0 +1,86 @@ +// **Scenario #12 - Collateral asset price increases** + +// Description: Check that if collateral asset price increases, you can now borrow more base asset. +use crate::utils::{print_case_title, setup, TestBaseAsset, TestData}; +use chrono::Utc; +use fuels::{ + accounts::ViewOnlyAccount, + types::{Bits256, Bytes, U256}, +}; +use market::PriceDataUpdate; +use market_sdk::parse_units; +use pyth_sdk::constants::ETH_USD_PRICE_FEED_ID; + +const AMOUNT_COEFFICIENT: u64 = 10u64.pow(0); +const SCALE_6: f64 = 10u64.pow(6) as f64; + +#[tokio::test] +async fn redstone_test() { + let TestData { market, oracle, .. } = setup(Some(10_000), TestBaseAsset::USDC).await; + + let price_feed_id = Bits256::from_hex_str(ETH_USD_PRICE_FEED_ID).unwrap(); + let price: u64 = 350_000_000_000; + let exponent: u32 = 8; + let publish_time: u64 = 0; + let confidence: u64 = 50; + + // Update price feeds + let mut update_data: Vec = Vec::new(); + + let price_feed_id_bytes = price_feed_id + .0 + .iter() + .map(|byte| *byte) + .collect::>(); + + update_data.extend(price_feed_id_bytes); + update_data.extend(price.to_be_bytes()); + update_data.extend(exponent.to_be_bytes()); + update_data.extend(publish_time.to_be_bytes()); + update_data.extend(confidence.to_be_bytes()); + + let update_data_bytes = Vec::from([Bytes { 0: update_data }]); + oracle.update_price_feeds(update_data_bytes).await.unwrap(); + + market.debug_increment_timestamp().await.unwrap(); + market.debug_increment_timestamp().await.unwrap(); + market.debug_increment_timestamp().await.unwrap(); + market.debug_increment_timestamp().await.unwrap(); + + // pyth price feed id ETH/USD + let price_feed_id = "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"; + let redstone_feed_id = U256::from("ETH".as_bytes()); + let redstone_payload = [ + 69, 84, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 62, 138, 241, 216, 238, 1, 149, 24, 251, 87, 128, 0, 0, 0, 32, 0, 0, 1, 55, 180, 4, 249, + 95, 24, 101, 206, 128, 159, 37, 66, 228, 73, 184, 177, 115, 210, 23, 121, 240, 62, 18, 210, + 56, 204, 197, 52, 64, 235, 64, 94, 111, 176, 160, 94, 27, 18, 211, 49, 2, 129, 254, 218, + 51, 58, 162, 57, 226, 224, 116, 18, 50, 238, 224, 163, 233, 107, 1, 177, 51, 81, 249, 140, + 28, 0, 1, 0, 0, 0, 0, 0, 2, 237, 87, 1, 30, 0, 0, + ]; + let pyth_price = oracle + .price(Bits256::from_hex_str(ETH_USD_PRICE_FEED_ID).unwrap()) + .await + .unwrap(); + + assert_eq!(pyth_price.value.publish_time, 0); + + let response = market + .get_price( + &[&oracle.instance], + Bits256::from_hex_str(price_feed_id).unwrap(), + redstone_feed_id, + Bytes(redstone_payload.to_vec()), + ) + .await + .unwrap(); + + assert_eq!(response.value.publish_time, 1_739_880_880_000); + assert_eq!(response.value.price, 268_619_077_870); + + println!( + "🔺 ETH price: {}", + response.value.price as f64 / 10_u64.pow(response.value.exponent) as f64 + ); +} diff --git a/fetchRedstone.ts b/fetchRedstone.ts index 06162b05..4ff4639e 100644 --- a/fetchRedstone.ts +++ b/fetchRedstone.ts @@ -25,15 +25,23 @@ https.request = function (...args) { async function main() { const cpp: ContractParamsProvider = new ContractParamsProvider({ dataServiceId: 'redstone-primary-prod', - dataPackagesIds: ['BTC', 'ETH'], + dataPackagesIds: [ + 'ETH', + 'ezETH', + 'USDC', + 'USDT', + // 'FUEL', + 'sDAI', + 'weETH', + 'wstETH', + ], uniqueSignersCount: 1, }); const dataPackages = await cpp.requestDataPackages(); const payloadHex = await cpp.getPayloadData(); const hexlified = cpp.getHexlifiedFeedIds(); const dataFeedIds = cpp.getDataFeedIds(); - console.log('🚀 ~ main ~ dataPackages: ', dataPackages); - console.log('🚀 ~ main ~ payloadBytes: ', payloadHex); + console.log('🚀 ~ main ~ payloadBytes: ', payloadHex.toString()); console.log('🚀 ~ main ~ hexlified: ', hexlified); console.log('🚀 ~ main ~ feedIds: ', dataFeedIds); } diff --git a/libs/market_sdk/src/market_utils.rs b/libs/market_sdk/src/market_utils.rs index 450e3acb..0e81a69a 100644 --- a/libs/market_sdk/src/market_utils.rs +++ b/libs/market_sdk/src/market_utils.rs @@ -756,17 +756,13 @@ impl Market { contract_ids: &[&dyn ContractDependency], price_feed_id: Bits256, redstone_feed_id: U256, + redstone_payload: Bytes, ) -> anyhow::Result> { let tx_policies = TxPolicies::default().with_script_gas_limit(DEFAULT_GAS_LIMIT); - Ok(self .instance .methods() - .get_price( - price_feed_id, - redstone_feed_id, - Bytes::from_hex_str("0x00").unwrap(), - ) + .get_price(price_feed_id, redstone_feed_id, redstone_payload) .with_contracts(contract_ids) .with_tx_policies(tx_policies) .call() diff --git a/libs/token_sdk/src/token_utils.rs b/libs/token_sdk/src/token_utils.rs index 6d8a8fba..a7c944ae 100644 --- a/libs/token_sdk/src/token_utils.rs +++ b/libs/token_sdk/src/token_utils.rs @@ -27,6 +27,7 @@ pub struct Asset { pub bits256: Bits256, pub default_price: u64, pub price_feed_id: Bits256, + pub redstone_feed_id: U256, pub price_feed_decimals: u32, } @@ -136,6 +137,7 @@ impl TokenContract { Asset { asset_id: asset_id.into(), price_feed_id: Bits256::from_hex_str(config.price_feed_id.as_str()).unwrap(), + redstone_feed_id: U256::from(symbol.as_bytes()), price_feed_decimals: config.price_feed_decimals, decimals: token.decimals, symbol: token.symbol, @@ -176,6 +178,7 @@ impl TokenContract { Asset { asset_id: asset_id.into(), price_feed_id: Bits256::from_hex_str(config.price_feed_id.as_str()).unwrap(), + redstone_feed_id: U256::from(symbol.as_bytes()), price_feed_decimals: config.price_feed_decimals, decimals: config.decimals.into(), symbol: symbol.clone(), From 144c06547dc18af8b17038ddd4ad1c64e2ec09be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Tue, 18 Feb 2025 15:45:45 +0100 Subject: [PATCH 10/17] chore: update pnpm lock --- pnpm-lock.yaml | 446 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 368 insertions(+), 78 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d59f4e8e..9848bdee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,10 @@ overrides: importers: .: + dependencies: + '@redstone-finance/sdk': + specifier: 0.7.5 + version: 0.7.5(bufferutil@4.0.9)(utf-8-validate@5.0.10) devDependencies: '@biomejs/biome': specifier: 1.9.4 @@ -47,7 +51,7 @@ importers: version: 0.35.1(@tanstack/react-query@5.61.0(react@18.3.1))(@types/react-dom@18.3.1)(@types/react@18.3.12)(fuels@0.96.1(encoding@0.1.13))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@pythnetwork/hermes-client': specifier: ^1.2.0 - version: 1.2.0(axios@1.7.4) + version: 1.2.0(axios@1.7.9) '@pythnetwork/pyth-fuel-js': specifier: ^1.0.7 version: 1.0.7(encoding@0.1.13) @@ -2073,6 +2077,9 @@ packages: '@ethersproject/abi@5.0.7': resolution: {integrity: sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw==} + '@ethersproject/abi@5.7.0': + resolution: {integrity: sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==} + '@ethersproject/abstract-provider@5.7.0': resolution: {integrity: sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==} @@ -2085,6 +2092,9 @@ packages: '@ethersproject/base64@5.7.0': resolution: {integrity: sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==} + '@ethersproject/basex@5.7.0': + resolution: {integrity: sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==} + '@ethersproject/bignumber@5.7.0': resolution: {integrity: sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==} @@ -2094,9 +2104,18 @@ packages: '@ethersproject/constants@5.7.0': resolution: {integrity: sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==} + '@ethersproject/contracts@5.7.0': + resolution: {integrity: sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==} + '@ethersproject/hash@5.7.0': resolution: {integrity: sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==} + '@ethersproject/hdnode@5.7.0': + resolution: {integrity: sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==} + + '@ethersproject/json-wallets@5.7.0': + resolution: {integrity: sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==} + '@ethersproject/keccak256@5.7.0': resolution: {integrity: sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==} @@ -2106,9 +2125,18 @@ packages: '@ethersproject/networks@5.7.1': resolution: {integrity: sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==} + '@ethersproject/pbkdf2@5.7.0': + resolution: {integrity: sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==} + '@ethersproject/properties@5.7.0': resolution: {integrity: sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==} + '@ethersproject/providers@5.7.2': + resolution: {integrity: sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==} + + '@ethersproject/random@5.7.0': + resolution: {integrity: sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==} + '@ethersproject/rlp@5.7.0': resolution: {integrity: sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==} @@ -2118,15 +2146,27 @@ packages: '@ethersproject/signing-key@5.7.0': resolution: {integrity: sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==} + '@ethersproject/solidity@5.7.0': + resolution: {integrity: sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==} + '@ethersproject/strings@5.7.0': resolution: {integrity: sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==} '@ethersproject/transactions@5.7.0': resolution: {integrity: sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==} + '@ethersproject/units@5.7.0': + resolution: {integrity: sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==} + + '@ethersproject/wallet@5.7.0': + resolution: {integrity: sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==} + '@ethersproject/web@5.7.1': resolution: {integrity: sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==} + '@ethersproject/wordlists@5.7.0': + resolution: {integrity: sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==} + '@float-capital/float-subgraph-uncrashable@0.0.0-internal-testing.5': resolution: {integrity: sha512-yZ0H5e3EpAYKokX/AbtplzlvSxEJY7ZfpvQyDzyODkks0hakAAlDG6fQu1SlDJMWorY7bbq1j7fCiFeTWci6TA==} hasBin: true @@ -3738,6 +3778,18 @@ packages: '@types/react': optional: true + '@redstone-finance/oracles-smartweave-contracts@0.7.5': + resolution: {integrity: sha512-js9X7HbKqLAttbElOLQO9/kf6kU/pGMtfUyd2PuOPFWcLgSGWPBoXQgtGstJyeY91Uhzg2TveqgbrZow+4PstQ==} + + '@redstone-finance/protocol@0.7.5': + resolution: {integrity: sha512-Yh/tCDqBM293JdDg84f1jPlmxHcg76UboDs5OnRwv3T0Ra0M5NH8GKydKNaAc83LVQhxbA7z829brPDaefbPLQ==} + + '@redstone-finance/sdk@0.7.5': + resolution: {integrity: sha512-IAJDpHxsHg0hv5yW26Gocn5WScR/LRB/+mnSKZzUk72ILBq6vUkV9J9lE/zdgKYBBlMtzj7OElIkcDNjkR2xKQ==} + + '@redstone-finance/utils@0.7.5': + resolution: {integrity: sha512-hldeC00/V397GMn/UHtYQ9ybbUET9bAM2sinlyK0bnLNx+nLlPAV9ePltqh56V5/7Bn+BDYYDDkRSGy7cj9X5w==} + '@repeaterjs/repeater@3.0.6': resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} @@ -4310,6 +4362,9 @@ packages: '@types/keyv@3.1.4': resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + '@types/lodash@4.17.15': + resolution: {integrity: sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==} + '@types/long@4.0.2': resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} @@ -4340,9 +4395,6 @@ packages: '@types/node@20.17.6': resolution: {integrity: sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==} - '@types/node@22.13.1': - resolution: {integrity: sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==} - '@types/node@22.13.4': resolution: {integrity: sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==} @@ -4791,6 +4843,9 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + aes-js@3.0.0: + resolution: {integrity: sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==} + aes-js@4.0.0-beta.5: resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} @@ -4976,6 +5031,9 @@ packages: axios@1.7.4: resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} + axios@1.7.9: + resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==} + babel-core@7.0.0-bridge.0: resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} peerDependencies: @@ -5032,6 +5090,9 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + bech32@1.1.4: + resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} + bech32@2.0.0: resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==} @@ -5524,9 +5585,8 @@ packages: resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} engines: {node: '>= 0.10.0'} - consola@3.2.3: - resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} - engines: {node: ^14.18.0 || >=16.10.0} + consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} consola@3.4.0: resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} @@ -5784,6 +5844,9 @@ packages: decimal.js-light@2.5.1: resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + decimal.js@10.5.0: + resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} + decode-uri-component@0.2.2: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} engines: {node: '>=0.10'} @@ -5984,9 +6047,6 @@ packages: elliptic@6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} - elliptic@6.5.7: - resolution: {integrity: sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==} - elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -6180,6 +6240,9 @@ packages: resolution: {integrity: sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==} engines: {node: '>=10.0.0'} + ethers@5.7.2: + resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} + ethers@6.13.4: resolution: {integrity: sha512-21YtnZVg4/zKkCQPjrDj38B1r4nQvTZLopUGMLQ1ePU2zV/joCfDC3t3iKQjWRzjjjbzR+mdAIoikeBRNkdllA==} engines: {node: '>=14.0.0'} @@ -8993,10 +9056,6 @@ packages: resolution: {integrity: sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==} engines: {node: '>=18.0.0'} - secp256k1@5.0.0: - resolution: {integrity: sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==} - engines: {node: '>=14.0.0'} - secp256k1@5.0.1: resolution: {integrity: sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==} engines: {node: '>=18.0.0'} @@ -10087,6 +10146,18 @@ packages: utf-8-validate: optional: true + ws@7.4.6: + resolution: {integrity: sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@7.5.10: resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} engines: {node: '>=8.3.0'} @@ -10419,7 +10490,7 @@ snapshots: '@babel/types': 7.26.8 '@types/gensync': 1.0.4 convert-source-map: 2.0.0 - debug: 4.4.0 + debug: 4.4.0(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -10570,7 +10641,7 @@ snapshots: '@babel/core': 7.25.7 '@babel/helper-compilation-targets': 7.26.5 '@babel/helper-plugin-utils': 7.26.5 - debug: 4.4.0 + debug: 4.4.0(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.10 transitivePeerDependencies: @@ -12038,7 +12109,7 @@ snapshots: '@babel/parser': 7.26.8 '@babel/template': 7.26.8 '@babel/types': 7.26.8 - debug: 4.4.0 + debug: 4.4.0(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -12616,6 +12687,18 @@ snapshots: '@ethersproject/properties': 5.7.0 '@ethersproject/strings': 5.7.0 + '@ethersproject/abi@5.7.0': + dependencies: + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/abstract-provider@5.7.0': dependencies: '@ethersproject/bignumber': 5.7.0 @@ -12646,6 +12729,11 @@ snapshots: dependencies: '@ethersproject/bytes': 5.7.0 + '@ethersproject/basex@5.7.0': + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/bignumber@5.7.0': dependencies: '@ethersproject/bytes': 5.7.0 @@ -12660,6 +12748,19 @@ snapshots: dependencies: '@ethersproject/bignumber': 5.7.0 + '@ethersproject/contracts@5.7.0': + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/hash@5.7.0': dependencies: '@ethersproject/abstract-signer': 5.7.0 @@ -12672,6 +12773,37 @@ snapshots: '@ethersproject/properties': 5.7.0 '@ethersproject/strings': 5.7.0 + '@ethersproject/hdnode@5.7.0': + dependencies: + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/basex': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/pbkdf2': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/wordlists': 5.7.0 + + '@ethersproject/json-wallets@5.7.0': + dependencies: + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/hdnode': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/pbkdf2': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/random': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + aes-js: 3.0.0 + scrypt-js: 3.0.1 + '@ethersproject/keccak256@5.7.0': dependencies: '@ethersproject/bytes': 5.7.0 @@ -12683,10 +12815,46 @@ snapshots: dependencies: '@ethersproject/logger': 5.7.0 + '@ethersproject/pbkdf2@5.7.0': + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/properties@5.7.0': dependencies: '@ethersproject/logger': 5.7.0 + '@ethersproject/providers@5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/base64': 5.7.0 + '@ethersproject/basex': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/networks': 5.7.1 + '@ethersproject/properties': 5.7.0 + '@ethersproject/random': 5.7.0 + '@ethersproject/rlp': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/web': 5.7.1 + bech32: 1.1.4 + ws: 7.4.6(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@ethersproject/random@5.7.0': + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/rlp@5.7.0': dependencies: '@ethersproject/bytes': 5.7.0 @@ -12707,6 +12875,15 @@ snapshots: elliptic: 6.5.4 hash.js: 1.1.7 + '@ethersproject/solidity@5.7.0': + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/strings@5.7.0': dependencies: '@ethersproject/bytes': 5.7.0 @@ -12725,6 +12902,30 @@ snapshots: '@ethersproject/rlp': 5.7.0 '@ethersproject/signing-key': 5.7.0 + '@ethersproject/units@5.7.0': + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/logger': 5.7.0 + + '@ethersproject/wallet@5.7.0': + dependencies: + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/hdnode': 5.7.0 + '@ethersproject/json-wallets': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/random': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/wordlists': 5.7.0 + '@ethersproject/web@5.7.1': dependencies: '@ethersproject/base64': 5.7.0 @@ -12733,6 +12934,14 @@ snapshots: '@ethersproject/properties': 5.7.0 '@ethersproject/strings': 5.7.0 + '@ethersproject/wordlists@5.7.0': + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@float-capital/float-subgraph-uncrashable@0.0.0-internal-testing.5': dependencies: '@rescript/std': 9.0.0 @@ -13422,7 +13631,7 @@ snapshots: '@types/js-yaml': 4.0.9 '@whatwg-node/fetch': 0.10.3 chalk: 4.1.2 - debug: 4.4.0 + debug: 4.4.0(supports-color@8.1.1) dotenv: 16.4.7 graphql: 16.9.0 graphql-request: 6.1.0(encoding@0.1.13)(graphql@16.9.0) @@ -13856,7 +14065,7 @@ snapshots: bufferutil: 4.0.9 cross-fetch: 4.1.0(encoding@0.1.13) date-fns: 2.30.0 - debug: 4.4.0 + debug: 4.4.0(supports-color@8.1.1) eciesjs: 0.3.21 eventemitter2: 6.4.9 readable-stream: 3.6.2 @@ -13929,7 +14138,7 @@ snapshots: '@types/uuid': 10.0.0 bowser: 2.11.0 cross-fetch: 4.1.0(encoding@0.1.13) - debug: 4.4.0 + debug: 4.4.0(supports-color@8.1.1) eciesjs: 0.3.21 eth-rpc-errors: 4.0.3 eventemitter2: 6.4.9 @@ -13962,7 +14171,7 @@ snapshots: dependencies: '@ethereumjs/tx': 4.2.0 '@types/debug': 4.1.12 - debug: 4.4.0 + debug: 4.4.0(supports-color@8.1.1) semver: 7.7.1 superstruct: 1.0.4 transitivePeerDependencies: @@ -13972,7 +14181,7 @@ snapshots: dependencies: '@ethereumjs/tx': 4.2.0 '@metamask/superstruct': 3.1.0 - '@noble/hashes': 1.5.0 + '@noble/hashes': 1.7.1 '@scure/base': 1.1.9 '@types/debug': 4.1.12 debug: 4.3.7 @@ -13986,7 +14195,7 @@ snapshots: dependencies: '@ethereumjs/tx': 4.2.0 '@metamask/superstruct': 3.1.0 - '@noble/hashes': 1.5.0 + '@noble/hashes': 1.7.1 '@scure/base': 1.1.9 '@types/debug': 4.1.12 debug: 4.3.7 @@ -14150,7 +14359,7 @@ snapshots: chalk: 4.1.2 clean-stack: 3.0.1 cli-progress: 3.12.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.4.0(supports-color@8.1.1) ejs: 3.1.10 get-package-type: 0.1.0 globby: 11.1.0 @@ -14186,7 +14395,7 @@ snapshots: chalk: 4.1.2 clean-stack: 3.0.1 cli-progress: 3.12.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.4.0(supports-color@8.1.1) ejs: 3.1.10 fs-extra: 9.1.0 get-package-type: 0.1.0 @@ -14198,7 +14407,7 @@ snapshots: natural-orderby: 2.0.3 object-treeify: 1.1.33 password-prompt: 1.1.3 - semver: 7.4.0 + semver: 7.7.1 string-width: 4.2.3 strip-ansi: 6.0.1 supports-color: 8.1.1 @@ -14218,7 +14427,7 @@ snapshots: dependencies: '@oclif/core': 2.16.0(@types/node@22.13.4)(typescript@5.6.3) chalk: 4.1.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -14349,9 +14558,9 @@ snapshots: '@protobufjs/utf8@1.1.0': {} - '@pythnetwork/hermes-client@1.2.0(axios@1.7.4)': + '@pythnetwork/hermes-client@1.2.0(axios@1.7.9)': dependencies: - '@zodios/core': 10.9.6(axios@1.7.4)(zod@3.23.8) + '@zodios/core': 10.9.6(axios@1.7.9)(zod@3.23.8) eventsource: 2.0.2 zod: 3.23.8 transitivePeerDependencies: @@ -15117,6 +15326,48 @@ snapshots: optionalDependencies: '@types/react': 18.3.12 + '@redstone-finance/oracles-smartweave-contracts@0.7.5': {} + + '@redstone-finance/protocol@0.7.5(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + decimal.js: 10.5.0 + ethers: 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + secp256k1: 5.0.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@redstone-finance/sdk@0.7.5(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@redstone-finance/oracles-smartweave-contracts': 0.7.5 + '@redstone-finance/protocol': 0.7.5(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@redstone-finance/utils': 0.7.5(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@types/lodash': 4.17.15 + axios: 1.7.9 + ethers: 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + lodash: 4.17.21 + zod: 3.23.8 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + + '@redstone-finance/utils@0.7.5(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@types/lodash': 4.17.15 + axios: 1.7.9 + consola: 2.15.3 + decimal.js: 10.5.0 + ethers: 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + lodash: 4.17.21 + zod: 3.23.8 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + '@repeaterjs/repeater@3.0.6': {} '@rescript/std@9.0.0': {} @@ -15394,7 +15645,7 @@ snapshots: cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.0 - debug: 4.4.0 + debug: 4.4.0(supports-color@8.1.1) esbuild: 0.24.2 joycon: 3.1.1 picocolors: 1.1.1 @@ -15841,22 +16092,22 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 22.13.1 + '@types/node': 20.17.19 '@types/responselike': 1.0.3 '@types/chai@4.3.20': {} '@types/cli-progress@3.11.6': dependencies: - '@types/node': 22.13.4 + '@types/node': 20.17.19 '@types/concat-stream@1.6.1': dependencies: - '@types/node': 8.10.66 + '@types/node': 20.17.19 '@types/connect@3.4.38': dependencies: - '@types/node': 20.17.6 + '@types/node': 20.17.19 '@types/d3-array@3.2.1': {} @@ -15892,7 +16143,7 @@ snapshots: '@types/form-data@0.0.33': dependencies: - '@types/node': 8.10.66 + '@types/node': 20.17.19 '@types/gensync@1.0.4': {} @@ -15915,7 +16166,9 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 22.13.1 + '@types/node': 20.17.19 + + '@types/lodash@4.17.15': {} '@types/long@4.0.2': {} @@ -15943,10 +16196,6 @@ snapshots: dependencies: undici-types: 6.19.8 - '@types/node@22.13.1': - dependencies: - undici-types: 6.20.0 - '@types/node@22.13.4': dependencies: undici-types: 6.20.0 @@ -15961,7 +16210,7 @@ snapshots: '@types/pbkdf2@3.1.2': dependencies: - '@types/node': 22.13.4 + '@types/node': 20.17.19 '@types/prettier@2.7.3': {} @@ -15980,11 +16229,11 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 22.13.1 + '@types/node': 20.17.19 '@types/secp256k1@4.0.6': dependencies: - '@types/node': 20.17.6 + '@types/node': 20.17.19 '@types/stack-utils@2.0.3': {} @@ -15996,11 +16245,11 @@ snapshots: '@types/ws@7.4.7': dependencies: - '@types/node': 20.17.6 + '@types/node': 20.17.19 '@types/ws@8.5.12': dependencies: - '@types/node': 20.17.6 + '@types/node': 20.17.19 '@types/ws@8.5.14': dependencies: @@ -17394,9 +17643,9 @@ snapshots: busboy: 1.6.0 tslib: 2.8.1 - '@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8)': + '@zodios/core@10.9.6(axios@1.7.9)(zod@3.23.8)': dependencies: - axios: 1.7.4 + axios: 1.7.9 zod: 3.23.8 JSONStream@1.3.2: @@ -17440,6 +17689,8 @@ snapshots: acorn@8.14.0: {} + aes-js@3.0.0: {} + aes-js@4.0.0-beta.5: {} agent-base@7.1.3: {} @@ -17601,6 +17852,14 @@ snapshots: transitivePeerDependencies: - debug + axios@1.7.9: + dependencies: + follow-redirects: 1.15.9(debug@4.3.4) + form-data: 4.0.1 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + babel-core@7.0.0-bridge.0(@babel/core@7.26.8): dependencies: '@babel/core': 7.26.8 @@ -17698,6 +17957,8 @@ snapshots: base64-js@1.5.1: {} + bech32@1.1.4: {} + bech32@2.0.0: {} better-path-resolve@1.0.0: @@ -18073,7 +18334,7 @@ snapshots: citty@0.1.6: dependencies: - consola: 3.2.3 + consola: 3.4.0 class-variance-authority@0.7.0: dependencies: @@ -18268,7 +18529,7 @@ snapshots: transitivePeerDependencies: - supports-color - consola@3.2.3: {} + consola@2.15.3: {} consola@3.4.0: {} @@ -18509,9 +18770,11 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.4.0: + debug@4.4.0(supports-color@8.1.1): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 decamelize@1.2.0: {} @@ -18519,6 +18782,8 @@ snapshots: decimal.js-light@2.5.1: {} + decimal.js@10.5.0: {} + decode-uri-component@0.2.2: {} decompress-response@6.0.0: @@ -18593,7 +18858,7 @@ snapshots: dns-over-http-resolver@1.2.3(node-fetch@2.7.0(encoding@0.1.13)): dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.4.0(supports-color@8.1.1) native-fetch: 3.0.0(node-fetch@2.7.0(encoding@0.1.13)) receptacle: 1.3.2 transitivePeerDependencies: @@ -18674,7 +18939,7 @@ snapshots: dependencies: '@types/secp256k1': 4.0.6 futoin-hkdf: 1.5.3 - secp256k1: 5.0.0 + secp256k1: 5.0.1 eciesjs@0.3.21: dependencies: @@ -18709,16 +18974,6 @@ snapshots: minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - elliptic@6.5.7: - dependencies: - bn.js: 4.12.1 - brorand: 1.1.0 - hash.js: 1.1.7 - hmac-drbg: 1.0.1 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - minimalistic-crypto-utils: 1.0.1 - elliptic@6.6.1: dependencies: bn.js: 4.12.1 @@ -19014,6 +19269,42 @@ snapshots: ethereum-cryptography: 0.1.3 rlp: 2.2.7 + ethers@5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/abstract-provider': 5.7.0 + '@ethersproject/abstract-signer': 5.7.0 + '@ethersproject/address': 5.7.0 + '@ethersproject/base64': 5.7.0 + '@ethersproject/basex': 5.7.0 + '@ethersproject/bignumber': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/contracts': 5.7.0 + '@ethersproject/hash': 5.7.0 + '@ethersproject/hdnode': 5.7.0 + '@ethersproject/json-wallets': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/logger': 5.7.0 + '@ethersproject/networks': 5.7.1 + '@ethersproject/pbkdf2': 5.7.0 + '@ethersproject/properties': 5.7.0 + '@ethersproject/providers': 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@ethersproject/random': 5.7.0 + '@ethersproject/rlp': 5.7.0 + '@ethersproject/sha2': 5.7.0 + '@ethersproject/signing-key': 5.7.0 + '@ethersproject/solidity': 5.7.0 + '@ethersproject/strings': 5.7.0 + '@ethersproject/transactions': 5.7.0 + '@ethersproject/units': 5.7.0 + '@ethersproject/wallet': 5.7.0 + '@ethersproject/web': 5.7.1 + '@ethersproject/wordlists': 5.7.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + ethers@6.13.4(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@adraffy/ens-normalize': 1.10.1 @@ -19720,7 +20011,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.3 - debug: 4.4.0 + debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -19738,7 +20029,7 @@ snapshots: https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 - debug: 4.4.0 + debug: 4.4.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -19857,7 +20148,7 @@ snapshots: any-signal: 2.1.2 blob-to-it: 1.0.4 browser-readablestream-to-it: 1.0.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.4.0(supports-color@8.1.1) err-code: 3.0.1 ipfs-core-types: 0.9.0(node-fetch@2.7.0(encoding@0.1.13)) ipfs-unixfs: 6.0.9 @@ -19886,7 +20177,7 @@ snapshots: '@ipld/dag-pb': 2.1.18 abort-controller: 3.0.0 any-signal: 2.1.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.4.0(supports-color@8.1.1) err-code: 3.0.1 ipfs-core-types: 0.9.0(node-fetch@2.7.0(encoding@0.1.13)) ipfs-core-utils: 0.13.0(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13)) @@ -20484,7 +20775,7 @@ snapshots: '@parcel/watcher-wasm': 2.4.1 citty: 0.1.6 clipboardy: 4.0.0 - consola: 3.2.3 + consola: 3.4.0 crossws: 0.3.1 defu: 6.1.4 get-port-please: 3.1.2 @@ -21642,7 +21933,7 @@ snapshots: '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 '@types/long': 4.0.2 - '@types/node': 22.13.4 + '@types/node': 20.17.19 long: 4.0.0 protobufjs@7.4.0: @@ -21657,7 +21948,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 22.13.1 + '@types/node': 20.17.19 long: 5.2.4 proxy-compare@2.5.1: {} @@ -22272,12 +22563,6 @@ snapshots: node-addon-api: 5.1.0 node-gyp-build: 4.8.4 - secp256k1@5.0.0: - dependencies: - elliptic: 6.5.7 - node-addon-api: 5.1.0 - node-gyp-build: 4.8.4 - secp256k1@5.0.1: dependencies: elliptic: 6.6.1 @@ -23044,7 +23329,7 @@ snapshots: typechain@8.3.2(typescript@5.6.3): dependencies: '@types/prettier': 2.7.3 - debug: 4.4.0 + debug: 4.4.0(supports-color@8.1.1) fs-extra: 7.0.1 glob: 7.1.7 js-sha3: 0.8.0 @@ -23089,7 +23374,7 @@ snapshots: unenv@1.10.0: dependencies: - consola: 3.2.3 + consola: 3.4.0 defu: 6.1.4 mime: 3.0.0 node-fetch-native: 1.6.4 @@ -23136,7 +23421,7 @@ snapshots: untun@0.1.3: dependencies: citty: 0.1.6 - consola: 3.2.3 + consola: 3.4.0 pathe: 1.1.2 update-browserslist-db@1.1.1(browserslist@4.24.0): @@ -23480,6 +23765,11 @@ snapshots: bufferutil: 4.0.8 utf-8-validate: 5.0.10 + ws@7.4.6(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 + ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.8 From efea4f38dc5cb86ed27c38a3ecd849ab3b38b5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Tue, 18 Feb 2025 16:04:24 +0100 Subject: [PATCH 11/17] chore: reorder imports in fetchRedstone.ts file --- fetchRedstone.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fetchRedstone.ts b/fetchRedstone.ts index 4ff4639e..4e17af25 100644 --- a/fetchRedstone.ts +++ b/fetchRedstone.ts @@ -1,6 +1,6 @@ -import { ContractParamsProvider } from '@redstone-finance/sdk'; import http from 'node:http'; import https from 'node:https'; +import { ContractParamsProvider } from '@redstone-finance/sdk'; const originalHttpRequest = http.request; http.request = function (...args) { From a895a08e5b02c7d0c5a2688365dbe357a974f60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Wed, 19 Feb 2025 09:38:40 +0100 Subject: [PATCH 12/17] fix: add skipping price updates if length is 0 --- contracts/market/src/main.sw | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/contracts/market/src/main.sw b/contracts/market/src/main.sw index 9b26f2fc..ec9ad7aa 100644 --- a/contracts/market/src/main.sw +++ b/contracts/market/src/main.sw @@ -412,8 +412,9 @@ impl Market for Contract { .insert((caller, asset_id), user_collateral); // Update price data - update_price_feeds_if_necessary_internal(price_data_update); - + if price_data_update.price_feed_ids.len() > 0 { + update_price_feeds_if_necessary_internal(price_data_update); + } // Note: no accrue interest, BorrowCollateralFactor < LiquidationCollateralFactor covers small changes // Check if the user is borrow collateralized require( @@ -664,7 +665,9 @@ impl Market for Contract { ); // Update price data - update_price_feeds_if_necessary_internal(price_data_update); + if price_data_update.price_feed_ids.len() > 0 { + update_price_feeds_if_necessary_internal(price_data_update); + } // Check that the user is borrow collateralized require( @@ -800,7 +803,9 @@ impl Market for Contract { accrue_internal(); // Update price data - update_price_feeds_if_necessary_internal(price_data_update); + if price_data_update.price_feed_ids.len() > 0 { + update_price_feeds_if_necessary_internal(price_data_update); + } let mut index = 0; // Loop and absorb each account From c5fc5ba98f48795583f51ee14a4da31ed22c5368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Wed, 19 Feb 2025 09:42:41 +0100 Subject: [PATCH 13/17] chore: set contract version to 6 --- contracts/market/src/main.sw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/market/src/main.sw b/contracts/market/src/main.sw index ec9ad7aa..a45739b4 100644 --- a/contracts/market/src/main.sw +++ b/contracts/market/src/main.sw @@ -37,7 +37,7 @@ use sway_libs::ownership::*; use sway_libs::signed_integers::i256::I256; // version of the smart contract -const VERSION: u8 = 5_u8; +const VERSION: u8 = 6_u8; // pyth oracle configuration params const ORACLE_MAX_STALENESS: u64 = 60; // 60 seconds From 26fa7ad5752ddfc84bb2fb4d0f4d991d2bd2ca2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Mon, 24 Feb 2025 14:30:11 +0100 Subject: [PATCH 14/17] chore: wip --- abis/market_abi/src/structs.sw | 4 +- contracts/market/Forc.toml | 3 +- scripts/src/update_collateral_assets.rs | 147 +++++++++++++----------- scripts/src/utils.rs | 8 +- 4 files changed, 91 insertions(+), 71 deletions(-) diff --git a/abis/market_abi/src/structs.sw b/abis/market_abi/src/structs.sw index 71ef1b49..e0c2a0bf 100644 --- a/abis/market_abi/src/structs.sw +++ b/abis/market_abi/src/structs.sw @@ -16,8 +16,6 @@ pub struct CollateralConfiguration { pub asset_id: AssetId, /// This field holds the price feed ID for the asset. pub price_feed_id: b256, - /// This field holds the redstone feed ID for the asset. - pub redstone_feed_id: u256, /// This field represents the number of decimals for the asset. pub decimals: u32, /// This field represents the collateral factor for borrowing. @@ -30,6 +28,8 @@ pub struct CollateralConfiguration { pub supply_cap: u64, // decimals: asset decimals /// This field indicates whether the collateral is paused. pub paused: bool, + /// This field holds the redstone feed ID for the asset. + pub redstone_feed_id: u256, } /// This struct contains the configuration details for a market. diff --git a/contracts/market/Forc.toml b/contracts/market/Forc.toml index ee8f1a8e..7778192a 100644 --- a/contracts/market/Forc.toml +++ b/contracts/market/Forc.toml @@ -5,7 +5,8 @@ name = "market" # address will be the addres of the proxy contract [proxy] -address = "0x657ab45a6eb98a4893a99fd104347179151e8b3828fd8f2a108cc09770d1ebae" +# address = "0x657ab45a6eb98a4893a99fd104347179151e8b3828fd8f2a108cc09770d1ebae" +address = "0xbeaa0e9479a83eb3eab0f66e0f0c0ce4af86a6fe6fd4549ae22d87083e086821" enabled = true [dependencies] diff --git a/scripts/src/update_collateral_assets.rs b/scripts/src/update_collateral_assets.rs index c49215bd..77c4646c 100644 --- a/scripts/src/update_collateral_assets.rs +++ b/scripts/src/update_collateral_assets.rs @@ -4,7 +4,7 @@ use clap::Parser; use fuels::{ accounts::{provider::Provider, wallet::WalletUnlocked}, crypto::SecretKey, - types::AssetId, + types::{AssetId, U256}, }; use std::str::FromStr; use utils::{ @@ -57,80 +57,93 @@ async fn main() -> anyhow::Result<()> { ); let market_config = read_market_config(&args.config_path)?; + println!("Market config: {:#?}", market_config); // get current collateral assets configurations - let collateral_asset_configurations = market_instance - .methods() - .get_collateral_configurations() - .with_contract_ids(&[market_contract_id.clone()]) - .call() - .await? - .value; + // let collateral_asset_configurations = market_instance + // .methods() + // .get_collateral_configurations() + // .with_contract_ids(&[market_contract_id.clone()]) + // .call() + // .await? + // .value; + // println!( + // "Collateral assets configurations: {:#?}", + // collateral_asset_configurations + // ); // iterater over collateral assets configurations in the market config file // if the collateral asset is already added, check if all properties are the same // otherwise, add the collateral asset for collateral_asset_config in market_config.collateral_assets { let asset_id = collateral_asset_config.clone().asset_id; - let asset_config = collateral_asset_configurations - .iter() - .find(|config| config.asset_id == AssetId::from_str(asset_id.as_str()).unwrap()); - - match asset_config { - Some(asset_config) => { - if asset_config != &collateral_asset_config { - println!( - "Updating collateral asset configuration for asset_id: {}", - asset_id - ); - println!("Old collateral asset configuration: {:#?}", asset_config); - println!( - "New collateral asset configuration: {:#?}", - collateral_asset_config - ); - if !get_yes_no_input( - "Do you really want to update this collateral asset? (yes/no): ", - ) { - continue; - } - - market_instance - .methods() - .update_collateral_asset( - AssetId::from_str(asset_id.as_str()).unwrap(), - collateral_asset_config.into(), - ) - .with_contract_ids(&[market_contract_id.clone()]) - .call() - .await?; - } else { - println!( - "Collateral asset configuration for asset_id: {} is already up-to-date", - asset_id - ); - } - } - None => { - println!( - "Adding collateral asset configuration for asset_id: {}", - asset_id - ); - println!( - "Collateral asset configuration: {:#?}", - collateral_asset_config - ); - if !get_yes_no_input("Do you really want to add this collateral asset? (yes/no): ") - { - continue; - } - market_instance - .methods() - .add_collateral_asset(collateral_asset_config.into()) - .with_contract_ids(&[market_contract_id.clone()]) - .call() - .await?; - } - } + // let asset_config = collateral_asset_configurations + // .iter() + // .find(|config| config.asset_id == AssetId::from_str(asset_id.as_str()).unwrap()); + market_instance + .methods() + .update_collateral_asset( + AssetId::from_str(asset_id.as_str()).unwrap(), + collateral_asset_config.into(), + ) + .with_contract_ids(&[market_contract_id.clone()]) + .call() + .await?; + // match asset_config { + // Some(asset_config) => { + // if asset_config != &collateral_asset_config { + // println!( + // "Updating collateral asset configuration for asset_id: {}", + // asset_id + // ); + // println!("Old collateral asset configuration: {:#?}", asset_config); + // println!( + // "New collateral asset configuration: {:#?}", + // collateral_asset_config + // ); + // if !get_yes_no_input( + // "Do you really want to update this collateral asset? (yes/no): ", + // ) { + // continue; + // } + + // market_instance + // .methods() + // .update_collateral_asset( + // AssetId::from_str(asset_id.as_str()).unwrap(), + // collateral_asset_config.into(), + // ) + // .with_contract_ids(&[market_contract_id.clone()]) + // .call() + // .await?; + // } else { + // println!( + // "Collateral asset configuration for asset_id: {} is already up-to-date", + // asset_id + // ); + // } + // } + // None => { + // println!( + // "Adding collateral asset configuration for asset_id: {}", + // asset_id + // ); + // println!( + // "Collateral asset configuration: {:#?}", + // collateral_asset_config + // ); + // if !get_yes_no_input("Do you really want to add this collateral asset? (yes/no): ") + // { + // continue; + // } + // market_instance + // .methods() + // .add_collateral_asset(collateral_asset_config.into()) + // .with_contract_ids(&[market_contract_id.clone()]) + // .call() + // .await?; + // } + // } } // read values to see if they are set correctly diff --git a/scripts/src/utils.rs b/scripts/src/utils.rs index 9c639bc4..650b41be 100644 --- a/scripts/src/utils.rs +++ b/scripts/src/utils.rs @@ -70,6 +70,7 @@ pub struct BaseAssetConfig { pub struct CollateralAssetConfig { pub asset_id: String, pub price_feed_id: String, + pub redstone_feed_id: Option, pub name: String, pub symbol: String, pub decimals: u32, @@ -263,8 +264,13 @@ impl PartialEq for CollateralAssetConfig { pub fn read_market_config(path: &str) -> anyhow::Result { let config_path = PathBuf::from(path); let config_str = std::fs::read_to_string(config_path)?; - serde_json::from_str(&config_str) + let mut market_config: MarketConfig = serde_json::from_str(&config_str) .map_err(|e| anyhow::anyhow!("Failed to parse market config: {}", e)) + .unwrap(); + for asset in &mut market_config.collateral_assets { + asset.redstone_feed_id = Some(U256::from(asset.symbol.as_bytes())); + } + Ok(market_config) } pub async fn verify_connected_network( From e8653ffee37eb0fee9aeaa9d370ecb141b4de500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Mon, 24 Feb 2025 18:20:18 +0100 Subject: [PATCH 15/17] chore: wip --- contracts/market/src/main.sw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/market/src/main.sw b/contracts/market/src/main.sw index a45739b4..1ef83128 100644 --- a/contracts/market/src/main.sw +++ b/contracts/market/src/main.sw @@ -37,13 +37,13 @@ use sway_libs::ownership::*; use sway_libs::signed_integers::i256::I256; // version of the smart contract -const VERSION: u8 = 6_u8; +const VERSION: u8 = 7_u8; // pyth oracle configuration params const ORACLE_MAX_STALENESS: u64 = 60; // 60 seconds -const ORACLE_DOWNTIME_THRESHOLD: u64 = 300; // 5 minutes const ORACLE_MAX_AHEADNESS: u64 = 60; // 60 seconds const ORACLE_MAX_CONF_WIDTH: u256 = 300; // 300 / 10000 = 3.0 % +const ORACLE_DOWNTIME_THRESHOLD: u64 = 300; // 5 minutes const REDSTONE_PRICE_EXPONENT: u32 = 8; // This is set during deployment of the contract configurable { From 81b4d6520c6333090d746cbb6670742355829a74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Mon, 24 Feb 2025 18:21:04 +0100 Subject: [PATCH 16/17] chore: update cargo toml --- scripts/Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/Cargo.toml b/scripts/Cargo.toml index c872c9a6..d5630dc3 100644 --- a/scripts/Cargo.toml +++ b/scripts/Cargo.toml @@ -44,6 +44,10 @@ path = "src/deploy_pyth.rs" name = "fill_reserves" path = "src/fill_reserves.rs" +[[bin]] +name = "get_market_owner" +path = "src/get_market_owner.rs" + [dependencies] fuels = { workspace = true } clap = { workspace = true } From 30a23560e6faeac11db57400f5b41a343ac844c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urban=20Vidovic=CC=8C?= Date: Fri, 28 Feb 2025 14:43:25 +0100 Subject: [PATCH 17/17] chore: update deployments --- DEPLOYMENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPLOYMENTS.md b/DEPLOYMENTS.md index 1fa27587..26f8c1c4 100644 --- a/DEPLOYMENTS.md +++ b/DEPLOYMENTS.md @@ -22,7 +22,7 @@ * Token factory contract: `0x3e4f1948aece07d3f30c8c5c425f914ac74653827de48394466f2a887eebe9c7` -* Market implementation contract: `0x63528412ed65c545ef1e3fb1988721f78c675e48265b70d8d55fbc42e1460730` +* Market implementation contract: `0x7a5bd92931474e3f86026d91fc0d556a79423dd1a2f6dd881e463443ae7459eb` * Market proxy contract: `0x6030cf103746576706d7dcc2ae6f6b32ba0db66907a8f9901a0170de5f06acc0`