From c4d7653ccd25a26e930cee0ce5a2138b0a15eed2 Mon Sep 17 00:00:00 2001 From: JordyRo1 Date: Wed, 6 Nov 2024 08:54:28 +0100 Subject: [PATCH 1/6] feat: chainlink interface --- solidity/src/Pragma.sol | 8 +++ solidity/src/interfaces/IPragma.sol | 5 ++ solidity/src/interfaces/PragmaStructs.sol | 2 +- solidity/src/migrations/Chainlink.sol | 81 +++++++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 solidity/src/migrations/Chainlink.sol diff --git a/solidity/src/Pragma.sol b/solidity/src/Pragma.sol index d78a93e..0c119f7 100644 --- a/solidity/src/Pragma.sol +++ b/solidity/src/Pragma.sol @@ -150,6 +150,14 @@ contract Pragma is Initializable, UUPSUpgradeable, OwnableUpgradeable, IPragma, return validTimePeriodSeconds; } + /* GETTERS */ + /// Even if the getters are automatically set for the public storage variable, we need to define the getter to make it + /// accessible for the interface + + function getSpotMedianFeed(bytes32 feedId) external view returns (SpotMedian memory) { + return spotMedianFeeds[feedId]; + } + function withdrawFunds(uint256 amount) external onlyOwner { require(amount <= address(this).balance, "Insufficient balance"); (bool success,) = owner().call{value: amount}(""); diff --git a/solidity/src/interfaces/IPragma.sol b/solidity/src/interfaces/IPragma.sol index e9a0b94..fea9d4b 100644 --- a/solidity/src/interfaces/IPragma.sol +++ b/solidity/src/interfaces/IPragma.sol @@ -58,4 +58,9 @@ interface IPragma { /// @param id The data feed ID. /// @return True if the data feed exists, false otherwise. function dataFeedExists(bytes32 id) external view returns (bool); + + /// @notice Getter accesssible through the interface + /// @param feedId The data feed id. + /// @return SpotMedian the entry associated to the spot feed id. + function getSpotMedianFeed(bytes32 feedId) external view returns (SpotMedian memory); } diff --git a/solidity/src/interfaces/PragmaStructs.sol b/solidity/src/interfaces/PragmaStructs.sol index b82db03..35041de 100644 --- a/solidity/src/interfaces/PragmaStructs.sol +++ b/solidity/src/interfaces/PragmaStructs.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.28; +pragma solidity ^0.8.20; struct DataFeed { bytes32 feedId; diff --git a/solidity/src/migrations/Chainlink.sol b/solidity/src/migrations/Chainlink.sol new file mode 100644 index 0000000..7d2467b --- /dev/null +++ b/solidity/src/migrations/Chainlink.sol @@ -0,0 +1,81 @@ +import "@pragmaoracle/solidity-sdk/src/interfaces/IPragma.sol"; +import "@pragmaoracle/solidity-sdk/src/interfaces/PragmaStructs.sol"; + +/** + * @title An adapter of the ChainlinkAggregatorV3 interface that supports Pragma price feeds + * Users should deploy an instance of this contract to wrap every price feed id that they need to use. + */ +contract PragmaAggregatorV3 { + bytes32 public feedId; + IPragma public pragmaInterface; + + constructor(address _pragma, bytes32 _feedId) { + feedId = _feedId; + pragmaInterface = IPragma(_pragma); + } + + function updateFeeds(bytes[] calldata priceUpdateData) public payable { + // Update the prices to the latest available values and pay the required fee for it. The `priceUpdateData` data + // should be retrieved from the Theoros SDK, you can find additional information on https://docs.pragmaoracle.com/ + uint256 fee = pragmaInterface.getUpdateFee(priceUpdateData); + pragmaInterface.updatePriceFeeds{value: fee}(priceUpdateData); + + // refund remaining eth + payable(msg.sender).call{value: address(this).balance}(""); + } + + function decimals() public view virtual returns (uint8) { + SpotMedian memory price = pragmaInterface.getSpotMedianFeed(feedId); + return uint8(-1 * int8(price.decimals)); + } + + function description() public pure returns (string memory) { + return "An adapter for Chainlink aggregator by PragmaV2"; + } + + function version() public pure returns (uint256) { + return 1; + } + + function latestAnswer() public view virtual returns (int256) { + SpotMedian memory price = pragmaInterface.getSpotMedianFeed(feedId); + return int256(price.price); + } + + function latestTimestamp() public view returns (uint256) { + SpotMedian memory price = pragmaInterface.getSpotMedianFeed(feedId); + return price.metadata.timestamp; + } + + function latestRound() public view returns (uint256) { + // use timestamp as the round id + return latestTimestamp(); + } + + function getAnswer(uint256) public view returns (int256) { + return latestAnswer(); + } + + function getTimestamp(uint256) external view returns (uint256) { + return latestTimestamp(); + } + + function getRoundData(uint80 _roundId) + external + view + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + SpotMedian memory price = pragmaInterface.getSpotMedianFeed(feedId); + return (_roundId, int256(price.price), price.metadata.timestamp, price.metadata.timestamp, _roundId); + } + + function latestRoundData() + external + view + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + SpotMedian memory price = pragmaInterface.getSpotMedianFeed(feedId); + roundId = uint80(price.metadata.timestamp); + return (roundId, int256(price.price), price.metadata.timestamp, price.metadata.timestamp, roundId); + } +} From 54f62bfe8ff68bbbde171649cdb9208c868cf135 Mon Sep 17 00:00:00 2001 From: JordyRo1 Date: Wed, 6 Nov 2024 09:04:14 +0100 Subject: [PATCH 2/6] fix: corrections --- solidity/package-lock.json | 12 ++++++++++-- solidity/package.json | 3 +++ solidity/remappings.txt | 3 ++- solidity/src/migrations/Chainlink.sol | 15 +++++++++++---- solidity/test/mocks/PragmaUpgraded.sol | 4 ++++ 5 files changed, 30 insertions(+), 7 deletions(-) diff --git a/solidity/package-lock.json b/solidity/package-lock.json index f16798b..49d2804 100644 --- a/solidity/package-lock.json +++ b/solidity/package-lock.json @@ -1,13 +1,16 @@ { "name": "@pragmaoracle/solidity-sdk", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@pragmaoracle/solidity-sdk", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", + "dependencies": { + "@pragmaoracle/solidity-sdk": "^1.0.1" + }, "devDependencies": { "@nomicfoundation/hardhat-ethers": "^3.0.8", "@nomicfoundation/hardhat-toolbox": "^5.0.0", @@ -2198,6 +2201,11 @@ "node": ">=8" } }, + "node_modules/@pragmaoracle/solidity-sdk": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pragmaoracle/solidity-sdk/-/solidity-sdk-1.0.1.tgz", + "integrity": "sha512-x5JOeEk6Tw9X4qFWGtyk2HNeTuBOnBCh/WbibWU+h6tg7A18HxDOV/mvgTw1HZfLDAyDZJ3WPniCP3V1w3eVVA==" + }, "node_modules/@scure/base": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", diff --git a/solidity/package.json b/solidity/package.json index 48b145a..c3b305e 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -37,5 +37,8 @@ "prettier": "^2.7.1", "prettier-plugin-solidity": "^1.0.0-rc.1", "solc": "^0.8.25" + }, + "dependencies": { + "@pragmaoracle/solidity-sdk": "^1.0.1" } } diff --git a/solidity/remappings.txt b/solidity/remappings.txt index 5be0b51..1c6f93e 100644 --- a/solidity/remappings.txt +++ b/solidity/remappings.txt @@ -1,2 +1,3 @@ @openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/ -@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ \ No newline at end of file +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ +@pragmaoracle/solidity-sdk/=node_modules/@pragmaoracle/solidity-sdk/ diff --git a/solidity/src/migrations/Chainlink.sol b/solidity/src/migrations/Chainlink.sol index 7d2467b..1f2076b 100644 --- a/solidity/src/migrations/Chainlink.sol +++ b/solidity/src/migrations/Chainlink.sol @@ -1,5 +1,12 @@ -import "@pragmaoracle/solidity-sdk/src/interfaces/IPragma.sol"; -import "@pragmaoracle/solidity-sdk/src/interfaces/PragmaStructs.sol"; +pragma solidity 0.8.28; + +// Replace this import with those down below once PR merged + +import "../interfaces/PragmaStructs.sol"; +import "../interfaces/IPragma.sol"; + +// import "@pragmaoracle/solidity-sdk/src/interfaces/IPragma.sol"; +// import "@pragmaoracle/solidity-sdk/src/interfaces/PragmaStructs.sol"; /** * @title An adapter of the ChainlinkAggregatorV3 interface that supports Pragma price feeds @@ -18,7 +25,7 @@ contract PragmaAggregatorV3 { // Update the prices to the latest available values and pay the required fee for it. The `priceUpdateData` data // should be retrieved from the Theoros SDK, you can find additional information on https://docs.pragmaoracle.com/ uint256 fee = pragmaInterface.getUpdateFee(priceUpdateData); - pragmaInterface.updatePriceFeeds{value: fee}(priceUpdateData); + pragmaInterface.updateDataFeeds{value: fee}(priceUpdateData); // refund remaining eth payable(msg.sender).call{value: address(this).balance}(""); @@ -26,7 +33,7 @@ contract PragmaAggregatorV3 { function decimals() public view virtual returns (uint8) { SpotMedian memory price = pragmaInterface.getSpotMedianFeed(feedId); - return uint8(-1 * int8(price.decimals)); + return uint8(-1 * int8(price.metadata.decimals)); } function description() public pure returns (string memory) { diff --git a/solidity/test/mocks/PragmaUpgraded.sol b/solidity/test/mocks/PragmaUpgraded.sol index 5464121..3135d08 100644 --- a/solidity/test/mocks/PragmaUpgraded.sol +++ b/solidity/test/mocks/PragmaUpgraded.sol @@ -147,6 +147,10 @@ contract PragmaUpgraded is Initializable, UUPSUpgradeable, OwnableUpgradeable, I function getValidTimePeriod() public view returns (uint256) { return validTimePeriodSeconds; } + + function getSpotMedianFeed(bytes32 feedId) external view returns (SpotMedian memory) { + return spotMedianFeeds[feedId]; + } function version() public pure returns (string memory) { return "2.0.0"; From 9e6deb9b6c2a3276cf9e825bd2a16ada267db467 Mon Sep 17 00:00:00 2001 From: JordyRo1 Date: Wed, 6 Nov 2024 09:15:19 +0100 Subject: [PATCH 3/6] fix: prettier --- solidity/README.md | 18 +- solidity/scripts/index.js | 4 +- solidity/src/Hyperlane.sol | 64 +++- solidity/src/Pragma.sol | 68 ++-- solidity/src/PragmaDecoder.sol | 185 ++++++++--- solidity/src/interfaces/IHyperlane.sol | 16 +- solidity/src/interfaces/IPragma.sol | 36 ++- solidity/src/interfaces/PragmaStructs.sol | 106 ++++--- solidity/src/libraries/BytesLib.sol | 134 ++++++-- solidity/src/libraries/DataParser.sol | 36 ++- solidity/src/libraries/EventsLib.sol | 25 +- solidity/src/libraries/MerkleTree.sol | 39 ++- solidity/src/libraries/UnsafeBytesLib.sol | 129 ++++++-- .../src/libraries/UnsafeCalldataBytesLib.sol | 56 +++- solidity/src/migrations/Chainlink.sol | 36 ++- solidity/test/PragmaDecoder.t.sol | 159 ++++++++-- solidity/test/TestUtils.sol | 293 +++++++++++++----- .../benchmarks/PragmaDecoderGasTest.t.sol | 33 +- solidity/test/mocks/PragmaUpgraded.sol | 62 +++- solidity/test/utils/BytesLibTest.sol | 12 +- solidity/test/utils/HyperlaneTestUtils.t.sol | 199 +++++++++--- solidity/test/utils/PragmaTestUtils.t.sol | 23 +- 22 files changed, 1305 insertions(+), 428 deletions(-) diff --git a/solidity/README.md b/solidity/README.md index 332b871..5c479af 100644 --- a/solidity/README.md +++ b/solidity/README.md @@ -46,10 +46,10 @@ contract YourContract { } /** - * This method is an example of how to interact with the Pragma contract to fetch Spot Median updates. You can check the documentation to - * find the available feeds. - * @param priceUpdate The encoded data to update the contract with the latest price - */ + * This method is an example of how to interact with the Pragma contract to fetch Spot Median updates. You can check the documentation to + * find the available feeds. + * @param priceUpdate The encoded data to update the contract with the latest price + */ function yourFunction(bytes[] calldata priceUpdate) public payable { // Submit a priceUpdate to the Pragma contract to update the on-chain price. // Updating the price requires paying the fee returned by getUpdateFee. @@ -59,9 +59,11 @@ contract YourContract { // Read the current price from a price feed if it is less than 60 seconds old. // Each price feed (e.g., Spot Median ETH/USD) is identified by a unique identifier id. bytes32 id = 0x4554482f555344; // ETH/USD - PragmaStructs.DataFeed memory data_feed = oracle.getSpotMedianNoOlderThan(id, 60); + PragmaStructs.DataFeed memory data_feed = oracle.getSpotMedianNoOlderThan( + id, + 60 + ); } - } ``` @@ -76,8 +78,8 @@ You can find [here](https://docs.pragma.build/v2/Price%20Feeds/supported-assets- Alternatively, you can copy the `IPragma.sol` interface and `PragmaStructs.sol` inside your repository, and generate an instance using a deployed Pragma contract. ```solidity -import {IPragma} from "./interfaces/IPragma.sol"; -import PragmaStructs from "./interfaces/PragmaStructs.sol"; +import { IPragma } from "./interfaces/IPragma.sol"; +import "./interfaces/PragmaStructs.sol" as PragmaStructs; ``` The rest remains the same as described above. diff --git a/solidity/scripts/index.js b/solidity/scripts/index.js index f67e335..17db4dd 100644 --- a/solidity/scripts/index.js +++ b/solidity/scripts/index.js @@ -42,7 +42,7 @@ function generateAbi(contracts) { } const output = JSON.parse( - solc.compile(JSON.stringify(input), { import: findImports }), + solc.compile(JSON.stringify(input), { import: findImports }) ); console.log(output); @@ -57,7 +57,7 @@ function generateAbi(contracts) { const abi = output.contracts[contractFile][trimedContract].abi; fs.writeFileSync( `abis/${trimedContract}.json`, - JSON.stringify(abi, null, 2) + "\n", + JSON.stringify(abi, null, 2) + "\n" ); } } diff --git a/solidity/src/Hyperlane.sol b/solidity/src/Hyperlane.sol index 1f709c5..cca5786 100644 --- a/solidity/src/Hyperlane.sol +++ b/solidity/src/Hyperlane.sol @@ -21,16 +21,26 @@ contract Hyperlane is IHyperlane { _validators = validators; } - function parseAndVerifyHyMsg(bytes calldata encodedHyMsg) + function parseAndVerifyHyMsg( + bytes calldata encodedHyMsg + ) public view - returns (HyMsg memory hyMsg, bool valid, string memory reason, uint256 index, bytes32 checkpointRoot) + returns ( + HyMsg memory hyMsg, + bool valid, + string memory reason, + uint256 index, + bytes32 checkpointRoot + ) { (hyMsg, index, checkpointRoot) = parseHyMsg(encodedHyMsg); (valid, reason) = verifyHyMsg(hyMsg); } - function verifyHyMsg(HyMsg memory hyMsg) public view returns (bool valid, string memory reason) { + function verifyHyMsg( + HyMsg memory hyMsg + ) public view returns (bool valid, string memory reason) { // TODO: fetch validators from calldata/storage address[] memory validators = _validators; if (validators.length == 0) { @@ -39,11 +49,18 @@ contract Hyperlane is IHyperlane { // We're using a fixed point number transformation with 1 decimal to deal with rounding. // we check that we have will be able to reach a quorum with the current signatures - if ((((validators.length * 10) / 3) * 2) / 10 + 1 > hyMsg.signatures.length) { + if ( + (((validators.length * 10) / 3) * 2) / 10 + 1 > + hyMsg.signatures.length + ) { return (false, "no quorum"); } // Verify signatures - (bool signaturesValid, string memory invalidReason) = verifySignatures(hyMsg.hash, hyMsg.signatures, validators); + (bool signaturesValid, string memory invalidReason) = verifySignatures( + hyMsg.hash, + hyMsg.signatures, + validators + ); if (!signaturesValid) { return (false, invalidReason); } @@ -51,17 +68,20 @@ contract Hyperlane is IHyperlane { return (true, ""); } - function verifySignatures(bytes32 hash, Signature[] memory signatures, address[] memory validators) - public - pure - returns (bool valid, string memory reason) - { + function verifySignatures( + bytes32 hash, + Signature[] memory signatures, + address[] memory validators + ) public pure returns (bool valid, string memory reason) { uint8 lastIndex = 0; // TODO: break on quorum for (uint256 i = 0; i < signatures.length; i++) { Signature memory sig = signatures[i]; - require(i == 0 || sig.validatorIndex > lastIndex, "signature indices must be ascending"); + require( + i == 0 || sig.validatorIndex > lastIndex, + "signature indices must be ascending" + ); lastIndex = sig.validatorIndex; bytes memory signature = abi.encodePacked(sig.r, sig.s, sig.v); if (!_verify(hash, signature, validators[sig.validatorIndex])) { @@ -71,7 +91,9 @@ contract Hyperlane is IHyperlane { return (true, ""); } - function parseHyMsg(bytes calldata encodedHyMsg) + function parseHyMsg( + bytes calldata encodedHyMsg + ) public pure returns (HyMsg memory hyMsg, uint256 index, bytes32 checkPointRoot) @@ -113,7 +135,13 @@ contract Hyperlane is IHyperlane { bytes32 merkleTreeHookAddress = encodedHyMsg.toBytes32(index); index += 32; - bytes32 domainHash = keccak256(abi.encodePacked(hyMsg.emitterChainId, merkleTreeHookAddress, "HYPERLANE")); + bytes32 domainHash = keccak256( + abi.encodePacked( + hyMsg.emitterChainId, + merkleTreeHookAddress, + "HYPERLANE" + ) + ); bytes32 root = encodedHyMsg.toBytes32(index); index += 32; @@ -125,12 +153,18 @@ contract Hyperlane is IHyperlane { index += 32; // Hash the configuration - hyMsg.hash = keccak256(abi.encodePacked(domainHash, root, checkpointIndex, messageId)); + hyMsg.hash = keccak256( + abi.encodePacked(domainHash, root, checkpointIndex, messageId) + ); hyMsg.payload = encodedHyMsg.slice(index, encodedHyMsg.length - index); } - function _verify(bytes32 data, bytes memory signature, address account) internal pure returns (bool) { + function _verify( + bytes32 data, + bytes memory signature, + address account + ) internal pure returns (bool) { return data.toEthSignedMessageHash().recover(signature) == account; } } diff --git a/solidity/src/Pragma.sol b/solidity/src/Pragma.sol index 0c119f7..9cfb123 100644 --- a/solidity/src/Pragma.sol +++ b/solidity/src/Pragma.sol @@ -16,7 +16,13 @@ import "./libraries/DataParser.sol"; /// @author Pragma Labs /// @custom:contact security@pragma.build /// @notice The Pragma contract. -contract Pragma is Initializable, UUPSUpgradeable, OwnableUpgradeable, IPragma, PragmaDecoder { +contract Pragma is + Initializable, + UUPSUpgradeable, + OwnableUpgradeable, + IPragma, + PragmaDecoder +{ /* STORAGE */ uint256 public validTimePeriodSeconds; uint256 public singleUpdateFeeInWei; @@ -38,21 +44,28 @@ contract Pragma is Initializable, UUPSUpgradeable, OwnableUpgradeable, IPragma, hyperlane = IHyperlane(_hyperlane); for (uint256 i = 0; i < _dataSourceEmitterChainIds.length; i++) { - _isValidDataSource[keccak256( - abi.encodePacked(_dataSourceEmitterChainIds[i], _dataSourceEmitterAddresses[i]) - )] = true; + _isValidDataSource[ + keccak256( + abi.encodePacked( + _dataSourceEmitterChainIds[i], + _dataSourceEmitterAddresses[i] + ) + ) + ] = true; } validTimePeriodSeconds = _validTimePeriodSeconds; singleUpdateFeeInWei = _singleUpdateFeeInWei; } - function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + function _authorizeUpgrade( + address newImplementation + ) internal override onlyOwner {} /// @inheritdoc IPragma function updateDataFeeds(bytes[] calldata updateData) external payable { uint256 totalNumUpdates = 0; uint256 len = updateData.length; - for (uint256 i = 0; i < len;) { + for (uint256 i = 0; i < len; ) { totalNumUpdates += updateDataInfoFromUpdate(updateData[i]); unchecked { i++; @@ -65,15 +78,22 @@ contract Pragma is Initializable, UUPSUpgradeable, OwnableUpgradeable, IPragma, } /// @inheritdoc IPragma - function getUpdateFee(bytes[] calldata updateData) external view returns (uint256 feeAmount) { + function getUpdateFee( + bytes[] calldata updateData + ) external view returns (uint256 feeAmount) { return 0; } - function getTotalFee(uint256 totalNumUpdates) private view returns (uint256 requiredFee) { + function getTotalFee( + uint256 totalNumUpdates + ) private view returns (uint256 requiredFee) { return totalNumUpdates * singleUpdateFeeInWei; } - function getSpotMedianNoOlderThan(bytes32 id, uint256 age) external view returns (SpotMedian memory data) { + function getSpotMedianNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (SpotMedian memory data) { data = spotMedianFeeds[id]; if (data.metadata.timestamp == 0) { revert ErrorsLib.DataNotFound(); @@ -84,7 +104,10 @@ contract Pragma is Initializable, UUPSUpgradeable, OwnableUpgradeable, IPragma, return data; } - function getTwapNoOlderThan(bytes32 id, uint256 age) external view returns (TWAP memory data) { + function getTwapNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (TWAP memory data) { data = twapFeeds[id]; if (data.metadata.timestamp == 0) { revert ErrorsLib.DataNotFound(); @@ -94,11 +117,10 @@ contract Pragma is Initializable, UUPSUpgradeable, OwnableUpgradeable, IPragma, } } - function getRealizedVolatilityNoOlderThan(bytes32 id, uint256 age) - external - view - returns (RealizedVolatility memory data) - { + function getRealizedVolatilityNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (RealizedVolatility memory data) { data = rvFeeds[id]; if (data.metadata.timestamp == 0) { revert ErrorsLib.DataNotFound(); @@ -108,7 +130,10 @@ contract Pragma is Initializable, UUPSUpgradeable, OwnableUpgradeable, IPragma, } } - function getOptionsNoOlderThan(bytes32 id, uint256 age) external view returns (Options memory data) { + function getOptionsNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (Options memory data) { data = optionsFeeds[id]; if (data.metadata.timestamp == 0) { revert ErrorsLib.DataNotFound(); @@ -118,7 +143,10 @@ contract Pragma is Initializable, UUPSUpgradeable, OwnableUpgradeable, IPragma, } } - function getPerpNoOlderThan(bytes32 id, uint256 age) external view returns (Perp memory data) { + function getPerpNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (Perp memory data) { data = perpFeeds[id]; if (data.metadata.timestamp == 0) { revert ErrorsLib.DataNotFound(); @@ -154,13 +182,15 @@ contract Pragma is Initializable, UUPSUpgradeable, OwnableUpgradeable, IPragma, /// Even if the getters are automatically set for the public storage variable, we need to define the getter to make it /// accessible for the interface - function getSpotMedianFeed(bytes32 feedId) external view returns (SpotMedian memory) { + function getSpotMedianFeed( + bytes32 feedId + ) external view returns (SpotMedian memory) { return spotMedianFeeds[feedId]; } function withdrawFunds(uint256 amount) external onlyOwner { require(amount <= address(this).balance, "Insufficient balance"); - (bool success,) = owner().call{value: amount}(""); + (bool success, ) = owner().call{value: amount}(""); require(success, "Transfer failed"); } diff --git a/solidity/src/PragmaDecoder.sol b/solidity/src/PragmaDecoder.sol index 78bf8ea..ed6ab91 100644 --- a/solidity/src/PragmaDecoder.sol +++ b/solidity/src/PragmaDecoder.sol @@ -29,11 +29,19 @@ abstract contract PragmaDecoder { mapping(bytes32 => Perp) public perpFeeds; // TODO: set valid data sources - function isValidDataSource(uint32 chainId, bytes32 emitterAddress) public view returns (bool) { - return _isValidDataSource[keccak256(abi.encodePacked(chainId, emitterAddress))]; + function isValidDataSource( + uint32 chainId, + bytes32 emitterAddress + ) public view returns (bool) { + return + _isValidDataSource[ + keccak256(abi.encodePacked(chainId, emitterAddress)) + ]; } - function parseAndVerifyHyMsg(bytes calldata encodedHyMsg) + function parseAndVerifyHyMsg( + bytes calldata encodedHyMsg + ) internal view returns (HyMsg memory hyMsg, uint256 index, bytes32 checkpointRoot) @@ -41,7 +49,8 @@ abstract contract PragmaDecoder { { bool valid; string memory reason; - (hyMsg, valid, reason, index, checkpointRoot) = hyperlane.parseAndVerifyHyMsg(encodedHyMsg); + (hyMsg, valid, reason, index, checkpointRoot) = hyperlane + .parseAndVerifyHyMsg(encodedHyMsg); if (!valid) revert ErrorsLib.InvalidHyperlaneSignatures(reason); } @@ -50,12 +59,17 @@ abstract contract PragmaDecoder { } } - function extractMetadataFromheader(bytes calldata updateData) internal pure returns (uint256 encodedOffset) { + function extractMetadataFromheader( + bytes calldata updateData + ) internal pure returns (uint256 encodedOffset) { unchecked { encodedOffset = 0; { - uint8 majorVersion = UnsafeCalldataBytesLib.toUint8(updateData, encodedOffset); + uint8 majorVersion = UnsafeCalldataBytesLib.toUint8( + updateData, + encodedOffset + ); encodedOffset += 1; @@ -63,7 +77,10 @@ abstract contract PragmaDecoder { revert ErrorsLib.InvalidVersion(); } - uint8 minorVersion = UnsafeCalldataBytesLib.toUint8(updateData, encodedOffset); + uint8 minorVersion = UnsafeCalldataBytesLib.toUint8( + updateData, + encodedOffset + ); encodedOffset += 1; @@ -75,7 +92,10 @@ abstract contract PragmaDecoder { // This field ensure that we can add headers in the future // without breaking the contract (future compatibility) - uint8 trailingHeaderSize = UnsafeCalldataBytesLib.toUint8(updateData, encodedOffset); + uint8 trailingHeaderSize = UnsafeCalldataBytesLib.toUint8( + updateData, + encodedOffset + ); encodedOffset += 1; // We use another encodedOffset for the trailing header and in the end add the @@ -95,13 +115,25 @@ abstract contract PragmaDecoder { } } - function extractCheckpointRootAndNumUpdates(bytes calldata updateData, uint256 encodedOffset) + function extractCheckpointRootAndNumUpdates( + bytes calldata updateData, + uint256 encodedOffset + ) internal view - returns (uint256 offset, bytes32 checkpointRoot, uint8 numUpdates, bytes calldata encoded) + returns ( + uint256 offset, + bytes32 checkpointRoot, + uint8 numUpdates, + bytes calldata encoded + ) { unchecked { - encoded = UnsafeCalldataBytesLib.slice(updateData, encodedOffset, updateData.length - encodedOffset); + encoded = UnsafeCalldataBytesLib.slice( + updateData, + encodedOffset, + updateData.length - encodedOffset + ); offset = 0; uint16 hyMsgSize = UnsafeCalldataBytesLib.toUint16(encoded, offset); @@ -110,8 +142,17 @@ abstract contract PragmaDecoder { { bytes memory encodedPayload; { - (HyMsg memory hyMsg, uint256 index, bytes32 root) = - parseAndVerifyHyMsg(UnsafeCalldataBytesLib.slice(encoded, offset, hyMsgSize)); + ( + HyMsg memory hyMsg, + uint256 index, + bytes32 root + ) = parseAndVerifyHyMsg( + UnsafeCalldataBytesLib.slice( + encoded, + offset, + hyMsgSize + ) + ); checkpointRoot = root; encodedPayload = hyMsg.payload; offset += index; @@ -124,7 +165,10 @@ abstract contract PragmaDecoder { if (payloadOffset > encodedPayload.length) { revert ErrorsLib.InvalidUpdateData(); } - numUpdates = UnsafeBytesLib.toUint8(encodedPayload, payloadOffset); + numUpdates = UnsafeBytesLib.toUint8( + encodedPayload, + payloadOffset + ); offset += encodedOffset + 1; payloadOffset += 1; } @@ -132,20 +176,30 @@ abstract contract PragmaDecoder { } } - function _isProofValid(bytes calldata encodedProof, uint256 offset, bytes32 root, bytes calldata leafData) - internal - virtual - returns (bool valid, uint256 endOffset) - { + function _isProofValid( + bytes calldata encodedProof, + uint256 offset, + bytes32 root, + bytes calldata leafData + ) internal virtual returns (bool valid, uint256 endOffset) { // TODO: The proof is ignored for now until we figure out how to get it from Hyperlane. // (valid, endOffset) = MerkleTree.isProofValid(encodedProof, offset, root, leafData); return (true, offset); } - function extractDataInfoFromUpdate(bytes calldata encoded, uint256 offset, bytes32 checkpointRoot) + function extractDataInfoFromUpdate( + bytes calldata encoded, + uint256 offset, + bytes32 checkpointRoot + ) internal - returns (uint256 endOffset, ParsedData memory parsedData, bytes32 feedId, uint64 publishTime) + returns ( + uint256 endOffset, + ParsedData memory parsedData, + bytes32 feedId, + uint64 publishTime + ) { unchecked { bytes calldata encodedUpdate; @@ -156,12 +210,22 @@ abstract contract PragmaDecoder { uint16 proofSize = UnsafeCalldataBytesLib.toUint16(encoded, offset); offset += 2; { - encodedProof = UnsafeCalldataBytesLib.slice(encoded, offset, proofSize); + encodedProof = UnsafeCalldataBytesLib.slice( + encoded, + offset, + proofSize + ); uint256 encodedUpdateIndex = offset + proofSize; - encodedUpdate = - UnsafeCalldataBytesLib.slice(encoded, encodedUpdateIndex, encoded.length - 40 - encodedUpdateIndex); // 32 bytes for feedId, 8 bytes for publishTime - fulldataFeed = - UnsafeCalldataBytesLib.slice(encoded, encodedUpdateIndex, encoded.length - encodedUpdateIndex); + encodedUpdate = UnsafeCalldataBytesLib.slice( + encoded, + encodedUpdateIndex, + encoded.length - 40 - encodedUpdateIndex + ); // 32 bytes for feedId, 8 bytes for publishTime + fulldataFeed = UnsafeCalldataBytesLib.slice( + encoded, + encodedUpdateIndex, + encoded.length - encodedUpdateIndex + ); } // bool valid; // (valid, offset) = _isProofValid(encoded, offset, checkpointRoot, encodedUpdate); @@ -172,20 +236,30 @@ abstract contract PragmaDecoder { } } - function parseDataFeed(bytes calldata encodedDataFeed) + function parseDataFeed( + bytes calldata encodedDataFeed + ) private pure - returns (ParsedData memory parsedData, bytes32 feedId, uint64 publishTime) + returns ( + ParsedData memory parsedData, + bytes32 feedId, + uint64 publishTime + ) { parsedData = DataParser.parse(encodedDataFeed); // Assuming feedId and publishTime are appended at the end of encodedDataFeed uint256 offset = encodedDataFeed.length - 40; // 32 bytes for feedId, 8 bytes for publishTime - feedId = bytes32(UnsafeCalldataBytesLib.toUint256(encodedDataFeed, offset)); + feedId = bytes32( + UnsafeCalldataBytesLib.toUint256(encodedDataFeed, offset) + ); offset += 32; publishTime = UnsafeBytesLib.toUint64(encodedDataFeed, offset); } - function updateDataInfoFromUpdate(bytes calldata updateData) internal returns (uint8 numUpdates) { + function updateDataInfoFromUpdate( + bytes calldata updateData + ) internal returns (uint8 numUpdates) { // Extract header metadata uint256 encodedOffset = extractMetadataFromheader(updateData); @@ -194,15 +268,32 @@ abstract contract PragmaDecoder { bytes32 checkpointRoot; bytes calldata encoded; - (offset, checkpointRoot, numUpdates, encoded) = extractCheckpointRootAndNumUpdates(updateData, encodedOffset); + ( + offset, + checkpointRoot, + numUpdates, + encoded + ) = extractCheckpointRootAndNumUpdates(updateData, encodedOffset); unchecked { for (uint256 i = 0; i < numUpdates; i++) { ParsedData memory parsedData; bytes32 feedId; uint64 publishTime; - (offset, parsedData, feedId, publishTime) = - extractDataInfoFromUpdate(updateData, offset, checkpointRoot); - updateLatestDataInfoIfNecessary(feedId, parsedData, publishTime); + ( + offset, + parsedData, + feedId, + publishTime + ) = extractDataInfoFromUpdate( + updateData, + offset, + checkpointRoot + ); + updateLatestDataInfoIfNecessary( + feedId, + parsedData, + publishTime + ); } } // We check that the offset is at the end of the encoded data. @@ -210,13 +301,19 @@ abstract contract PragmaDecoder { if (offset != updateData.length) revert ErrorsLib.InvalidUpdateData(); } - function updateLatestDataInfoIfNecessary(bytes32 feedId, ParsedData memory parsedData, uint64 publishTime) - internal - { + function updateLatestDataInfoIfNecessary( + bytes32 feedId, + ParsedData memory parsedData, + uint64 publishTime + ) internal { if (parsedData.dataType == FeedType.SpotMedian) { if (publishTime > spotMedianFeeds[feedId].metadata.timestamp) { spotMedianFeeds[feedId] = parsedData.spot; - emit EventsLib.SpotMedianUpdate(feedId, publishTime, parsedData.spot); + emit EventsLib.SpotMedianUpdate( + feedId, + publishTime, + parsedData.spot + ); } } else if (parsedData.dataType == FeedType.Twap) { if (publishTime > twapFeeds[feedId].metadata.timestamp) { @@ -226,12 +323,20 @@ abstract contract PragmaDecoder { } else if (parsedData.dataType == FeedType.RealizedVolatility) { if (publishTime > rvFeeds[feedId].metadata.timestamp) { rvFeeds[feedId] = parsedData.rv; - emit EventsLib.RealizedVolatilityUpdate(feedId, publishTime, parsedData.rv); + emit EventsLib.RealizedVolatilityUpdate( + feedId, + publishTime, + parsedData.rv + ); } } else if (parsedData.dataType == FeedType.Options) { if (publishTime > optionsFeeds[feedId].metadata.timestamp) { optionsFeeds[feedId] = parsedData.options; - emit EventsLib.OptionsUpdate(feedId, publishTime, parsedData.options); + emit EventsLib.OptionsUpdate( + feedId, + publishTime, + parsedData.options + ); } } else if (parsedData.dataType == FeedType.Perpetuals) { if (publishTime > perpFeeds[feedId].metadata.timestamp) { diff --git a/solidity/src/interfaces/IHyperlane.sol b/solidity/src/interfaces/IHyperlane.sol index e5ffe11..f40109f 100644 --- a/solidity/src/interfaces/IHyperlane.sol +++ b/solidity/src/interfaces/IHyperlane.sol @@ -14,16 +14,26 @@ interface IHyperlane { /// @return hyMsg The parsed Hyperlane message. /// @return valid Whether the message is valid. /// @return reason The reason the message is invalid. - function parseAndVerifyHyMsg(bytes calldata encodedHyMsg) + function parseAndVerifyHyMsg( + bytes calldata encodedHyMsg + ) external view - returns (HyMsg memory hyMsg, bool valid, string memory reason, uint256 index, bytes32 checkpointRoot); + returns ( + HyMsg memory hyMsg, + bool valid, + string memory reason, + uint256 index, + bytes32 checkpointRoot + ); /// @notice Parses an Hyperlane message. /// @dev message should be encoded following the specs (TODO: add docs) /// @param encodedHyMsg The encoded Hyperlane message. /// @return hyMsg The parsed Hyperlane message. - function parseHyMsg(bytes calldata encodedHyMsg) + function parseHyMsg( + bytes calldata encodedHyMsg + ) external pure returns (HyMsg memory hyMsg, uint256 index, bytes32 checkpointRoot); diff --git a/solidity/src/interfaces/IPragma.sol b/solidity/src/interfaces/IPragma.sol index fea9d4b..131abb0 100644 --- a/solidity/src/interfaces/IPragma.sol +++ b/solidity/src/interfaces/IPragma.sol @@ -20,39 +20,53 @@ interface IPragma { /// @notice Returns the required fee to update an array of price updates. /// @param updateData Array of price update data. /// @return feeAmount The required fee in Wei. - function getUpdateFee(bytes[] calldata updateData) external view returns (uint256 feeAmount); + function getUpdateFee( + bytes[] calldata updateData + ) external view returns (uint256 feeAmount); /// @notice Fetches the latest spot median price that is no older than a specified age. /// @param id The unique identifier of the data feed. /// @param age The maximum allowed age (in seconds) for the price data. /// @return The latest valid SpotMedian data, or a revert if no valid data is available within the specified age. - function getSpotMedianNoOlderThan(bytes32 id, uint256 age) external view returns (SpotMedian memory); + function getSpotMedianNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (SpotMedian memory); /// @notice Fetches the latest TWAP (Time-Weighted Average Price) that is no older than a specified age. /// @param id The unique identifier of the TWAP data feed. /// @param age The maximum allowed age (in seconds) for the TWAP data. /// @return The latest valid TWAP data, or a revert if no valid data is available within the specified age. - function getTwapNoOlderThan(bytes32 id, uint256 age) external view returns (TWAP memory); + function getTwapNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (TWAP memory); /// @notice Fetches the latest realized volatility that is no older than a specified age. /// @param id The unique identifier of the realized volatility data feed. /// @param age The maximum allowed age (in seconds) for the volatility data. /// @return The latest valid RealizedVolatility data, or a revert if no valid data is available within the specified age. - function getRealizedVolatilityNoOlderThan(bytes32 id, uint256 age) - external - view - returns (RealizedVolatility memory); + function getRealizedVolatilityNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (RealizedVolatility memory); /// @notice Fetches the latest options data that is no older than a specified age. /// @param id The unique identifier of the options data feed. /// @param age The maximum allowed age (in seconds) for the options data. /// @return The latest valid Options data, or a revert if no valid data is available within the specified age. - function getOptionsNoOlderThan(bytes32 id, uint256 age) external view returns (Options memory); + function getOptionsNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (Options memory); /// @notice Fetches the latest perpetuals data that is no older than a specified age. /// @param id The unique identifier of the perpetuals data feed. /// @param age The maximum allowed age (in seconds) for the perpetuals data. /// @return The latest valid Perp data, or a revert if no valid data is available within the specified age. - function getPerpNoOlderThan(bytes32 id, uint256 age) external view returns (Perp memory); + function getPerpNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (Perp memory); /// @notice Checks if a data feed exists. /// @param id The data feed ID. @@ -62,5 +76,7 @@ interface IPragma { /// @notice Getter accesssible through the interface /// @param feedId The data feed id. /// @return SpotMedian the entry associated to the spot feed id. - function getSpotMedianFeed(bytes32 feedId) external view returns (SpotMedian memory); + function getSpotMedianFeed( + bytes32 feedId + ) external view returns (SpotMedian memory); } diff --git a/solidity/src/interfaces/PragmaStructs.sol b/solidity/src/interfaces/PragmaStructs.sol index 35041de..04dc156 100644 --- a/solidity/src/interfaces/PragmaStructs.sol +++ b/solidity/src/interfaces/PragmaStructs.sol @@ -113,67 +113,85 @@ enum FeedType { library StructsInitializers { function initializeParsedData() internal pure returns (ParsedData memory) { - return ParsedData({ - dataType: FeedType.SpotMedian, - spot: initializeSpotMedian(), - twap: initializeTwap(), - rv: initializeRV(), - options: initializeOptions(), - perp: initializePerpetuals() - }); + return + ParsedData({ + dataType: FeedType.SpotMedian, + spot: initializeSpotMedian(), + twap: initializeTwap(), + rv: initializeRV(), + options: initializeOptions(), + perp: initializePerpetuals() + }); } function initializeMetadata() internal pure returns (Metadata memory) { - return Metadata({feedId: 0, timestamp: 0, numberOfSources: 0, decimals: 0}); + return + Metadata({ + feedId: 0, + timestamp: 0, + numberOfSources: 0, + decimals: 0 + }); } function initializeSpotMedian() internal pure returns (SpotMedian memory) { - return SpotMedian({metadata: initializeMetadata(), price: 0, volume: 0}); + return + SpotMedian({metadata: initializeMetadata(), price: 0, volume: 0}); } function initializeTwap() internal pure returns (TWAP memory) { - return TWAP({ - metadata: initializeMetadata(), - twapPrice: 0, - timePeriod: 0, - startPrice: 0, - endPrice: 0, - totalVolume: 0, - numberOfDataPoints: 0 - }); + return + TWAP({ + metadata: initializeMetadata(), + twapPrice: 0, + timePeriod: 0, + startPrice: 0, + endPrice: 0, + totalVolume: 0, + numberOfDataPoints: 0 + }); } function initializeRV() internal pure returns (RealizedVolatility memory) { - return RealizedVolatility({ - metadata: initializeMetadata(), - volatility: 0, - timePeriod: 0, - startPrice: 0, - endPrice: 0, - highPrice: 0, - lowPrice: 0, - numberOfDataPoints: 0 - }); + return + RealizedVolatility({ + metadata: initializeMetadata(), + volatility: 0, + timePeriod: 0, + startPrice: 0, + endPrice: 0, + highPrice: 0, + lowPrice: 0, + numberOfDataPoints: 0 + }); } function initializeOptions() internal pure returns (Options memory) { - return Options({ - metadata: initializeMetadata(), - strikePrice: 0, - impliedVolatility: 0, - timeToExpiry: 0, - isCall: false, - underlyingPrice: 0, - optionPrice: 0, - delta: 0, - gamma: 0, - vega: 0, - theta: 0, - rho: 0 - }); + return + Options({ + metadata: initializeMetadata(), + strikePrice: 0, + impliedVolatility: 0, + timeToExpiry: 0, + isCall: false, + underlyingPrice: 0, + optionPrice: 0, + delta: 0, + gamma: 0, + vega: 0, + theta: 0, + rho: 0 + }); } function initializePerpetuals() internal pure returns (Perp memory) { - return Perp({metadata: initializeMetadata(), markPrice: 0, fundingRate: 0, openInterest: 0, volume: 0}); + return + Perp({ + metadata: initializeMetadata(), + markPrice: 0, + fundingRate: 0, + openInterest: 0, + volume: 0 + }); } } diff --git a/solidity/src/libraries/BytesLib.sol b/solidity/src/libraries/BytesLib.sol index 7de63b0..d4bca11 100644 --- a/solidity/src/libraries/BytesLib.sol +++ b/solidity/src/libraries/BytesLib.sol @@ -9,7 +9,10 @@ pragma solidity 0.8.28; library BytesLib { - function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) { + function concat( + bytes memory _preBytes, + bytes memory _postBytes + ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { @@ -57,10 +60,14 @@ library BytesLib { // length of the arrays. end := add(mc, length) - for { let cc := add(_postBytes, 0x20) } lt(mc, end) { + for { + let cc := add(_postBytes, 0x20) + } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) - } { mstore(mc, mload(cc)) } + } { + mstore(mc, mload(cc)) + } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the @@ -79,7 +86,10 @@ library BytesLib { return tempBytes; } - function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { + function concatStorage( + bytes storage _preBytes, + bytes memory _postBytes + ) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot @@ -92,7 +102,10 @@ library BytesLib { // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. - let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) + let slength := div( + and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), + 2 + ) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array @@ -157,7 +170,10 @@ library BytesLib { sstore( sc, add( - and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00), + and( + fslot, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 + ), and(mload(mc), mask) ) ) @@ -168,7 +184,9 @@ library BytesLib { } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) - } { sstore(sc, mload(mc)) } + } { + sstore(sc, mload(mc)) + } mask := exp(0x100, sub(mc, end)) @@ -200,7 +218,9 @@ library BytesLib { } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) - } { sstore(sc, mload(mc)) } + } { + sstore(sc, mload(mc)) + } mask := exp(0x100, sub(mc, end)) @@ -209,7 +229,11 @@ library BytesLib { } } - function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { + function slice( + bytes memory _bytes, + uint256 _start, + uint256 _length + ) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); @@ -236,17 +260,28 @@ library BytesLib { // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. - let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let mc := add( + add(tempBytes, lengthmod), + mul(0x20, iszero(lengthmod)) + ) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. - let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) + let cc := add( + add( + add(_bytes, lengthmod), + mul(0x20, iszero(lengthmod)) + ), + _start + ) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) - } { mstore(mc, mload(cc)) } + } { + mstore(mc, mload(cc)) + } mstore(tempBytes, _length) @@ -268,18 +303,27 @@ library BytesLib { return tempBytes; } - function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { + function toAddress( + bytes memory _bytes, + uint256 _start + ) internal pure returns (address) { require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); address tempAddress; assembly { - tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) + tempAddress := div( + mload(add(add(_bytes, 0x20), _start)), + 0x1000000000000000000000000 + ) } return tempAddress; } - function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { + function toUint8( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint8) { require(_bytes.length >= _start + 1, "toUint8_outOfBounds"); uint8 tempUint; @@ -290,7 +334,10 @@ library BytesLib { return tempUint; } - function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { + function toUint16( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint16) { require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; @@ -301,7 +348,10 @@ library BytesLib { return tempUint; } - function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { + function toUint32( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint32) { require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); uint32 tempUint; @@ -312,7 +362,10 @@ library BytesLib { return tempUint; } - function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { + function toUint64( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint64) { require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); uint64 tempUint; @@ -323,7 +376,10 @@ library BytesLib { return tempUint; } - function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { + function toUint96( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint96) { require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); uint96 tempUint; @@ -334,7 +390,10 @@ library BytesLib { return tempUint; } - function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { + function toUint128( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint128) { require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); uint128 tempUint; @@ -345,7 +404,10 @@ library BytesLib { return tempUint; } - function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { + function toUint256( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint256) { require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; @@ -356,7 +418,10 @@ library BytesLib { return tempUint; } - function toInt256(bytes memory _bytes, uint256 _start) internal pure returns (int256) { + function toInt256( + bytes memory _bytes, + uint256 _start + ) internal pure returns (int256) { require(_bytes.length >= _start + 32, "toInt256_outOfBounds"); int256 tempInt; @@ -367,7 +432,10 @@ library BytesLib { return tempInt; } - function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { + function toBytes32( + bytes memory _bytes, + uint256 _start + ) internal pure returns (bytes32) { require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); bytes32 tempBytes32; @@ -378,7 +446,10 @@ library BytesLib { return tempBytes32; } - function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { + function equal( + bytes memory _preBytes, + bytes memory _postBytes + ) internal pure returns (bool) { bool success = true; assembly { @@ -396,8 +467,9 @@ library BytesLib { let mc := add(_preBytes, 0x20) let end := add(mc, length) - for { let cc := add(_postBytes, 0x20) } - // the next line is the loop condition: + for { + let cc := add(_postBytes, 0x20) + } // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) @@ -420,14 +492,20 @@ library BytesLib { return success; } - function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) { + function equalStorage( + bytes storage _preBytes, + bytes memory _postBytes + ) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes.slot) // Decode the length of the stored array like in concatStorage(). - let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) + let slength := div( + and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), + 2 + ) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal diff --git a/solidity/src/libraries/DataParser.sol b/solidity/src/libraries/DataParser.sol index cb6f20b..76f3c24 100644 --- a/solidity/src/libraries/DataParser.sol +++ b/solidity/src/libraries/DataParser.sol @@ -8,12 +8,15 @@ import "./ErrorsLib.sol"; library DataParser { using BytesLib for bytes; - function parse(bytes memory data) internal pure returns (ParsedData memory) { + function parse( + bytes memory data + ) internal pure returns (ParsedData memory) { uint8 offset = 2; // type of feed after the asset class uint8 rawDataType = data.toUint8(offset); FeedType dataType = safeCastToFeedType(rawDataType); - ParsedData memory parsedData = StructsInitializers.initializeParsedData(); + ParsedData memory parsedData = StructsInitializers + .initializeParsedData(); parsedData.dataType = dataType; if (dataType == FeedType.SpotMedian) { parsedData.spot = parseSpotData(data); @@ -32,7 +35,9 @@ library DataParser { return parsedData; } - function safeCastToFeedType(uint8 rawDataType) internal pure returns (FeedType) { + function safeCastToFeedType( + uint8 rawDataType + ) internal pure returns (FeedType) { if (rawDataType <= uint8(type(FeedType).max)) { return FeedType(rawDataType); } else { @@ -40,7 +45,10 @@ library DataParser { } } - function parseMetadata(bytes memory data, uint256 startIndex) internal pure returns (Metadata memory, uint256) { + function parseMetadata( + bytes memory data, + uint256 startIndex + ) internal pure returns (Metadata memory, uint256) { Metadata memory metadata = StructsInitializers.initializeMetadata(); uint256 index = startIndex; @@ -59,7 +67,9 @@ library DataParser { return (metadata, index); } - function parseSpotData(bytes memory data) internal pure returns (SpotMedian memory) { + function parseSpotData( + bytes memory data + ) internal pure returns (SpotMedian memory) { SpotMedian memory entry = StructsInitializers.initializeSpotMedian(); uint256 index = 0; @@ -74,7 +84,9 @@ library DataParser { return entry; } - function parseTWAPData(bytes memory data) internal pure returns (TWAP memory) { + function parseTWAPData( + bytes memory data + ) internal pure returns (TWAP memory) { TWAP memory entry = StructsInitializers.initializeTwap(); uint256 index = 0; @@ -100,7 +112,9 @@ library DataParser { return entry; } - function parseRealizedVolatilityData(bytes memory data) internal pure returns (RealizedVolatility memory) { + function parseRealizedVolatilityData( + bytes memory data + ) internal pure returns (RealizedVolatility memory) { RealizedVolatility memory entry = StructsInitializers.initializeRV(); uint256 index = 0; @@ -129,7 +143,9 @@ library DataParser { return entry; } - function parseOptionsData(bytes memory data) internal pure returns (Options memory) { + function parseOptionsData( + bytes memory data + ) internal pure returns (Options memory) { Options memory entry = StructsInitializers.initializeOptions(); uint256 index = 0; @@ -170,7 +186,9 @@ library DataParser { return entry; } - function parsePerpData(bytes memory data) internal pure returns (Perp memory) { + function parsePerpData( + bytes memory data + ) internal pure returns (Perp memory) { Perp memory entry = StructsInitializers.initializePerpetuals(); uint256 index = 0; diff --git a/solidity/src/libraries/EventsLib.sol b/solidity/src/libraries/EventsLib.sol index 8c66eb0..bd1cbfb 100644 --- a/solidity/src/libraries/EventsLib.sol +++ b/solidity/src/libraries/EventsLib.sol @@ -12,13 +12,22 @@ library EventsLib { /// @param feedId Pragma Feed ID. /// @param publishTime Unix timestamp of the update. /// @param value New value of the data feed. - event DataFeedUpdate(bytes32 indexed feedId, uint64 publishTime, uint32 numSourcesAggregated, int64 value); + event DataFeedUpdate( + bytes32 indexed feedId, + uint64 publishTime, + uint32 numSourcesAggregated, + int64 value + ); /// @dev Emitted when a Spot Median feed with `feedId` has received a fresh update. /// @param feedId Pragma Feed ID. /// @param publishTime Unix timestamp of the update. /// @param spotMedian New Spot Median data. - event SpotMedianUpdate(bytes32 indexed feedId, uint64 publishTime, SpotMedian spotMedian); + event SpotMedianUpdate( + bytes32 indexed feedId, + uint64 publishTime, + SpotMedian spotMedian + ); /// @dev Emitted when a TWAP feed with `feedId` has received a fresh update. /// @param feedId Pragma Feed ID. @@ -30,13 +39,21 @@ library EventsLib { /// @param feedId Pragma Feed ID. /// @param publishTime Unix timestamp of the update. /// @param realizedVolatility New Realized Volatility data. - event RealizedVolatilityUpdate(bytes32 indexed feedId, uint64 publishTime, RealizedVolatility realizedVolatility); + event RealizedVolatilityUpdate( + bytes32 indexed feedId, + uint64 publishTime, + RealizedVolatility realizedVolatility + ); /// @dev Emitted when an Options feed with `feedId` has received a fresh update. /// @param feedId Pragma Feed ID. /// @param publishTime Unix timestamp of the update. /// @param options New Options data. - event OptionsUpdate(bytes32 indexed feedId, uint64 publishTime, Options options); + event OptionsUpdate( + bytes32 indexed feedId, + uint64 publishTime, + Options options + ); /// @dev Emitted when a Perp feed with `feedId` has received a fresh update. /// @param feedId Pragma Feed ID. diff --git a/solidity/src/libraries/MerkleTree.sol b/solidity/src/libraries/MerkleTree.sol index 2e6db5c..7ae69c6 100644 --- a/solidity/src/libraries/MerkleTree.sol +++ b/solidity/src/libraries/MerkleTree.sol @@ -25,7 +25,10 @@ library MerkleTree { return hash(abi.encodePacked(MERKLE_LEAF_PREFIX, data)); } - function nodeHash(bytes32 childA, bytes32 childB) internal pure returns (bytes32) { + function nodeHash( + bytes32 childA, + bytes32 childB + ) internal pure returns (bytes32) { if (childA > childB) { (childA, childB) = (childB, childA); } @@ -40,21 +43,30 @@ library MerkleTree { /// `proofOffset` parameters. It is the caller's responsibility to ensure /// that the `encodedProof` is long enough to contain the proof and the /// `proofOffset` is not out of bound. - function isProofValid(bytes calldata encodedProof, uint256 offset, bytes32 root, bytes calldata leafData) - internal - pure - returns (bool valid, uint256 endOffset) - { + function isProofValid( + bytes calldata encodedProof, + uint256 offset, + bytes32 root, + bytes calldata leafData + ) internal pure returns (bool valid, uint256 endOffset) { unchecked { bytes32 currentDigest = MerkleTree.leafHash(leafData); uint256 proofOffset = 0; - uint16 proofSizeArray = UnsafeCalldataBytesLib.toUint16(encodedProof, proofOffset); + uint16 proofSizeArray = UnsafeCalldataBytesLib.toUint16( + encodedProof, + proofOffset + ); proofOffset += 2; for (uint256 i = 0; i < proofSizeArray; i++) { - bytes32 siblingDigest = bytes32(UnsafeCalldataBytesLib.toBytes32(encodedProof, proofOffset)); + bytes32 siblingDigest = bytes32( + UnsafeCalldataBytesLib.toBytes32(encodedProof, proofOffset) + ); proofOffset += 32; // TO CHECK - currentDigest = MerkleTree.nodeHash(currentDigest, siblingDigest); + currentDigest = MerkleTree.nodeHash( + currentDigest, + siblingDigest + ); } valid = currentDigest == root; endOffset = offset + proofOffset; @@ -69,11 +81,10 @@ library MerkleTree { /// messages as leafs (in the same given order) and returns the root digest /// and the proofs for each message. If the number of messages is not a power /// of 2, the tree is padded with empty messages. - function constructProofs(bytes[] memory messages, uint8 depth) - internal - pure - returns (bytes32 root, bytes[] memory proofs) - { + function constructProofs( + bytes[] memory messages, + uint8 depth + ) internal pure returns (bytes32 root, bytes[] memory proofs) { require((1 << depth) >= messages.length, "depth too small"); bytes32[] memory tree = new bytes32[]((1 << (depth + 1))); diff --git a/solidity/src/libraries/UnsafeBytesLib.sol b/solidity/src/libraries/UnsafeBytesLib.sol index ea19518..b36138e 100644 --- a/solidity/src/libraries/UnsafeBytesLib.sol +++ b/solidity/src/libraries/UnsafeBytesLib.sol @@ -12,7 +12,10 @@ pragma solidity 0.8.28; library UnsafeBytesLib { - function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) { + function concat( + bytes memory _preBytes, + bytes memory _postBytes + ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { @@ -60,10 +63,14 @@ library UnsafeBytesLib { // length of the arrays. end := add(mc, length) - for { let cc := add(_postBytes, 0x20) } lt(mc, end) { + for { + let cc := add(_postBytes, 0x20) + } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) - } { mstore(mc, mload(cc)) } + } { + mstore(mc, mload(cc)) + } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the @@ -82,7 +89,10 @@ library UnsafeBytesLib { return tempBytes; } - function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { + function concatStorage( + bytes storage _preBytes, + bytes memory _postBytes + ) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot @@ -95,7 +105,10 @@ library UnsafeBytesLib { // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. - let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) + let slength := div( + and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), + 2 + ) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array @@ -160,7 +173,10 @@ library UnsafeBytesLib { sstore( sc, add( - and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00), + and( + fslot, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 + ), and(mload(mc), mask) ) ) @@ -171,7 +187,9 @@ library UnsafeBytesLib { } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) - } { sstore(sc, mload(mc)) } + } { + sstore(sc, mload(mc)) + } mask := exp(0x100, sub(mc, end)) @@ -203,7 +221,9 @@ library UnsafeBytesLib { } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) - } { sstore(sc, mload(mc)) } + } { + sstore(sc, mload(mc)) + } mask := exp(0x100, sub(mc, end)) @@ -212,7 +232,11 @@ library UnsafeBytesLib { } } - function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) { + function slice( + bytes memory _bytes, + uint256 _start, + uint256 _length + ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { @@ -236,17 +260,28 @@ library UnsafeBytesLib { // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. - let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let mc := add( + add(tempBytes, lengthmod), + mul(0x20, iszero(lengthmod)) + ) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. - let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) + let cc := add( + add( + add(_bytes, lengthmod), + mul(0x20, iszero(lengthmod)) + ), + _start + ) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) - } { mstore(mc, mload(cc)) } + } { + mstore(mc, mload(cc)) + } mstore(tempBytes, _length) @@ -268,17 +303,26 @@ library UnsafeBytesLib { return tempBytes; } - function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { + function toAddress( + bytes memory _bytes, + uint256 _start + ) internal pure returns (address) { address tempAddress; assembly { - tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) + tempAddress := div( + mload(add(add(_bytes, 0x20), _start)), + 0x1000000000000000000000000 + ) } return tempAddress; } - function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { + function toUint8( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint8) { uint8 tempUint; assembly { @@ -288,7 +332,10 @@ library UnsafeBytesLib { return tempUint; } - function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { + function toUint16( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint16) { uint16 tempUint; assembly { @@ -298,7 +345,10 @@ library UnsafeBytesLib { return tempUint; } - function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { + function toUint32( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint32) { uint32 tempUint; assembly { @@ -308,7 +358,10 @@ library UnsafeBytesLib { return tempUint; } - function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { + function toUint64( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint64) { uint64 tempUint; assembly { @@ -318,7 +371,10 @@ library UnsafeBytesLib { return tempUint; } - function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { + function toUint96( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint96) { uint96 tempUint; assembly { @@ -328,7 +384,10 @@ library UnsafeBytesLib { return tempUint; } - function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { + function toUint128( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint128) { uint128 tempUint; assembly { @@ -338,7 +397,10 @@ library UnsafeBytesLib { return tempUint; } - function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { + function toUint256( + bytes memory _bytes, + uint256 _start + ) internal pure returns (uint256) { uint256 tempUint; assembly { @@ -348,7 +410,10 @@ library UnsafeBytesLib { return tempUint; } - function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { + function toBytes32( + bytes memory _bytes, + uint256 _start + ) internal pure returns (bytes32) { bytes32 tempBytes32; assembly { @@ -358,7 +423,10 @@ library UnsafeBytesLib { return tempBytes32; } - function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { + function equal( + bytes memory _preBytes, + bytes memory _postBytes + ) internal pure returns (bool) { bool success = true; assembly { @@ -376,8 +444,9 @@ library UnsafeBytesLib { let mc := add(_preBytes, 0x20) let end := add(mc, length) - for { let cc := add(_postBytes, 0x20) } - // the next line is the loop condition: + for { + let cc := add(_postBytes, 0x20) + } // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) @@ -400,14 +469,20 @@ library UnsafeBytesLib { return success; } - function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) { + function equalStorage( + bytes storage _preBytes, + bytes memory _postBytes + ) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes.slot) // Decode the length of the stored array like in concatStorage(). - let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) + let slength := div( + and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), + 2 + ) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal diff --git a/solidity/src/libraries/UnsafeCalldataBytesLib.sol b/solidity/src/libraries/UnsafeCalldataBytesLib.sol index 3ffd6bb..7c06569 100644 --- a/solidity/src/libraries/UnsafeCalldataBytesLib.sol +++ b/solidity/src/libraries/UnsafeCalldataBytesLib.sol @@ -12,15 +12,25 @@ pragma solidity 0.8.28; library UnsafeCalldataBytesLib { - function slice(bytes calldata _bytes, uint256 _start, uint256 _length) internal pure returns (bytes calldata) { + function slice( + bytes calldata _bytes, + uint256 _start, + uint256 _length + ) internal pure returns (bytes calldata) { return _bytes[_start:_start + _length]; } - function sliceFrom(bytes calldata _bytes, uint256 _start) internal pure returns (bytes calldata) { + function sliceFrom( + bytes calldata _bytes, + uint256 _start + ) internal pure returns (bytes calldata) { return _bytes[_start:_bytes.length]; } - function toAddress(bytes calldata _bytes, uint256 _start) internal pure returns (address) { + function toAddress( + bytes calldata _bytes, + uint256 _start + ) internal pure returns (address) { address tempAddress; assembly { @@ -30,7 +40,10 @@ library UnsafeCalldataBytesLib { return tempAddress; } - function toUint8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) { + function toUint8( + bytes calldata _bytes, + uint256 _start + ) internal pure returns (uint8) { uint8 tempUint; assembly { @@ -40,7 +53,10 @@ library UnsafeCalldataBytesLib { return tempUint; } - function toUint16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) { + function toUint16( + bytes calldata _bytes, + uint256 _start + ) internal pure returns (uint16) { uint16 tempUint; assembly { @@ -50,7 +66,10 @@ library UnsafeCalldataBytesLib { return tempUint; } - function toUint32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) { + function toUint32( + bytes calldata _bytes, + uint256 _start + ) internal pure returns (uint32) { uint32 tempUint; assembly { @@ -60,7 +79,10 @@ library UnsafeCalldataBytesLib { return tempUint; } - function toUint64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) { + function toUint64( + bytes calldata _bytes, + uint256 _start + ) internal pure returns (uint64) { uint64 tempUint; assembly { @@ -70,7 +92,10 @@ library UnsafeCalldataBytesLib { return tempUint; } - function toUint96(bytes calldata _bytes, uint256 _start) internal pure returns (uint96) { + function toUint96( + bytes calldata _bytes, + uint256 _start + ) internal pure returns (uint96) { uint96 tempUint; assembly { @@ -80,7 +105,10 @@ library UnsafeCalldataBytesLib { return tempUint; } - function toUint128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) { + function toUint128( + bytes calldata _bytes, + uint256 _start + ) internal pure returns (uint128) { uint128 tempUint; assembly { @@ -90,7 +118,10 @@ library UnsafeCalldataBytesLib { return tempUint; } - function toUint256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) { + function toUint256( + bytes calldata _bytes, + uint256 _start + ) internal pure returns (uint256) { uint256 tempUint; assembly { @@ -100,7 +131,10 @@ library UnsafeCalldataBytesLib { return tempUint; } - function toBytes32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) { + function toBytes32( + bytes calldata _bytes, + uint256 _start + ) internal pure returns (bytes32) { bytes32 tempBytes32; assembly { diff --git a/solidity/src/migrations/Chainlink.sol b/solidity/src/migrations/Chainlink.sol index 1f2076b..b3918fe 100644 --- a/solidity/src/migrations/Chainlink.sol +++ b/solidity/src/migrations/Chainlink.sol @@ -67,22 +67,48 @@ contract PragmaAggregatorV3 { return latestTimestamp(); } - function getRoundData(uint80 _roundId) + function getRoundData( + uint80 _roundId + ) external view - returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) { SpotMedian memory price = pragmaInterface.getSpotMedianFeed(feedId); - return (_roundId, int256(price.price), price.metadata.timestamp, price.metadata.timestamp, _roundId); + return ( + _roundId, + int256(price.price), + price.metadata.timestamp, + price.metadata.timestamp, + _roundId + ); } function latestRoundData() external view - returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) { SpotMedian memory price = pragmaInterface.getSpotMedianFeed(feedId); roundId = uint80(price.metadata.timestamp); - return (roundId, int256(price.price), price.metadata.timestamp, price.metadata.timestamp, roundId); + return ( + roundId, + int256(price.price), + price.metadata.timestamp, + price.metadata.timestamp, + roundId + ); } } diff --git a/solidity/test/PragmaDecoder.t.sol b/solidity/test/PragmaDecoder.t.sol index 3a67a8f..2e6e3b0 100644 --- a/solidity/test/PragmaDecoder.t.sol +++ b/solidity/test/PragmaDecoder.t.sol @@ -26,7 +26,9 @@ contract PragmaHarnessTest is Test { function _setUp(FeedType dataType) public { // Default setup with a specific data type, e.g., FeedType.SpotMedian - pragmaHarness = PragmaHarness(TestUtils.configurePragmaContract(dataType)); + pragmaHarness = PragmaHarness( + TestUtils.configurePragmaContract(dataType) + ); } function setupRaw() public { @@ -36,9 +38,11 @@ contract PragmaHarnessTest is Test { function testUpdateRawFeed() public { setupRaw(); // encoded update - bytes memory encodedUpdate = - hex"010000017003010002c8960ad17eddf3ec435aea12d031c6a6298e5c8e786550c1238fb423a4f5661455ef5cac013d6cc34a4ce472ba2031cb2ae84dfd35d0ecd0b51b4d437dc0b81b0003c638000000000000000000611a3d0060240f2bccef7e64f920eec05c5bfffbc48c6ceaa4efca8748772b60cbafc30536953cdd0dd5b8e24428e4fb6eab5c143daba15f62b24606e50d822508faefff8c9ddbc7988bf343935dd1f19da8e03cccc7e63ae3cccadc04a64c9e6b79750003c638e03cccfc78838eafd29d5868d9e2333c684d10828874c31b7dbc01869ec98c2001006b000000000000000000004c5553442f555344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c5553442f5553440000000000000000"; - uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate(encodedUpdate); + bytes + memory encodedUpdate = hex"010000017003010002c8960ad17eddf3ec435aea12d031c6a6298e5c8e786550c1238fb423a4f5661455ef5cac013d6cc34a4ce472ba2031cb2ae84dfd35d0ecd0b51b4d437dc0b81b0003c638000000000000000000611a3d0060240f2bccef7e64f920eec05c5bfffbc48c6ceaa4efca8748772b60cbafc30536953cdd0dd5b8e24428e4fb6eab5c143daba15f62b24606e50d822508faefff8c9ddbc7988bf343935dd1f19da8e03cccc7e63ae3cccadc04a64c9e6b79750003c638e03cccfc78838eafd29d5868d9e2333c684d10828874c31b7dbc01869ec98c2001006b000000000000000000004c5553442f555344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c5553442f5553440000000000000000"; + uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate( + encodedUpdate + ); assertEq(numUpdates, 1, "Number of updates should be 1"); } @@ -54,15 +58,30 @@ contract PragmaHarnessTest is Test { ) ); - bytes memory encodedUpdate = TestUtils.createEncodedUpdate(FeedType.SpotMedian, feedId); - uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate(encodedUpdate); + bytes memory encodedUpdate = TestUtils.createEncodedUpdate( + FeedType.SpotMedian, + feedId + ); + uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate( + encodedUpdate + ); assertEq(numUpdates, 1, "Number of updates should be 1"); - SpotMedian memory spotMedian = pragmaHarness.exposed_spotMedianFeeds(feedId); + SpotMedian memory spotMedian = pragmaHarness.exposed_spotMedianFeeds( + feedId + ); - assertEq(spotMedian.metadata.timestamp, block.timestamp, "Timestamp should match"); - assertEq(spotMedian.metadata.numberOfSources, 5, "Number of sources should be 5"); + assertEq( + spotMedian.metadata.timestamp, + block.timestamp, + "Timestamp should match" + ); + assertEq( + spotMedian.metadata.numberOfSources, + 5, + "Number of sources should be 5" + ); assertEq(spotMedian.metadata.decimals, 8, "Decimals should be 8"); assertEq(spotMedian.metadata.feedId, feedId, "Feed ID should match"); assertEq(spotMedian.price, 2000 * 1e8, "Price should match"); @@ -80,15 +99,28 @@ contract PragmaHarnessTest is Test { TestConstantsLib.BTC_USD ) ); - bytes memory encodedUpdate = TestUtils.createEncodedUpdate(FeedType.Twap, feedId); - uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate(encodedUpdate); + bytes memory encodedUpdate = TestUtils.createEncodedUpdate( + FeedType.Twap, + feedId + ); + uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate( + encodedUpdate + ); assertEq(numUpdates, 1, "Number of updates should be 1"); TWAP memory twap = pragmaHarness.exposed_twapFeeds(feedId); - assertEq(twap.metadata.timestamp, block.timestamp, "Timestamp should match"); - assertEq(twap.metadata.numberOfSources, 5, "Number of sources should be 5"); + assertEq( + twap.metadata.timestamp, + block.timestamp, + "Timestamp should match" + ); + assertEq( + twap.metadata.numberOfSources, + 5, + "Number of sources should be 5" + ); assertEq(twap.metadata.decimals, 8, "Decimals should be 8"); assertEq(twap.metadata.feedId, feedId, "Feed ID should match"); assertEq(twap.twapPrice, 30000 * 1e8, "TWAP price should match"); @@ -96,7 +128,11 @@ contract PragmaHarnessTest is Test { assertEq(twap.startPrice, 29000 * 1e8, "Start price should match"); assertEq(twap.endPrice, 31000 * 1e8, "End price should match"); assertEq(twap.totalVolume, 1000 * 1e18, "Total volume should match"); - assertEq(twap.numberOfDataPoints, 60, "Number of data points should match"); + assertEq( + twap.numberOfDataPoints, + 60, + "Number of data points should match" + ); } function testUpdateDataInfoFromUpdateRealizedVolatility() public { @@ -110,13 +146,26 @@ contract PragmaHarnessTest is Test { TestConstantsLib.BTC_USD ) ); - bytes memory encodedUpdate = TestUtils.createEncodedUpdate(FeedType.RealizedVolatility, feedId); - uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate(encodedUpdate); + bytes memory encodedUpdate = TestUtils.createEncodedUpdate( + FeedType.RealizedVolatility, + feedId + ); + uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate( + encodedUpdate + ); RealizedVolatility memory rv = pragmaHarness.exposed_rvFeeds(feedId); assertEq(numUpdates, 1, "Number of updates should be 1"); - assertEq(rv.metadata.timestamp, block.timestamp, "Timestamp should match"); - assertEq(rv.metadata.numberOfSources, 5, "Number of sources should be 5"); + assertEq( + rv.metadata.timestamp, + block.timestamp, + "Timestamp should match" + ); + assertEq( + rv.metadata.numberOfSources, + 5, + "Number of sources should be 5" + ); assertEq(rv.metadata.decimals, 8, "Decimals should be 8"); assertEq(rv.metadata.feedId, feedId, "Feed id ID should match"); assertEq(rv.volatility, 50 * 1e6, "Volatility should match"); // 50% volatility @@ -125,7 +174,11 @@ contract PragmaHarnessTest is Test { assertEq(rv.endPrice, 2100 * 1e8, "End price should match"); assertEq(rv.highPrice, 2200 * 1e8, "High price should match"); assertEq(rv.lowPrice, 1800 * 1e8, "Low price should match"); - assertEq(rv.numberOfDataPoints, 1440, "Number of data points should match"); + assertEq( + rv.numberOfDataPoints, + 1440, + "Number of data points should match" + ); } function testUpdateDataInfoFromUpdateOptions() public { @@ -139,22 +192,43 @@ contract PragmaHarnessTest is Test { TestConstantsLib.BTC_USD ) ); - bytes memory encodedUpdate = TestUtils.createEncodedUpdate(FeedType.Options, feedId); - uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate(encodedUpdate); + bytes memory encodedUpdate = TestUtils.createEncodedUpdate( + FeedType.Options, + feedId + ); + uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate( + encodedUpdate + ); assertEq(numUpdates, 1, "Number of updates should be 1"); Options memory options = pragmaHarness.exposed_optionsFeeds(feedId); - assertEq(options.metadata.timestamp, block.timestamp, "Timestamp should match"); - assertEq(options.metadata.numberOfSources, 5, "Number of sources should be 5"); + assertEq( + options.metadata.timestamp, + block.timestamp, + "Timestamp should match" + ); + assertEq( + options.metadata.numberOfSources, + 5, + "Number of sources should be 5" + ); assertEq(options.metadata.decimals, 8, "Decimals should be 8"); assertEq(options.metadata.feedId, feedId, "Feed ID should match"); assertEq(options.strikePrice, 2000 * 1e8, "Strike price should match"); - assertEq(options.impliedVolatility, 50 * 1e6, "Implied volatility should match"); + assertEq( + options.impliedVolatility, + 50 * 1e6, + "Implied volatility should match" + ); assertEq(options.timeToExpiry, 604800, "Time to expiry should match"); assertEq(options.isCall, true, "Option type should be call"); - assertEq(options.underlyingPrice, 1950 * 1e8, "Underlying price should match"); + assertEq( + options.underlyingPrice, + 1950 * 1e8, + "Underlying price should match" + ); assertEq(options.optionPrice, 100 * 1e8, "Option price should match"); assertEq(options.delta, 60 * 1e6, "Delta should match"); assertEq(options.gamma, 2 * 1e6, "Gamma should match"); @@ -174,14 +248,27 @@ contract PragmaHarnessTest is Test { TestConstantsLib.BTC_USD ) ); - bytes memory encodedUpdate = TestUtils.createEncodedUpdate(FeedType.Perpetuals, feedId); - uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate(encodedUpdate); + bytes memory encodedUpdate = TestUtils.createEncodedUpdate( + FeedType.Perpetuals, + feedId + ); + uint8 numUpdates = pragmaHarness.exposed_updateDataInfoFromUpdate( + encodedUpdate + ); assertEq(numUpdates, 1, "Number of updates should be 1"); Perp memory perp = pragmaHarness.exposed_perpFeeds(feedId); - assertEq(perp.metadata.timestamp, block.timestamp, "Timestamp should match"); - assertEq(perp.metadata.numberOfSources, 5, "Number of sources should be 5"); + assertEq( + perp.metadata.timestamp, + block.timestamp, + "Timestamp should match" + ); + assertEq( + perp.metadata.numberOfSources, + 5, + "Number of sources should be 5" + ); assertEq(perp.metadata.decimals, 8, "Decimals should be 8"); assertEq(perp.metadata.feedId, feedId, "Feed ID should match"); assertEq(perp.markPrice, 2000 * 1e8, "Mark price should match"); @@ -198,7 +285,9 @@ contract PragmaUpgradeableTest is Test { address public user; function setUp() public { - pragma_ = PragmaHarness(TestUtils.configurePragmaContract(FeedType.SpotMedian)); + pragma_ = PragmaHarness( + TestUtils.configurePragmaContract(FeedType.SpotMedian) + ); owner = address(this); user = address(0x1); } @@ -235,8 +324,14 @@ contract PragmaUpgradeableTest is Test { pragma_.upgradeToAndCall(address(pragmaV2), ""); // Check if state is maintained - assertEq(PragmaUpgraded(address(pragma_)).validTimePeriodSeconds(), 120); - assertEq(PragmaUpgraded(address(pragma_)).singleUpdateFeeInWei(), 0.1 ether); + assertEq( + PragmaUpgraded(address(pragma_)).validTimePeriodSeconds(), + 120 + ); + assertEq( + PragmaUpgraded(address(pragma_)).singleUpdateFeeInWei(), + 0.1 ether + ); // Add more checks for other state variables } diff --git a/solidity/test/TestUtils.sol b/solidity/test/TestUtils.sol index 5107663..da0998e 100644 --- a/solidity/test/TestUtils.sol +++ b/solidity/test/TestUtils.sol @@ -13,47 +13,67 @@ import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transpa contract PragmaHarness is Pragma { constructor() Pragma() {} - function exposed_updateDataInfoFromUpdate(bytes calldata updateData) external returns (uint8) { + function exposed_updateDataInfoFromUpdate( + bytes calldata updateData + ) external returns (uint8) { return updateDataInfoFromUpdate(updateData); } - function exposed_spotMedianFeeds(bytes32 feedId) external view returns (SpotMedian memory) { + function exposed_spotMedianFeeds( + bytes32 feedId + ) external view returns (SpotMedian memory) { return spotMedianFeeds[feedId]; } - function exposed_twapFeeds(bytes32 feedId) external view returns (TWAP memory) { + function exposed_twapFeeds( + bytes32 feedId + ) external view returns (TWAP memory) { return twapFeeds[feedId]; } - function exposed_rvFeeds(bytes32 feedId) external view returns (RealizedVolatility memory) { + function exposed_rvFeeds( + bytes32 feedId + ) external view returns (RealizedVolatility memory) { return rvFeeds[feedId]; } - function exposed_optionsFeeds(bytes32 feedId) external view returns (Options memory) { + function exposed_optionsFeeds( + bytes32 feedId + ) external view returns (Options memory) { return optionsFeeds[feedId]; } - function exposed_perpFeeds(bytes32 feedId) external view returns (Perp memory) { + function exposed_perpFeeds( + bytes32 feedId + ) external view returns (Perp memory) { return perpFeeds[feedId]; } - function _isProofValid(bytes calldata encodedProof, uint256 offset, bytes32 root, bytes calldata leafData) - internal - virtual - override - returns (bool valid, uint256 endOffset) - { + function _isProofValid( + bytes calldata encodedProof, + uint256 offset, + bytes32 root, + bytes calldata leafData + ) internal virtual override returns (bool valid, uint256 endOffset) { // valid set to true for testing unchecked { bytes32 currentDigest = MerkleTree.leafHash(leafData); uint256 proofOffset = 0; - uint16 proofSizeArray = UnsafeCalldataBytesLib.toUint16(encodedProof, proofOffset); + uint16 proofSizeArray = UnsafeCalldataBytesLib.toUint16( + encodedProof, + proofOffset + ); proofOffset += 2; for (uint256 i = 0; i < proofSizeArray; i++) { - bytes32 siblingDigest = bytes32(UnsafeCalldataBytesLib.toBytes32(encodedProof, proofOffset)); + bytes32 siblingDigest = bytes32( + UnsafeCalldataBytesLib.toBytes32(encodedProof, proofOffset) + ); proofOffset += 32; // TO CHECK - currentDigest = MerkleTree.nodeHash(currentDigest, siblingDigest); + currentDigest = MerkleTree.nodeHash( + currentDigest, + siblingDigest + ); } valid = true; endOffset = offset + proofOffset; @@ -64,44 +84,71 @@ contract PragmaHarness is Pragma { library TestUtils { using BytesLib for bytes; - function createHyperlaneMessage(bytes memory payload, bytes32 feedId) internal view returns (bytes memory) { + function createHyperlaneMessage( + bytes memory payload, + bytes32 feedId + ) internal view returns (bytes memory) { bytes memory signatures = abi.encodePacked( uint8(5), // number of signatures uint8(0), - bytes32(0x83db08d4e1590714aef8600f5f1e3c967ab6a3b9f93bb4242de0306510e688ea), - bytes32(0x0af5d1d51ea7e51a291789ff4866a1e36bc4134d956870799380d2d71f5dbf3d), + bytes32( + 0x83db08d4e1590714aef8600f5f1e3c967ab6a3b9f93bb4242de0306510e688ea + ), + bytes32( + 0x0af5d1d51ea7e51a291789ff4866a1e36bc4134d956870799380d2d71f5dbf3d + ), uint8(27), uint8(1), - bytes32(0xf81a5dd3f871ad2d27a3b538e73663d723f8263fb3d289514346d43d000175f5), - bytes32(0x083df770623e9ae52a7bb154473961e24664bb003bdfdba6100fb5e540875ce1), + bytes32( + 0xf81a5dd3f871ad2d27a3b538e73663d723f8263fb3d289514346d43d000175f5 + ), + bytes32( + 0x083df770623e9ae52a7bb154473961e24664bb003bdfdba6100fb5e540875ce1 + ), uint8(27), uint8(2), - bytes32(0x76b194f951f94492ca582dab63dc413b9ac1ca9992c22bc2186439e9ab8fdd3c), - bytes32(0x62a6a6f402edaa53e9bdc715070a61edb0d98d4e14e182f60bdd4ae932b40b29), + bytes32( + 0x76b194f951f94492ca582dab63dc413b9ac1ca9992c22bc2186439e9ab8fdd3c + ), + bytes32( + 0x62a6a6f402edaa53e9bdc715070a61edb0d98d4e14e182f60bdd4ae932b40b29 + ), uint8(27), uint8(3), - bytes32(0x35932eefd85897d868aaacd4ba7aee81a2384e42ba062133f6d37fdfebf94ad4), - bytes32(0x78cce49db96ee27c3f461800388ac95101476605baa64a194b7dd4d56d2d4a4d), + bytes32( + 0x35932eefd85897d868aaacd4ba7aee81a2384e42ba062133f6d37fdfebf94ad4 + ), + bytes32( + 0x78cce49db96ee27c3f461800388ac95101476605baa64a194b7dd4d56d2d4a4d + ), uint8(27), uint8(4), - bytes32(0x6b38d4353d69396e91c57542254348d16459d448ab887574e9476a6ff76d49a1), - bytes32(0x3527627295bde423d7d799afef22affac4f00c70a5b651ad14c8879aeb9b6e03), + bytes32( + 0x6b38d4353d69396e91c57542254348d16459d448ab887574e9476a6ff76d49a1 + ), + bytes32( + 0x3527627295bde423d7d799afef22affac4f00c70a5b651ad14c8879aeb9b6e03 + ), uint8(27) ); - return abi.encodePacked( - uint8(3), // version - signatures, - uint32(0), // nonce - uint64(block.timestamp), // timestamp - uint32(1), // emitterChainId - bytes32(uint256(0x1234)), // emitterAddress - bytes32(uint256(0x12311)), // merkle tree hook address - payload - ); + return + abi.encodePacked( + uint8(3), // version + signatures, + uint32(0), // nonce + uint64(block.timestamp), // timestamp + uint32(1), // emitterChainId + bytes32(uint256(0x1234)), // emitterAddress + bytes32(uint256(0x12311)), // merkle tree hook address + payload + ); } - function createEncodedUpdate(FeedType dataType, bytes32 feedId) internal view returns (bytes memory) { + function createEncodedUpdate( + FeedType dataType, + bytes32 feedId + ) internal view returns (bytes memory) { bytes memory updateData = abi.encodePacked( feedId, uint64(block.timestamp), // timestamp @@ -162,9 +209,15 @@ library TestUtils { bytes memory proof = abi.encodePacked( uint16(3), // proof length in array - bytes32(0x1012312123213123213231231233421341341234134142341123331123123123), - bytes32(0x1012312312312312312311231233434342421414123413413123331123123123), - bytes32(0x1012312312312312312312323324234234234234324234212123331123123123) + bytes32( + 0x1012312123213123213231231233421341341234134142341123331123123123 + ), + bytes32( + 0x1012312312312312312311231233434342421414123413413123331123123123 + ), + bytes32( + 0x1012312312312312312312323324234234234234324234212123331123123123 + ) ); bytes memory hyMsgPayload = abi.encodePacked( @@ -182,21 +235,27 @@ library TestUtils { bytes memory hyMsg = createHyperlaneMessage(hyMsgPayload, feedId); - return abi.encodePacked( - uint8(1), // majorVersion - uint8(0), // minorVersion - uint8(0), // trailingHeaderSize - uint16(hyMsg.length), // hyMsgSize - hyMsg - ); + return + abi.encodePacked( + uint8(1), // majorVersion + uint8(0), // minorVersion + uint8(0), // trailingHeaderSize + uint16(hyMsg.length), // hyMsgSize + hyMsg + ); } - function extractUpdateData(bytes memory encodedUpdate) internal pure returns (bytes memory) { + function extractUpdateData( + bytes memory encodedUpdate + ) internal pure returns (bytes memory) { // Skip the header (22 bytes) and extract the update data return encodedUpdate.slice(22, encodedUpdate.length - 22); } - function setUpHyperlane(uint8 numValidators, address[] memory initSigners) public returns (address) { + function setUpHyperlane( + uint8 numValidators, + address[] memory initSigners + ) public returns (address) { if (initSigners.length == 0) { initSigners = new address[](numValidators); } @@ -204,51 +263,103 @@ library TestUtils { return address(hyperlane_); } - function configurePragmaContract(FeedType dataType) internal returns (address) { + function configurePragmaContract( + FeedType dataType + ) internal returns (address) { address[][] memory validatorSets = new address[][](5); // SPOT MEDIAN validatorSets[0] = new address[](5); - validatorSets[0][0] = address(0x00e8E7139138f65b1aa54DA6947acD517209D3f394); - validatorSets[0][1] = address(0x008c28701CFc840C250E254E4fC39FB91CCC6AA8D7); - validatorSets[0][2] = address(0x00f15acc7Cb78888c5D9428B1522425c204e4ABAd5); - validatorSets[0][3] = address(0x00CeCc9Bc7f72DE019579F6e1a5fb969CBbb2A7bd5); - validatorSets[0][4] = address(0x00Fa992B3954C23e1b8882Ac32d964374E06F55333); + validatorSets[0][0] = address( + 0x00e8E7139138f65b1aa54DA6947acD517209D3f394 + ); + validatorSets[0][1] = address( + 0x008c28701CFc840C250E254E4fC39FB91CCC6AA8D7 + ); + validatorSets[0][2] = address( + 0x00f15acc7Cb78888c5D9428B1522425c204e4ABAd5 + ); + validatorSets[0][3] = address( + 0x00CeCc9Bc7f72DE019579F6e1a5fb969CBbb2A7bd5 + ); + validatorSets[0][4] = address( + 0x00Fa992B3954C23e1b8882Ac32d964374E06F55333 + ); // TWAP validatorSets[1] = new address[](5); - validatorSets[1][0] = address(0x00e3b5D43D7f26E4bDa6f6B26cB170C176f0dA5a20); - validatorSets[1][1] = address(0x0098E4Ba0098C45880d5Fbb60E9B0b9e07168FBd61); - validatorSets[1][2] = address(0x00585EF8DE363ed6d6c0CaD3787fD6E0a17719EA0B); - validatorSets[1][3] = address(0x00976Ae456b0C4B2F98719dfB47237e27e69375748); - validatorSets[1][4] = address(0x001597E2726DDd04eeaF091BC23f5e46D8558dDcA6); + validatorSets[1][0] = address( + 0x00e3b5D43D7f26E4bDa6f6B26cB170C176f0dA5a20 + ); + validatorSets[1][1] = address( + 0x0098E4Ba0098C45880d5Fbb60E9B0b9e07168FBd61 + ); + validatorSets[1][2] = address( + 0x00585EF8DE363ed6d6c0CaD3787fD6E0a17719EA0B + ); + validatorSets[1][3] = address( + 0x00976Ae456b0C4B2F98719dfB47237e27e69375748 + ); + validatorSets[1][4] = address( + 0x001597E2726DDd04eeaF091BC23f5e46D8558dDcA6 + ); // Realized volatility validatorSets[2] = new address[](5); - validatorSets[2][0] = address(0x00d9607ED6086BEE33C572C05B1B7cB645caCf8dD3); - validatorSets[2][1] = address(0x00FeC1E22DEC7D064bEBB88692ddaEaCDeA6Eb593f); - validatorSets[2][2] = address(0x000Af1cbA0Ce31d2A9B6BeC4b8700caA26B0cCeCE9); - validatorSets[2][3] = address(0x009F85b89b07C9b8E607886b9f6C068a6b14E2e3f0); - validatorSets[2][4] = address(0x003A5B961D0Be9e10BA79b6249C2e05F88bcD2c51b); + validatorSets[2][0] = address( + 0x00d9607ED6086BEE33C572C05B1B7cB645caCf8dD3 + ); + validatorSets[2][1] = address( + 0x00FeC1E22DEC7D064bEBB88692ddaEaCDeA6Eb593f + ); + validatorSets[2][2] = address( + 0x000Af1cbA0Ce31d2A9B6BeC4b8700caA26B0cCeCE9 + ); + validatorSets[2][3] = address( + 0x009F85b89b07C9b8E607886b9f6C068a6b14E2e3f0 + ); + validatorSets[2][4] = address( + 0x003A5B961D0Be9e10BA79b6249C2e05F88bcD2c51b + ); // Options validatorSets[3] = new address[](5); - validatorSets[3][0] = address(0x00e86C32850c1d9426B03668f351F831082d2E4044); - validatorSets[3][1] = address(0x005d79EBF4fE933b3086b1Eb0d8Fc82aF10511a9f1); - validatorSets[3][2] = address(0x006A403FD4dAf647Be863839b3f767a85736dA031e); - validatorSets[3][3] = address(0x000c10AEB13fCc0dCb40AD130aa02102DF011f7731); - validatorSets[3][4] = address(0x00B434aA8CA372c496242015CC4215ca10dEFA6dC2); + validatorSets[3][0] = address( + 0x00e86C32850c1d9426B03668f351F831082d2E4044 + ); + validatorSets[3][1] = address( + 0x005d79EBF4fE933b3086b1Eb0d8Fc82aF10511a9f1 + ); + validatorSets[3][2] = address( + 0x006A403FD4dAf647Be863839b3f767a85736dA031e + ); + validatorSets[3][3] = address( + 0x000c10AEB13fCc0dCb40AD130aa02102DF011f7731 + ); + validatorSets[3][4] = address( + 0x00B434aA8CA372c496242015CC4215ca10dEFA6dC2 + ); // Perp validatorSets[4] = new address[](5); - validatorSets[4][0] = address(0x0074C61a875653D6cD07c6aFD8499625f4248c32b4); - validatorSets[4][1] = address(0x00b8E79066654F5cb18CDC0610dA40a57a962d6253); - validatorSets[4][2] = address(0x0071fA97365539079cEe27DC1De110277B400f1151); - validatorSets[4][3] = address(0x00fB065ded62A92D7BEb7Ed0cCEB717808227Fe9A9); - validatorSets[4][4] = address(0x008841cF99b6b607e898B59E7674E05f794701535C); + validatorSets[4][0] = address( + 0x0074C61a875653D6cD07c6aFD8499625f4248c32b4 + ); + validatorSets[4][1] = address( + 0x00b8E79066654F5cb18CDC0610dA40a57a962d6253 + ); + validatorSets[4][2] = address( + 0x0071fA97365539079cEe27DC1De110277B400f1151 + ); + validatorSets[4][3] = address( + 0x00fB065ded62A92D7BEb7Ed0cCEB717808227Fe9A9 + ); + validatorSets[4][4] = address( + 0x008841cF99b6b607e898B59E7674E05f794701535C + ); uint8 validatorSetIndex; if (dataType == FeedType.SpotMedian) { @@ -266,8 +377,12 @@ library TestUtils { } // Set up the Hyperlane contract with the provided validator - IHyperlane hyperlane = - IHyperlane(setUpHyperlane(uint8(validatorSets[validatorSetIndex].length), validatorSets[validatorSetIndex])); + IHyperlane hyperlane = IHyperlane( + setUpHyperlane( + uint8(validatorSets[validatorSetIndex].length), + validatorSets[validatorSetIndex] + ) + ); uint32[] memory chainIds = new uint32[](1); chainIds[0] = 1; @@ -287,20 +402,29 @@ library TestUtils { 0.1 ether ); - TransparentUpgradeableProxy proxy = - new TransparentUpgradeableProxy(address(pragmaImpl), address(this), initData); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(pragmaImpl), + address(this), + initData + ); return address(proxy); } function configurePragmaRawContract() internal returns (address) { address[] memory validatorSets = new address[](1); validatorSets[0] = address(0xF6311461A6d8b44cb3F62b2FCd47570A28443ca0); - IHyperlane hyperlane = IHyperlane(setUpHyperlane(uint8(validatorSets.length), validatorSets)); + IHyperlane hyperlane = IHyperlane( + setUpHyperlane(uint8(validatorSets.length), validatorSets) + ); uint32[] memory chainIds = new uint32[](1); chainIds[0] = 6363709; bytes32[] memory emitterAddresses = new bytes32[](1); - emitterAddresses[0] = bytes32(uint256(0x60240F2BCCEF7E64F920EEC05C5BFFFBC48C6CEAA4EFCA8748772B60CBAFC3)); + emitterAddresses[0] = bytes32( + uint256( + 0x60240F2BCCEF7E64F920EEC05C5BFFFBC48C6CEAA4EFCA8748772B60CBAFC3 + ) + ); PragmaHarness pragmaImpl = new PragmaHarness(); @@ -314,8 +438,11 @@ library TestUtils { 0.1 ether ); - TransparentUpgradeableProxy proxy = - new TransparentUpgradeableProxy(address(pragmaImpl), address(this), initData); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(pragmaImpl), + address(this), + initData + ); return address(proxy); } } diff --git a/solidity/test/benchmarks/PragmaDecoderGasTest.t.sol b/solidity/test/benchmarks/PragmaDecoderGasTest.t.sol index 04fc39d..69c185e 100644 --- a/solidity/test/benchmarks/PragmaDecoderGasTest.t.sol +++ b/solidity/test/benchmarks/PragmaDecoderGasTest.t.sol @@ -10,7 +10,10 @@ import "../utils/TestConstants.sol"; contract PragmaDecoderGasTest is Test { PragmaHarness private pragmaHarness; - function setUpHyperlane(uint8 numValidators, address[] memory initSigners) public returns (address) { + function setUpHyperlane( + uint8 numValidators, + address[] memory initSigners + ) public returns (address) { if (initSigners.length == 0) { initSigners = new address[](numValidators); } @@ -24,11 +27,19 @@ contract PragmaDecoderGasTest is Test { } function _setUp(FeedType dataType) public { - pragmaHarness = PragmaHarness(TestUtils.configurePragmaContract(dataType)); + pragmaHarness = PragmaHarness( + TestUtils.configurePragmaContract(dataType) + ); } function testGasAllUpdates() public { - string[5] memory updateTypes = ["SpotMedian", "TWAP", "RealizedVolatility", "Options", "Perpetuals"]; + string[5] memory updateTypes = [ + "SpotMedian", + "TWAP", + "RealizedVolatility", + "Options", + "Perpetuals" + ]; uint256[5] memory currencies = [ TestConstantsLib.ETH_USD, TestConstantsLib.BTC_USD, @@ -48,13 +59,25 @@ contract PragmaDecoderGasTest is Test { currencies[i] ) ); - bytes memory encodedUpdate = TestUtils.createEncodedUpdate(dataType, feedId); + bytes memory encodedUpdate = TestUtils.createEncodedUpdate( + dataType, + feedId + ); uint256 gasBefore = gasleft(); pragmaHarness.exposed_updateDataInfoFromUpdate(encodedUpdate); uint256 gasUsed = gasBefore - gasleft(); - console.log(string(abi.encodePacked("Gas used for ", updateTypes[i], " update: ", vm.toString(gasUsed)))); + console.log( + string( + abi.encodePacked( + "Gas used for ", + updateTypes[i], + " update: ", + vm.toString(gasUsed) + ) + ) + ); } } } diff --git a/solidity/test/mocks/PragmaUpgraded.sol b/solidity/test/mocks/PragmaUpgraded.sol index 3135d08..b28bcda 100644 --- a/solidity/test/mocks/PragmaUpgraded.sol +++ b/solidity/test/mocks/PragmaUpgraded.sol @@ -16,7 +16,13 @@ import "../../src/libraries/DataParser.sol"; /// @author Pragma Labs /// @custom:contact security@pragma.build /// @notice The Pragma contract. -contract PragmaUpgraded is Initializable, UUPSUpgradeable, OwnableUpgradeable, IPragma, PragmaDecoder { +contract PragmaUpgraded is + Initializable, + UUPSUpgradeable, + OwnableUpgradeable, + IPragma, + PragmaDecoder +{ /* STORAGE */ uint256 public validTimePeriodSeconds; uint256 public singleUpdateFeeInWei; @@ -39,18 +45,23 @@ contract PragmaUpgraded is Initializable, UUPSUpgradeable, OwnableUpgradeable, I __Pragma_init(_validTimePeriodSeconds, _singleUpdateFeeInWei); } - function __Pragma_init(uint256 _validTimePeriodSeconds, uint256 _singleUpdateFeeInWei) internal initializer { + function __Pragma_init( + uint256 _validTimePeriodSeconds, + uint256 _singleUpdateFeeInWei + ) internal initializer { validTimePeriodSeconds = _validTimePeriodSeconds; singleUpdateFeeInWei = _singleUpdateFeeInWei; } - function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + function _authorizeUpgrade( + address newImplementation + ) internal override onlyOwner {} /// @inheritdoc IPragma function updateDataFeeds(bytes[] calldata updateData) external payable { uint256 totalNumUpdates = 0; uint256 len = updateData.length; - for (uint256 i = 0; i < len;) { + for (uint256 i = 0; i < len; ) { totalNumUpdates += updateDataInfoFromUpdate(updateData[i]); unchecked { i++; @@ -63,15 +74,22 @@ contract PragmaUpgraded is Initializable, UUPSUpgradeable, OwnableUpgradeable, I } /// @inheritdoc IPragma - function getUpdateFee(bytes[] calldata updateData) external view returns (uint256 feeAmount) { + function getUpdateFee( + bytes[] calldata updateData + ) external view returns (uint256 feeAmount) { return 100000; } - function getTotalFee(uint256 totalNumUpdates) private view returns (uint256 requiredFee) { + function getTotalFee( + uint256 totalNumUpdates + ) private view returns (uint256 requiredFee) { return totalNumUpdates * singleUpdateFeeInWei; } - function getSpotMedianNoOlderThan(bytes32 id, uint256 age) external view returns (SpotMedian memory data) { + function getSpotMedianNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (SpotMedian memory data) { data = spotMedianFeeds[id]; if (data.metadata.timestamp == 0) { revert ErrorsLib.DataNotFound(); @@ -82,7 +100,10 @@ contract PragmaUpgraded is Initializable, UUPSUpgradeable, OwnableUpgradeable, I return data; } - function getTwapNoOlderThan(bytes32 id, uint256 age) external view returns (TWAP memory data) { + function getTwapNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (TWAP memory data) { data = twapFeeds[id]; if (data.metadata.timestamp == 0) { revert ErrorsLib.DataNotFound(); @@ -92,11 +113,10 @@ contract PragmaUpgraded is Initializable, UUPSUpgradeable, OwnableUpgradeable, I } } - function getRealizedVolatilityNoOlderThan(bytes32 id, uint256 age) - external - view - returns (RealizedVolatility memory data) - { + function getRealizedVolatilityNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (RealizedVolatility memory data) { data = rvFeeds[id]; if (data.metadata.timestamp == 0) { revert ErrorsLib.DataNotFound(); @@ -106,7 +126,10 @@ contract PragmaUpgraded is Initializable, UUPSUpgradeable, OwnableUpgradeable, I } } - function getOptionsNoOlderThan(bytes32 id, uint256 age) external view returns (Options memory data) { + function getOptionsNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (Options memory data) { data = optionsFeeds[id]; if (data.metadata.timestamp == 0) { revert ErrorsLib.DataNotFound(); @@ -116,7 +139,10 @@ contract PragmaUpgraded is Initializable, UUPSUpgradeable, OwnableUpgradeable, I } } - function getPerpNoOlderThan(bytes32 id, uint256 age) external view returns (Perp memory data) { + function getPerpNoOlderThan( + bytes32 id, + uint256 age + ) external view returns (Perp memory data) { data = perpFeeds[id]; if (data.metadata.timestamp == 0) { revert ErrorsLib.DataNotFound(); @@ -147,8 +173,10 @@ contract PragmaUpgraded is Initializable, UUPSUpgradeable, OwnableUpgradeable, I function getValidTimePeriod() public view returns (uint256) { return validTimePeriodSeconds; } - - function getSpotMedianFeed(bytes32 feedId) external view returns (SpotMedian memory) { + + function getSpotMedianFeed( + bytes32 feedId + ) external view returns (SpotMedian memory) { return spotMedianFeeds[feedId]; } diff --git a/solidity/test/utils/BytesLibTest.sol b/solidity/test/utils/BytesLibTest.sol index 742ef7f..a588400 100644 --- a/solidity/test/utils/BytesLibTest.sol +++ b/solidity/test/utils/BytesLibTest.sol @@ -62,7 +62,11 @@ contract BytesToInt256Test is Test { console.log("Retrieved value:", uint256(retrievedValue)); - assertEq(retrievedValue, value, "Retrieved value does not match original value"); + assertEq( + retrievedValue, + value, + "Retrieved value does not match original value" + ); } function testFuzzing(int256 value, uint8 offset) public { @@ -86,6 +90,10 @@ contract BytesToInt256Test is Test { console.logBytes(data); } - assertEq(retrievedValue, value, "Retrieved value does not match original value"); + assertEq( + retrievedValue, + value, + "Retrieved value does not match original value" + ); } } diff --git a/solidity/test/utils/HyperlaneTestUtils.t.sol b/solidity/test/utils/HyperlaneTestUtils.t.sol index 7649cb4..270ef5d 100644 --- a/solidity/test/utils/HyperlaneTestUtils.t.sol +++ b/solidity/test/utils/HyperlaneTestUtils.t.sol @@ -12,7 +12,10 @@ abstract contract HyperlaneTestUtils is Test { uint32 constant CHAIN_ID = 2; // Ethereum address hyperlaneAddr; - function setUpHyperlane(uint8 numValidators, address[] memory initSigners) public returns (address) { + function setUpHyperlane( + uint8 numValidators, + address[] memory initSigners + ) public returns (address) { if (initSigners.length == 0) { initSigners = new address[](numValidators); } @@ -20,7 +23,10 @@ abstract contract HyperlaneTestUtils is Test { return address(hyperlane_); } - function isNotMatch(bytes memory a, bytes memory b) public pure returns (bool) { + function isNotMatch( + bytes memory a, + bytes memory b + ) public pure returns (bool) { return keccak256(a) != keccak256(b); } @@ -37,19 +43,39 @@ abstract contract HyperlaneTestUtils is Test { uint8 numSigners ) public view returns (bytes memory updateData) { bytes32[5] memory r = [ - bytes32(0x83db08d4e1590714aef8600f5f1e3c967ab6a3b9f93bb4242de0306510e688ea), - bytes32(0xf81a5dd3f871ad2d27a3b538e73663d723f8263fb3d289514346d43d000175f5), - bytes32(0x76b194f951f94492ca582dab63dc413b9ac1ca9992c22bc2186439e9ab8fdd3c), - bytes32(0x35932eefd85897d868aaacd4ba7aee81a2384e42ba062133f6d37fdfebf94ad4), - bytes32(0x6b38d4353d69396e91c57542254348d16459d448ab887574e9476a6ff76d49a1) + bytes32( + 0x83db08d4e1590714aef8600f5f1e3c967ab6a3b9f93bb4242de0306510e688ea + ), + bytes32( + 0xf81a5dd3f871ad2d27a3b538e73663d723f8263fb3d289514346d43d000175f5 + ), + bytes32( + 0x76b194f951f94492ca582dab63dc413b9ac1ca9992c22bc2186439e9ab8fdd3c + ), + bytes32( + 0x35932eefd85897d868aaacd4ba7aee81a2384e42ba062133f6d37fdfebf94ad4 + ), + bytes32( + 0x6b38d4353d69396e91c57542254348d16459d448ab887574e9476a6ff76d49a1 + ) ]; bytes32[5] memory s = [ - bytes32(0x0af5d1d51ea7e51a291789ff4866a1e36bc4134d956870799380d2d71f5dbf3d), - bytes32(0x083df770623e9ae52a7bb154473961e24664bb003bdfdba6100fb5e540875ce1), - bytes32(0x62a6a6f402edaa53e9bdc715070a61edb0d98d4e14e182f60bdd4ae932b40b29), - bytes32(0x78cce49db96ee27c3f461800388ac95101476605baa64a194b7dd4d56d2d4a4d), - bytes32(0x3527627295bde423d7d799afef22affac4f00c70a5b651ad14c8879aeb9b6e03) + bytes32( + 0x0af5d1d51ea7e51a291789ff4866a1e36bc4134d956870799380d2d71f5dbf3d + ), + bytes32( + 0x083df770623e9ae52a7bb154473961e24664bb003bdfdba6100fb5e540875ce1 + ), + bytes32( + 0x62a6a6f402edaa53e9bdc715070a61edb0d98d4e14e182f60bdd4ae932b40b29 + ), + bytes32( + 0x78cce49db96ee27c3f461800388ac95101476605baa64a194b7dd4d56d2d4a4d + ), + bytes32( + 0x3527627295bde423d7d799afef22affac4f00c70a5b651ad14c8879aeb9b6e03 + ) ]; bytes memory signatures; @@ -59,11 +85,21 @@ abstract contract HyperlaneTestUtils is Test { uint8 validatorIndex = uint8(i); // Example index for validator uint8 v = 27; // Pack all signature parts - signatures = abi.encodePacked(signatures, validatorIndex, r[i], s[i], v); + signatures = abi.encodePacked( + signatures, + validatorIndex, + r[i], + s[i], + v + ); } - bytes32 domainHash = keccak256(abi.encodePacked(emitterChainId, merkleTreeHookAddress, "HYPERLANE")); - bytes32 _hash = keccak256(abi.encodePacked(domainHash, root, checkpointIndex, messageId)); + bytes32 domainHash = keccak256( + abi.encodePacked(emitterChainId, merkleTreeHookAddress, "HYPERLANE") + ); + bytes32 _hash = keccak256( + abi.encodePacked(domainHash, root, checkpointIndex, messageId) + ); // Construct the updateData by concatenating all parts updateData = abi.encodePacked( TestConstantsLib.HYPERLANE_VERSION, @@ -86,11 +122,15 @@ contract HyperlaneTestUtilsTest is Test, HyperlaneTestUtils { uint32 constant TEST_NONCE = 1234; uint64 constant TEST_UPDATE_TIMESTAMP = 112; uint32 constant TEST_EMITTER_CHAIN_ID = 7; - bytes32 constant TEST_EMITTER_ADDR = 0x0000000000000000000000000000000000000000000000000000000000000bad; - bytes32 constant TEST_MERKLE_TREE_ADDRESS = 0x0000000000000000000000000000000000000000000000000000000000000aaa; - bytes32 constant TEST_ROOT = 0x0000000000000000000000000000000000000000000000000000000000000bbb; + bytes32 constant TEST_EMITTER_ADDR = + 0x0000000000000000000000000000000000000000000000000000000000000bad; + bytes32 constant TEST_MERKLE_TREE_ADDRESS = + 0x0000000000000000000000000000000000000000000000000000000000000aaa; + bytes32 constant TEST_ROOT = + 0x0000000000000000000000000000000000000000000000000000000000000bbb; uint32 constant TEST_CHECKPOINT_INDEX = 10; - bytes32 constant TEST_MESSAGE_ID = 0x000000000000000000000000000000000000000000000000000000000000dddd; + bytes32 constant TEST_MESSAGE_ID = + 0x000000000000000000000000000000000000000000000000000000000000dddd; bytes constant TEST_PAYLOAD = hex"deadbeaf"; uint8 constant TEST_NUM_SIGNERS = 5; @@ -104,14 +144,30 @@ contract HyperlaneTestUtilsTest is Test, HyperlaneTestUtils { assertTrue(valid); assertEq(reason, ""); assertEq(hyMsg.nonce, TEST_NONCE, "Nonce does not correspond"); - assertEq(hyMsg.timestamp, TEST_UPDATE_TIMESTAMP, "Timestamp does not correspond"); - assertEq(hyMsg.emitterChainId, TEST_EMITTER_CHAIN_ID, "Emitter chain id does not correspond"); - assertEq(hyMsg.emitterAddress, TEST_EMITTER_ADDR, "Emitter address does not correspond"); + assertEq( + hyMsg.timestamp, + TEST_UPDATE_TIMESTAMP, + "Timestamp does not correspond" + ); + assertEq( + hyMsg.emitterChainId, + TEST_EMITTER_CHAIN_ID, + "Emitter chain id does not correspond" + ); + assertEq( + hyMsg.emitterAddress, + TEST_EMITTER_ADDR, + "Emitter address does not correspond" + ); assertEq(hyMsg.payload, TEST_PAYLOAD, "Payload does not correspond"); // parseAndVerifyHyMsg() returns an empty signatures array for gas savings since it's not used // after its been verified. parseHyMsg() returns the full signatures array. - (hyMsg,,) = hyperlane.parseHyMsg(updateData); - assertEq(hyMsg.signatures.length, TEST_NUM_SIGNERS, "Num signers does not correspond"); + (hyMsg, , ) = hyperlane.parseHyMsg(updateData); + assertEq( + hyMsg.signatures.length, + TEST_NUM_SIGNERS, + "Num signers does not correspond" + ); } function testGenerateUpdateDataWorks() public { @@ -124,7 +180,9 @@ contract HyperlaneTestUtilsTest is Test, HyperlaneTestUtils { validators[4] = address(0x00D7638ca8D8dbd7ab2A89BA7244191014142a137C); // Set up the Hyperlane contract with the provided validators - IHyperlane hyperlane = IHyperlane(setUpHyperlane(uint8(validators.length), validators)); + IHyperlane hyperlane = IHyperlane( + setUpHyperlane(uint8(validators.length), validators) + ); bytes memory updateData = generateUpdateData( TEST_NONCE, @@ -139,8 +197,15 @@ contract HyperlaneTestUtilsTest is Test, HyperlaneTestUtils { TEST_NUM_SIGNERS ); - (HyMsg memory hyMsg, bool valid, string memory reason,,) = hyperlane.parseAndVerifyHyMsg(updateData); - assertHyMsgMatchesTestValues(hyMsg, valid, reason, updateData, hyperlane); + (HyMsg memory hyMsg, bool valid, string memory reason, , ) = hyperlane + .parseAndVerifyHyMsg(updateData); + assertHyMsgMatchesTestValues( + hyMsg, + valid, + reason, + updateData, + hyperlane + ); } function testParseHyMsg() public { @@ -178,32 +243,84 @@ contract HyperlaneTestUtilsTest is Test, HyperlaneTestUtils { address[] memory addresses; IHyperlane hyperlane = IHyperlane(setUpHyperlane(0, addresses)); // Parse the message - (HyMsg memory parsedMsg,,) = hyperlane.parseHyMsg(encodedHyMsg); + (HyMsg memory parsedMsg, , ) = hyperlane.parseHyMsg(encodedHyMsg); // Verify parsed fields assertEq(parsedMsg.version, 3, "Incorrect version"); - assertEq(parsedMsg.signatures.length, 2, "Incorrect number of signatures"); + assertEq( + parsedMsg.signatures.length, + 2, + "Incorrect number of signatures" + ); // Check first signature - assertEq(parsedMsg.signatures[0].validatorIndex, 0, "Incorrect validator index for first signature"); - assertEq(uint256(parsedMsg.signatures[0].r), 1, "Incorrect r for first signature"); - assertEq(uint256(parsedMsg.signatures[0].s), 2, "Incorrect s for first signature"); - assertEq(parsedMsg.signatures[0].v, 27, "Incorrect v for first signature"); + assertEq( + parsedMsg.signatures[0].validatorIndex, + 0, + "Incorrect validator index for first signature" + ); + assertEq( + uint256(parsedMsg.signatures[0].r), + 1, + "Incorrect r for first signature" + ); + assertEq( + uint256(parsedMsg.signatures[0].s), + 2, + "Incorrect s for first signature" + ); + assertEq( + parsedMsg.signatures[0].v, + 27, + "Incorrect v for first signature" + ); // Check second signature - assertEq(parsedMsg.signatures[1].validatorIndex, 1, "Incorrect validator index for second signature"); - assertEq(uint256(parsedMsg.signatures[1].r), 3, "Incorrect r for second signature"); - assertEq(uint256(parsedMsg.signatures[1].s), 4, "Incorrect s for second signature"); - assertEq(parsedMsg.signatures[1].v, 28, "Incorrect v for second signature"); + assertEq( + parsedMsg.signatures[1].validatorIndex, + 1, + "Incorrect validator index for second signature" + ); + assertEq( + uint256(parsedMsg.signatures[1].r), + 3, + "Incorrect r for second signature" + ); + assertEq( + uint256(parsedMsg.signatures[1].s), + 4, + "Incorrect s for second signature" + ); + assertEq( + parsedMsg.signatures[1].v, + 28, + "Incorrect v for second signature" + ); assertEq(parsedMsg.nonce, 1234, "Incorrect nonce"); assertEq(parsedMsg.timestamp, block.timestamp, "Incorrect timestamp"); assertEq(parsedMsg.emitterChainId, 5, "Incorrect emitter chain ID"); - assertEq(uint256(parsedMsg.emitterAddress), 6, "Incorrect emitter address"); - bytes32 domainHash = keccak256(abi.encodePacked(parsedMsg.emitterChainId, merkleTreeHookAddress, "HYPERLANE")); - bytes32 expectedHash = keccak256(abi.encodePacked(domainHash, root, checkpointIndex, messageId)); + assertEq( + uint256(parsedMsg.emitterAddress), + 6, + "Incorrect emitter address" + ); + bytes32 domainHash = keccak256( + abi.encodePacked( + parsedMsg.emitterChainId, + merkleTreeHookAddress, + "HYPERLANE" + ) + ); + bytes32 expectedHash = keccak256( + abi.encodePacked(domainHash, root, checkpointIndex, messageId) + ); assertEq(parsedMsg.hash, expectedHash, "Hash computation failed"); - assertEq(parsedMsg.payload, bytes("Hello, Hyperlane!"), "Incorrect payload"); + assertEq( + parsedMsg.payload, + bytes("Hello, Hyperlane!"), + "Incorrect payload" + ); } } diff --git a/solidity/test/utils/PragmaTestUtils.t.sol b/solidity/test/utils/PragmaTestUtils.t.sol index 5f64fe9..06708a1 100644 --- a/solidity/test/utils/PragmaTestUtils.t.sol +++ b/solidity/test/utils/PragmaTestUtils.t.sol @@ -11,7 +11,8 @@ import {DataFeedType} from "../../src/interfaces/IPragma.sol"; abstract contract PragmaTestUtils is Test, RandTestUtils, HyperlaneTestUtils { uint32 constant SOURCE_EMITTER_CHAIN_ID = 0x1; - bytes32 constant SOURCE_EMITTER_ADDRESS = 0x03dA250675D8c2BB7cef7E1b7FDFe17aA4D5752Ed82A9333e4F9a12b22E521aa; + bytes32 constant SOURCE_EMITTER_ADDRESS = + 0x03dA250675D8c2BB7cef7E1b7FDFe17aA4D5752Ed82A9333e4F9a12b22E521aa; uint256 constant SINGLE_UPDATE_FEE_IN_WEI = 1; uint256 constant VALID_TIME_PERIOD_IN_SECONDS = 60; @@ -64,11 +65,9 @@ abstract contract PragmaTestUtils is Test, RandTestUtils, HyperlaneTestUtils { bool brokenSignature; } - function encodeDataFeedMessages(DataFeedMessage[] memory dataFeedMessages) - internal - pure - returns (bytes[] memory encodedDataFeedMessages) - { + function encodeDataFeedMessages( + DataFeedMessage[] memory dataFeedMessages + ) internal pure returns (bytes[] memory encodedDataFeedMessages) { encodedDataFeedMessages = new bytes[](dataFeedMessages.length); for (uint256 i = 0; i < dataFeedMessages.length; i++) { @@ -87,9 +86,12 @@ abstract contract PragmaTestUtils is Test, RandTestUtils, HyperlaneTestUtils { DataFeedMessage[] memory dataFeedMessages, MerkleUpdateConfig memory config ) internal returns (bytes memory hyMerkleUpdateData) { - bytes[] memory encodedDataFeedMessages = encodeDataFeedMessages(dataFeedMessages); + bytes[] memory encodedDataFeedMessages = encodeDataFeedMessages( + dataFeedMessages + ); - (bytes32 rootDigest, bytes[] memory proofs) = MerkleTree.constructProofs(encodedDataFeedMessages, config.depth); + (bytes32 rootDigest, bytes[] memory proofs) = MerkleTree + .constructProofs(encodedDataFeedMessages, config.depth); bytes memory hyperlanePayload = abi.encodePacked(rootDigest); @@ -124,7 +126,10 @@ abstract contract PragmaTestUtils is Test, RandTestUtils, HyperlaneTestUtils { for (uint256 i = 0; i < dataFeedMessages.length; i++) { hyMerkleUpdateData = abi.encodePacked( - hyMerkleUpdateData, uint16(encodedDataFeedMessages[i].length), encodedDataFeedMessages[i], proofs[i] + hyMerkleUpdateData, + uint16(encodedDataFeedMessages[i].length), + encodedDataFeedMessages[i], + proofs[i] ); } } From 3533869741fd8037d1718220683c7c41ccf80486 Mon Sep 17 00:00:00 2001 From: JordyRo1 Date: Wed, 6 Nov 2024 13:08:12 +0100 Subject: [PATCH 4/6] fix: slither --- solidity/src/libraries/BytesLib.sol | 3 +-- solidity/src/libraries/UnsafeBytesLib.sol | 3 +-- solidity/src/migrations/Chainlink.sol | 11 ++++++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/solidity/src/libraries/BytesLib.sol b/solidity/src/libraries/BytesLib.sol index d4bca11..da01a64 100644 --- a/solidity/src/libraries/BytesLib.sol +++ b/solidity/src/libraries/BytesLib.sol @@ -469,8 +469,7 @@ library BytesLib { for { let cc := add(_postBytes, 0x20) - } // the next line is the loop condition: - // while(uint256(mc < end) + cb == 2) + } // while(uint256(mc < end) + cb == 2) // the next line is the loop condition: eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) diff --git a/solidity/src/libraries/UnsafeBytesLib.sol b/solidity/src/libraries/UnsafeBytesLib.sol index b36138e..cc16b1a 100644 --- a/solidity/src/libraries/UnsafeBytesLib.sol +++ b/solidity/src/libraries/UnsafeBytesLib.sol @@ -446,8 +446,7 @@ library UnsafeBytesLib { for { let cc := add(_postBytes, 0x20) - } // the next line is the loop condition: - // while(uint256(mc < end) + cb == 2) + } // while(uint256(mc < end) + cb == 2) // the next line is the loop condition: eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) diff --git a/solidity/src/migrations/Chainlink.sol b/solidity/src/migrations/Chainlink.sol index b3918fe..48849ae 100644 --- a/solidity/src/migrations/Chainlink.sol +++ b/solidity/src/migrations/Chainlink.sol @@ -28,7 +28,8 @@ contract PragmaAggregatorV3 { pragmaInterface.updateDataFeeds{value: fee}(priceUpdateData); // refund remaining eth - payable(msg.sender).call{value: address(this).balance}(""); + // (bool success, ) = payable(msg.sender).call{value: address(this).balance}(""); + // require(success, "Transfer failed."); } function decimals() public view virtual returns (uint8) { @@ -68,12 +69,12 @@ contract PragmaAggregatorV3 { } function getRoundData( - uint80 _roundId + uint80 roundId ) external view returns ( - uint80 roundId, + uint80, int256 answer, uint256 startedAt, uint256 updatedAt, @@ -82,11 +83,11 @@ contract PragmaAggregatorV3 { { SpotMedian memory price = pragmaInterface.getSpotMedianFeed(feedId); return ( - _roundId, + roundId, int256(price.price), price.metadata.timestamp, price.metadata.timestamp, - _roundId + roundId ); } From 99da002515d652dc2fa6078dbef67b895e1cc70d Mon Sep 17 00:00:00 2001 From: JordyRo1 Date: Wed, 6 Nov 2024 13:12:27 +0100 Subject: [PATCH 5/6] fix: slither v2 --- solidity/src/migrations/Chainlink.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solidity/src/migrations/Chainlink.sol b/solidity/src/migrations/Chainlink.sol index 48849ae..d8f19a4 100644 --- a/solidity/src/migrations/Chainlink.sol +++ b/solidity/src/migrations/Chainlink.sol @@ -14,7 +14,7 @@ import "../interfaces/IPragma.sol"; */ contract PragmaAggregatorV3 { bytes32 public feedId; - IPragma public pragmaInterface; + IPragma public immutable pragmaInterface; constructor(address _pragma, bytes32 _feedId) { feedId = _feedId; From 53717f16905b9ef97ab86873876ab6e580341c79 Mon Sep 17 00:00:00 2001 From: JordyRo1 Date: Wed, 6 Nov 2024 14:35:13 +0100 Subject: [PATCH 6/6] feat: pyth migration --- .prettierignore | 2 + solidity/src/Pragma.sol | 6 +- solidity/src/libraries/BytesLib.sol | 3 +- solidity/src/libraries/ErrorsLib.sol | 2 + solidity/src/libraries/UnsafeBytesLib.sol | 3 +- solidity/src/migrations/Chainlink.sol | 2 +- solidity/src/migrations/Pyth.sol | 102 ++++++++++++++++++++++ 7 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 solidity/src/migrations/Pyth.sol diff --git a/.prettierignore b/.prettierignore index 0a0061b..e722f82 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,4 @@ target openapi.json +solidity/lib +solidity/out \ No newline at end of file diff --git a/solidity/src/Pragma.sol b/solidity/src/Pragma.sol index 9cfb123..f85dda9 100644 --- a/solidity/src/Pragma.sol +++ b/solidity/src/Pragma.sol @@ -185,7 +185,11 @@ contract Pragma is function getSpotMedianFeed( bytes32 feedId ) external view returns (SpotMedian memory) { - return spotMedianFeeds[feedId]; + SpotMedian memory feed = spotMedianFeeds[feedId]; + if (feed.metadata.timestamp == 0) { + revert ErrorsLib.DataNotFound(); + } + return feed; } function withdrawFunds(uint256 amount) external onlyOwner { diff --git a/solidity/src/libraries/BytesLib.sol b/solidity/src/libraries/BytesLib.sol index da01a64..2e1611d 100644 --- a/solidity/src/libraries/BytesLib.sol +++ b/solidity/src/libraries/BytesLib.sol @@ -469,8 +469,7 @@ library BytesLib { for { let cc := add(_postBytes, 0x20) - } // while(uint256(mc < end) + cb == 2) // the next line is the loop condition: - eq(add(lt(mc, end), cb), 2) { + } eq(add(lt(mc, end), cb), 2) { // while(uint256(mc < end) + cb == 2) // the next line is the loop condition: mc := add(mc, 0x20) cc := add(cc, 0x20) } { diff --git a/solidity/src/libraries/ErrorsLib.sol b/solidity/src/libraries/ErrorsLib.sol index 15adc7d..5fdaa03 100644 --- a/solidity/src/libraries/ErrorsLib.sol +++ b/solidity/src/libraries/ErrorsLib.sol @@ -32,4 +32,6 @@ library ErrorsLib { // Data feed is stale. (e.g., not updated for a long time) // TODO: add signature error DataStale(); + // Parameters passed as arguments are incorrect + error InvalidArgument(); } diff --git a/solidity/src/libraries/UnsafeBytesLib.sol b/solidity/src/libraries/UnsafeBytesLib.sol index cc16b1a..18cf3f0 100644 --- a/solidity/src/libraries/UnsafeBytesLib.sol +++ b/solidity/src/libraries/UnsafeBytesLib.sol @@ -446,8 +446,7 @@ library UnsafeBytesLib { for { let cc := add(_postBytes, 0x20) - } // while(uint256(mc < end) + cb == 2) // the next line is the loop condition: - eq(add(lt(mc, end), cb), 2) { + } eq(add(lt(mc, end), cb), 2) { // while(uint256(mc < end) + cb == 2) // the next line is the loop condition: mc := add(mc, 0x20) cc := add(cc, 0x20) } { diff --git a/solidity/src/migrations/Chainlink.sol b/solidity/src/migrations/Chainlink.sol index d8f19a4..2fa24c0 100644 --- a/solidity/src/migrations/Chainlink.sol +++ b/solidity/src/migrations/Chainlink.sol @@ -29,7 +29,7 @@ contract PragmaAggregatorV3 { // refund remaining eth // (bool success, ) = payable(msg.sender).call{value: address(this).balance}(""); - // require(success, "Transfer failed."); + // require(success, "Transfer failed."); } function decimals() public view virtual returns (uint8) { diff --git a/solidity/src/migrations/Pyth.sol b/solidity/src/migrations/Pyth.sol new file mode 100644 index 0000000..369eb77 --- /dev/null +++ b/solidity/src/migrations/Pyth.sol @@ -0,0 +1,102 @@ +pragma solidity 0.8.28; + +// Replace this import with those down below once PR merged + +import "../interfaces/PragmaStructs.sol"; +import "../interfaces/IPragma.sol"; +import "../libraries/ErrorsLib.sol"; + +// import "@pragmaoracle/solidity-sdk/src/interfaces/IPragma.sol"; +// import "@pragmaoracle/solidity-sdk/src/interfaces/PragmaStructs.sol"; + +/** + * @title An adapter of the Pyth interface that supports Pragma price feeds + * Users should deploy an instance of this contract to wrap every price feed id that they need to use. + */ + +contract PragmaAggregatorV3 { + IPragma public immutable pragmaInterface; + + constructor(address _pragma) { + pragmaInterface = IPragma(_pragma); + } + + function getPriceUnsafe( + bytes32 id + ) public view returns (SpotMedian memory) { + SpotMedian memory feed = pragmaInterface.getSpotMedianFeed(id); + return feed; + } + + function getPriceNoOlderThan( + bytes32 id, + uint age + ) public view returns (SpotMedian memory) { + SpotMedian memory feed = getPriceUnsafe(id); + + if (diff(block.timestamp, feed.metadata.timestamp) > age) + revert ErrorsLib.DataStale(); + + return feed; + } + + function updatePriceFeeds(bytes[] calldata updateData) public payable { + pragmaInterface.updateDataFeeds(updateData); + } + + function getUpdateFee( + bytes[] calldata updateData + ) external view returns (uint feeAmount) { + return pragmaInterface.getUpdateFee(updateData); + } + + function updatePriceFeedsIfNecessary( + bytes[] calldata updateData, + bytes32[] calldata feedIds, + uint64[] calldata publishTimes + ) external payable { + if (feedIds.length != publishTimes.length) + revert ErrorsLib.InvalidArgument(); + + for (uint i = 0; i < feedIds.length; i++) { + if ( + !pragmaInterface.dataFeedExists(feedIds[i]) || + pragmaInterface + .getSpotMedianFeed(feedIds[i]) + .metadata + .timestamp < + publishTimes[i] + ) { + updatePriceFeeds(updateData); + return; + } + } + revert ErrorsLib.DataStale(); + } + + // function parsePriceFeedUpdates( + // bytes[] calldata updateData, + // bytes32[] calldata priceIds, + // uint64 minPublishTime, + // uint64 maxPublishTime + // ) external payable returns (SpotMedian[] memory priceFeeds){ + + // } + + // function parsePriceFeedUpdatesUnique( + // bytes[] calldata updateData, + // bytes32[] calldata priceIds, + // uint64 minPublishTime, + // uint64 maxPublishTime + // ) external payable returns (SpotMedian[] memory priceFeeds) { + + // } + + function diff(uint x, uint y) internal pure returns (uint) { + if (x > y) { + return x - y; + } else { + return y - x; + } + } +}