diff --git a/target_chains/aptos/contracts/sources/pyth.move b/target_chains/aptos/contracts/sources/pyth.move index a831643061..66a3c129c8 100644 --- a/target_chains/aptos/contracts/sources/pyth.move +++ b/target_chains/aptos/contracts/sources/pyth.move @@ -30,7 +30,6 @@ module pyth::pyth { const PYTHNET_ACCUMULATOR_UPDATE_MAGIC: u64 = 1347305813; const ACCUMULATOR_UPDATE_WORMHOLE_VERIFICATION_MAGIC: u64 = 1096111958; - // ----------------------------------------------------------------------------- // Initialisation functions @@ -520,6 +519,127 @@ module pyth::pyth { }; state::get_base_update_fee() * total_updates } + + + /// Parse Benchmark Prices Method + + /// Parses and validates price feeds within the specified publish time range. + fun parse_and_validate_price_feeds( + price_infos: &vector, + requested_price_ids: vector>, + min_publish_time: u64, + max_publish_time: u64 + ): vector { + // Initialize vectors to store parsed price feeds and price IDs + let valid_price_feeds = vector::empty(); + let valid_price_ids = vector::empty>(); + + // Iterate through the price information to filter and validate based on publish times + let i = 0; + while (i < vector::length(price_infos)) { + let price_info = vector::borrow(price_infos, i); + let price_feed = price_info::get_price_feed(price_info); + let price: price::Price = price_feed::get_price(price_feed); + let timestamp = price::get_timestamp(&price); + + // Check if the price feed is within the specified publish time range + if (timestamp >= min_publish_time && timestamp <= max_publish_time) { + let price_id: &price_identifier::PriceIdentifier = price_feed::get_price_identifier(price_feed); + let price_id_bytes = price_identifier::get_bytes(price_id); + vector::push_back(&mut valid_price_ids, price_id_bytes); + vector::push_back(&mut valid_price_feeds, *price_feed); + }; + i = i + 1; + }; + + // Ensure all requested price IDs have corresponding valid updates + let k = 0; + while (k < vector::length(&requested_price_ids)) { + let requested_price_id = vector::borrow(&requested_price_ids, k); + let is_found = false; + + // Check if the requested price ID is in the valid price IDs + let j = 0; + while (j < vector::length(&valid_price_ids)) { + let valid_price_id = vector::borrow(&valid_price_ids, j); + if (requested_price_id == valid_price_id) { + is_found = true; + break + }; + j = j + 1; + }; + + // Abort if any requested price ID does not have a valid update + if (!is_found) { + abort error::unknown_price_feed() // Replace with a more suitable error if needed + }; + k = k + 1; + }; + + return valid_price_feeds + } + + /// Parses a single VAA and returns a vector of price feeds within the specified publish time range. + fun parse_price_feed_updates_from_vaa( + vaa: vector, + requested_price_ids: vector>, + min_publish_time: u64, + max_publish_time: u64 + ): vector { + let cur = cursor::init(vaa); + let header: u64 = deserialize::deserialize_u32(&mut cur); + if (header == PYTHNET_ACCUMULATOR_UPDATE_MAGIC) { + let price_infos = parse_and_verify_accumulator_message(&mut cur); + cursor::rest(cur); + return parse_and_validate_price_feeds(&price_infos, requested_price_ids, min_publish_time, max_publish_time) + } else { + let vaa = vaa::parse_and_verify(vaa); + verify_data_source(&vaa); + let price_infos = batch_price_attestation::destroy(batch_price_attestation::deserialize(vaa::destroy(vaa))); + cursor::rest(cur); + return parse_and_validate_price_feeds(&price_infos, requested_price_ids, min_publish_time, max_publish_time) + } + } + + /// Public function to parse multiple price feed updates from VAA data. + public fun parse_price_feed_updates( + update_data: vector>, + requested_price_ids: vector>, + min_publish_time: u64, + max_publish_time: u64, + fee: Coin + ): vector { + // Validate and deposit the update fee + let update_fee = get_update_fee(&update_data); + assert!(update_fee <= coin::value(&fee), error::insufficient_fee()); + coin::deposit(@pyth, fee); + let pyth_balance = coin::balance(@pyth); + assert!(pyth_balance >= update_fee, error::insufficient_fee()); + + // Initialize a vector to store all valid price feeds + let all_valid_price_feeds = vector::empty(); + + // Iterate through the update_data vector + let i = 0; + while (i < vector::length(&update_data)) { + // Parse a single VAA and get its valid price feeds + let single_vaa = vector::borrow(&update_data, i); + let valid_price_feeds = parse_price_feed_updates_from_vaa(*single_vaa, requested_price_ids, min_publish_time, max_publish_time); + + // Add each valid price feed to the all_valid_price_feeds vector + let j = 0; + while (j < vector::length(&valid_price_feeds)) { + let price_feed = vector::borrow(&valid_price_feeds, j); + vector::push_back(&mut all_valid_price_feeds, *price_feed); + j = j + 1; + }; + + i = i + 1; + }; + + all_valid_price_feeds + } + } // ----------------------------------------------------------------------------- @@ -1430,4 +1550,172 @@ module pyth::pyth_test { cleanup_test(burn_capability, mint_capability); } -} + + // Test case for successful parsing of price feed updates + #[test(aptos_framework = @aptos_framework)] + fun test_parse_price_feed_updates_success(aptos_framework: &signer) { + let update_fee = 50; + let initial_balance = 100; + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, + x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", + vector[/* Add valid DataSource objects here */], + update_fee, + initial_balance); + + let update_data = vector[ + /* Add actual VAA bytes here for testing */ + ]; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + let min_publish_time: u64 = 1663074345; + let max_publish_time: u64 = 1663680750; + + let test_price_feeds: vector = pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + + assert!(vector::length(&test_price_feeds) > 0, 1); + // Add further assertions based on expected results + + cleanup_test(burn_capability, mint_capability); + } + + // Test case for insufficient fee during parsing of price feed updates + #[test(aptos_framework = @aptos_framework)] + #[expected_failure(abort_code = 65542, location = pyth::pyth)] + fun test_parse_price_feed_updates_insufficient_fee(aptos_framework: &signer) { + let update_fee = 50; + let initial_balance = 20; + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, + x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", + vector[/* Add valid DataSource objects here */], + update_fee, + initial_balance); + + let update_data = vector[ + /* Add actual VAA bytes here for testing */ + ]; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + let min_publish_time: u64 = 1663074345; + let max_publish_time: u64 = 1663680750; + + pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + + cleanup_test(burn_capability, mint_capability); + } + + // Test case for corrupt VAA during parsing of price feed updates + #[test(aptos_framework = @aptos_framework)] + #[expected_failure(abort_code = 6, location = wormhole::vaa)] + fun test_parse_price_feed_updates_corrupt_vaa(aptos_framework: &signer) { + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", vector[], 50, 100); + + let corrupt_vaa = x"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + let min_publish_time: u64 = 1663074345; + let max_publish_time: u64 = 1663680750; + + pyth::parse_price_feed_updates(vector[corrupt_vaa], price_ids, min_publish_time, max_publish_time, coins); + + cleanup_test(burn_capability, mint_capability); + } + + // Test case for invalid data source during parsing of price feed updates + #[test(aptos_framework = @aptos_framework)] + #[expected_failure(abort_code = 65539, location = pyth::pyth)] + fun test_parse_price_feed_updates_invalid_data_source(aptos_framework: &signer) { + let update_data = vector[ + /* Add actual VAA bytes here for testing */ + ]; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + let min_publish_time: u64 = 1663074345; + let max_publish_time: u64 = 1663680750; + + let data_sources = vector[ + DataSource { id: 4, address: external_address::from_bytes(x"0000000000000000000000000000000000000000000000000000000000007742") }, + DataSource { id: 5, address: external_address::from_bytes(x"0000000000000000000000000000000000000000000000000000000000007637") } + ]; + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources, 50, 100); + + pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + + cleanup_test(burn_capability, mint_capability); + } + + // Test case for invalid price ID during parsing of price feed updates + #[test(aptos_framework = @aptos_framework)] + #[expected_failure(abort_code = 393224, location = pyth::pyth)] + fun test_parse_price_feed_updates_invalid_price_id(aptos_framework: &signer) { + let update_fee = 50; + let initial_balance = 100; + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, + x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", + vector[/* Add valid DataSource objects here */], + update_fee, + initial_balance); + + let update_data = vector[ + /* Add actual VAA bytes here for testing */ + ]; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d2", // invalid price id + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + let min_publish_time: u64 = 1663074345; + let max_publish_time: u64 = 1663680750; + + pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + + cleanup_test(burn_capability, mint_capability); + } + + // Test case for invalid publish times during parsing of price feed updates + #[test(aptos_framework = @aptos_framework)] + #[expected_failure(abort_code = 393224, location = pyth::pyth)] + fun test_parse_price_feed_updates_invalid_publish_times(aptos_framework: &signer) { + let update_fee = 50; + let initial_balance = 100; + let (burn_capability, mint_capability, coins) = setup_test(aptos_framework, 500, 1, + x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", + vector[/* Add valid DataSource objects here */], + update_fee, + initial_balance); + + let update_data = vector[ + /* Add actual VAA bytes here for testing */ + ]; + let price_ids = vector[ + x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1", + x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe", + x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d", + x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8", + ]; + // invalid publish times: max_publish_time is less than min_publish_time + let min_publish_time: u64 = 1663680750; + let max_publish_time: u64 = 1663074345; + + pyth::parse_price_feed_updates(update_data, price_ids, min_publish_time, max_publish_time, coins); + + cleanup_test(burn_capability, mint_capability); + } + +} \ No newline at end of file diff --git a/target_chains/ethereum/sdk/solidity/PriceCombine.sol b/target_chains/ethereum/sdk/solidity/PriceCombine.sol new file mode 100644 index 0000000000..f1e90da722 --- /dev/null +++ b/target_chains/ethereum/sdk/solidity/PriceCombine.sol @@ -0,0 +1,167 @@ +// PriceCombine.sol +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.0; + +import "./PythStructs.sol"; + +// Constants for working with Pyth's number representation +int32 constant PD_EXPO = -9; +uint64 constant PD_SCALE = 1_000_000_000; +uint64 constant MAX_PD_V_U64 = (1 << 28) - 1; + +// Solidity Library to easily combine 2 Pyth Prices into 1 ( for example SOL:USD/ETH:USD=SOL/ETH). For Rust implementation, refer Pyth Rust SDK https://github.com/pyth-network/pyth-sdk-rs/blob/main/pyth-sdk/src/price.rs +// Based on the Rust implementation, the functions are defined within the `impl Price` block, and they are public methods of the `Price` struct and are as follows `get_price_in_quote`, `div`, `normalize`, `scale_to_exponent` +// Similarly the functions implemented in solidity are `public` and `pure` types. + +library PriceCombine { + /** + * @notice Computes the price in quote currency. + * @param self The price of the base currency. + * @param quote The price of the quote currency. + * @param resultExpo The exponent for the result. + * @return The combined price in the quote currency. + */ + function getPriceInQuote(PythStructs.Price memory self, PythStructs.Price memory quote, int32 resultExpo) public pure returns (PythStructs.Price memory) { + PythStructs.Price memory result = div(self, quote); + // Return zero price so the error can be gracefully handled by the caller + if (result.price == 0 && result.conf == 0) { + return PythStructs.Price({ + price: 0, + conf: 0, + expo: 0, + publishTime: 0 + }); + } + return scaleToExponent(result, resultExpo); + } + + /** + * @notice Divides the price of the base currency by the quote currency price. + * @param self The price of the base currency. + * @param other The price of the quote currency. + * @return The combined price after division. + */ + function div(PythStructs.Price memory self, PythStructs.Price memory other) public pure returns (PythStructs.Price memory) { + PythStructs.Price memory base = normalize(self); + other = normalize(other); + + // If the price of the quote currency is zero, return zero + if (other.price == 0) { + return PythStructs.Price({ + price: 0, + conf: 0, + expo: 0, + publishTime: 0 + }); + } + + // Convert prices to unsigned integers and get their signs + (uint64 basePrice, int64 baseSign) = toUnsigned(base.price); + (uint64 otherPrice, int64 otherSign) = toUnsigned(other.price); + + // Compute the midprice + uint64 midprice = basePrice * PD_SCALE / otherPrice; + int32 midpriceExpo = base.expo - other.expo + PD_EXPO; + + // Compute the confidence interval + uint64 otherConfidencePct = other.conf * PD_SCALE / otherPrice; + uint128 conf = (base.conf * PD_SCALE / otherPrice) + (otherConfidencePct * midprice) / PD_SCALE; + + // Check for overflow and return the result + if (conf < type(uint64).max) { + return PythStructs.Price( + int64(int64(midprice) * baseSign * otherSign), + uint64(conf), + midpriceExpo, + self.publishTime < other.publishTime ? self.publishTime : other.publishTime + ); + } else { + // Return zero price if there's an overflow + return PythStructs.Price({ + price: 0, + conf: 0, + expo: 0, + publishTime: 0 + }); + } + } + + /** + * @notice Normalizes the price and confidence to be within acceptable range. + * @param self The price structure to normalize. + * @return The normalized price structure. + */ + function normalize(PythStructs.Price memory self) public pure returns (PythStructs.Price memory) { + (uint64 price, int64 sign) = toUnsigned(self.price); + uint64 conf = self.conf; + int32 expo = self.expo; + + // Adjust the price and confidence if they are too large + while (price > MAX_PD_V_U64 || conf > MAX_PD_V_U64) { + price = price / 10; + conf = conf / 10; + expo = expo + 1; + } + + return PythStructs.Price({ + price: int64(price) * sign, + conf: conf, + expo: expo, + publishTime: self.publishTime + }); + } + + /** + * @notice Scales the price to the target exponent. + * @param self The price structure to scale. + * @param targetExpo The target exponent. + * @return The scaled price structure. + */ + function scaleToExponent(PythStructs.Price memory self, int32 targetExpo) public pure returns (PythStructs.Price memory) { + int32 delta = targetExpo - self.expo; + int64 price = self.price; + uint64 conf = self.conf; + + // Adjust the price and confidence based on the exponent difference + if (delta >= 0) { + while (delta > 0 && (price != 0 || conf != 0)) { + price = price / 10; + conf = conf / 10; + delta = delta - 1; + } + return PythStructs.Price({ + price: price, + conf: conf, + expo: targetExpo, + publishTime: self.publishTime + }); + } else { + while (delta < 0) { + price = price * 10; + conf = conf * 10; + delta = delta + 1; + } + return PythStructs.Price({ + price: price, + conf: conf, + expo: targetExpo, + publishTime: self.publishTime + }); + } + } + + /** + * @notice Converts a signed integer to an unsigned integer and a sign bit. + * @param x here is the signed integer. + * @return The unsigned integer and sign bit. + */ + function toUnsigned(int64 x) public pure returns (uint64, int64) { + if (x == type(int64).min) { + return (uint64(type(int64).max) + 1, -1); + } else if (x < 0) { + return (uint64(-x), -1); + } else { + return (uint64(x), 1); + } + } +} diff --git a/target_chains/ethereum/sdk/solidity/PriceCombineTest.sol b/target_chains/ethereum/sdk/solidity/PriceCombineTest.sol new file mode 100644 index 0000000000..d984f5a856 --- /dev/null +++ b/target_chains/ethereum/sdk/solidity/PriceCombineTest.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.0; + +import "./PythStructs.sol"; +import "./PriceCombine.sol"; + +// This contract is used to test the library functions and tested in Remix IDE. All ABIs are present in the /abis folder +contract PriceCombineTest { + using PriceCombine for PythStructs.Price; + + /** + * @notice Test the getPriceInQuote function + * @param basePrice The price of the base currency (example: SOL/USD) 13913000000 (represents the current SOL/USD $139.13 with 8 decimal places) + * @param baseConf The confidence interval of the base currency + * @param baseExpo The exponent of the base currency (here: -8 -> 8 decimal units) indicates price is scaled by 10^-8 + * @param basePublishTime The publish time of the base currency (UNIX timestamp) + * @param quotePrice The price of the quote currency (example: ETH/USD) 341853000000 (represents the current ETH/USD $3418.53 with 8 decimal places) + * @param quoteConf The confidence interval of the quote currency + * @param quoteExpo The exponent of the quote currency (here: -8 -> 8 decimal units) indicates price is scaled by 10^-8 + * @param quotePublishTime The publish time of the quote currency (UNIX timestamp) + * @param resultExpo The desired exponent for the result (here: -8) + * @return The price, confidence interval, exponent, and publish time of the resulting price + */ + function testGetPriceInQuote(int64 basePrice, uint64 baseConf, int32 baseExpo, uint basePublishTime, + int64 quotePrice, uint64 quoteConf, int32 quoteExpo, uint quotePublishTime, + int32 resultExpo) public pure returns (int64, uint64, int32, uint) { + PythStructs.Price memory base = PythStructs.Price(basePrice, baseConf, baseExpo, basePublishTime); + PythStructs.Price memory quote = PythStructs.Price(quotePrice, quoteConf, quoteExpo, quotePublishTime); + PythStructs.Price memory result = base.getPriceInQuote(quote, resultExpo); + return (result.price, result.conf, result.expo, result.publishTime); + } + + // Add more test functions as needed +} diff --git a/target_chains/ethereum/sdk/solidity/PythStructs.sol b/target_chains/ethereum/sdk/solidity/PythStructs.sol index b3d2ee2c6a..9696af78bf 100644 --- a/target_chains/ethereum/sdk/solidity/PythStructs.sol +++ b/target_chains/ethereum/sdk/solidity/PythStructs.sol @@ -30,4 +30,4 @@ contract PythStructs { // Latest available exponentially-weighted moving average price Price emaPrice; } -} +} \ No newline at end of file diff --git a/target_chains/ethereum/sdk/solidity/abis/PriceCombine.json b/target_chains/ethereum/sdk/solidity/abis/PriceCombine.json new file mode 100644 index 0000000000..49ce577a55 --- /dev/null +++ b/target_chains/ethereum/sdk/solidity/abis/PriceCombine.json @@ -0,0 +1,342 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "int64", + "name": "price", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "conf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "expo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "publishTime", + "type": "uint256" + } + ], + "internalType": "struct PythStructs.Price", + "name": "self", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "int64", + "name": "price", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "conf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "expo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "publishTime", + "type": "uint256" + } + ], + "internalType": "struct PythStructs.Price", + "name": "other", + "type": "tuple" + } + ], + "name": "div", + "outputs": [ + { + "components": [ + { + "internalType": "int64", + "name": "price", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "conf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "expo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "publishTime", + "type": "uint256" + } + ], + "internalType": "struct PythStructs.Price", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "int64", + "name": "price", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "conf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "expo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "publishTime", + "type": "uint256" + } + ], + "internalType": "struct PythStructs.Price", + "name": "self", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "int64", + "name": "price", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "conf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "expo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "publishTime", + "type": "uint256" + } + ], + "internalType": "struct PythStructs.Price", + "name": "quote", + "type": "tuple" + }, + { + "internalType": "int32", + "name": "resultExpo", + "type": "int32" + } + ], + "name": "getPriceInQuote", + "outputs": [ + { + "components": [ + { + "internalType": "int64", + "name": "price", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "conf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "expo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "publishTime", + "type": "uint256" + } + ], + "internalType": "struct PythStructs.Price", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "int64", + "name": "price", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "conf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "expo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "publishTime", + "type": "uint256" + } + ], + "internalType": "struct PythStructs.Price", + "name": "self", + "type": "tuple" + } + ], + "name": "normalize", + "outputs": [ + { + "components": [ + { + "internalType": "int64", + "name": "price", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "conf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "expo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "publishTime", + "type": "uint256" + } + ], + "internalType": "struct PythStructs.Price", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "int64", + "name": "price", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "conf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "expo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "publishTime", + "type": "uint256" + } + ], + "internalType": "struct PythStructs.Price", + "name": "self", + "type": "tuple" + }, + { + "internalType": "int32", + "name": "targetExpo", + "type": "int32" + } + ], + "name": "scaleToExponent", + "outputs": [ + { + "components": [ + { + "internalType": "int64", + "name": "price", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "conf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "expo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "publishTime", + "type": "uint256" + } + ], + "internalType": "struct PythStructs.Price", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int64", + "name": "x", + "type": "int64" + } + ], + "name": "toUnsigned", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + }, + { + "internalType": "int64", + "name": "", + "type": "int64" + } + ], + "stateMutability": "pure", + "type": "function" + } +] \ No newline at end of file diff --git a/target_chains/ethereum/sdk/solidity/abis/PriceCombineTest.json b/target_chains/ethereum/sdk/solidity/abis/PriceCombineTest.json new file mode 100644 index 0000000000..1ed43ff648 --- /dev/null +++ b/target_chains/ethereum/sdk/solidity/abis/PriceCombineTest.json @@ -0,0 +1,76 @@ +[ + { + "inputs": [ + { + "internalType": "int64", + "name": "basePrice", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "baseConf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "baseExpo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "basePublishTime", + "type": "uint256" + }, + { + "internalType": "int64", + "name": "quotePrice", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "quoteConf", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "quoteExpo", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "quotePublishTime", + "type": "uint256" + }, + { + "internalType": "int32", + "name": "resultExpo", + "type": "int32" + } + ], + "name": "testGetPriceInQuote", + "outputs": [ + { + "internalType": "int64", + "name": "", + "type": "int64" + }, + { + "internalType": "uint64", + "name": "", + "type": "uint64" + }, + { + "internalType": "int32", + "name": "", + "type": "int32" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + } +] \ No newline at end of file diff --git a/target_chains/ethereum/sdk/solidity/abis/PythStructs.json b/target_chains/ethereum/sdk/solidity/abis/PythStructs.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/target_chains/ethereum/sdk/solidity/abis/PythStructs.json @@ -0,0 +1 @@ +[] \ No newline at end of file