From 22fce48a87b35e7f3fb923296a030d8544640677 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 10:20:45 +0100 Subject: [PATCH 01/31] feat: redstone lib Signed-off-by: Pablo Maldonado --- .../v0.8/interfaces/AggregatorV3Interface.sol | 32 ++ .../proxy/utils/Initializable.sol | 165 ++++++++ .../utils/AddressUpgradeable.sol | 219 ++++++++++ .../contracts/utils/math/SafeMath.sol | 227 +++++++++++ .../contracts/core/CalldataExtractor.sol | 163 ++++++++ .../contracts/core/ProxyConnector.sol | 141 +++++++ .../contracts/core/RedstoneConstants.sol | 52 +++ .../contracts/core/RedstoneConsumerBase.sol | 310 ++++++++++++++ .../core/RedstoneConsumerBytesBase.sol | 217 ++++++++++ .../core/RedstoneConsumerNumericBase.sol | 112 ++++++ .../contracts/core/RedstoneDefaultsLib.sol | 39 ++ .../contracts/libs/BitmapLib.sol | 14 + .../contracts/libs/HardhatLoggerLib.sol | 14 + .../contracts/libs/NumbersLib.sol | 15 + .../contracts/libs/NumericArrayLib.sol | 56 +++ .../contracts/libs/SignatureLib.sol | 27 ++ .../contracts/core/IRedstoneAdapter.sol | 89 +++++ .../contracts/core/RedstoneAdapterBase.sol | 377 ++++++++++++++++++ .../MergedPriceFeedAdapterCommon.sol | 18 + .../contracts/price-feeds/PriceFeedBase.sol | 133 ++++++ .../price-feeds/PriceFeedsAdapterBase.sol | 46 +++ ...dPriceFeedAdapterWithRoundsPrimaryProd.sol | 24 ++ ...iceFeedAdapterWithoutRoundsPrimaryProd.sol | 24 ++ .../PriceFeedsAdapterWithRoundsMainDemo.sol | 21 + ...PriceFeedsAdapterWithoutRoundsMainDemo.sol | 25 ++ .../data-services/VSTPriceFeedsAdapter.sol | 51 +++ ...PriceFeedsAdapterWithoutRoundsMainDemo.sol | 29 ++ .../price-feeds/interfaces/IPriceFeed.sol | 20 + .../interfaces/IPriceFeedLegacy.sol | 24 ++ .../MergedPriceFeedAdapterWithRounds.sol | 52 +++ .../with-rounds/PriceFeedWithRounds.sol | 62 +++ .../PriceFeedsAdapterWithRounds.sol | 183 +++++++++ .../MergedPriceFeedAdapterWithoutRounds.sol | 53 +++ .../without-rounds/PriceFeedWithoutRounds.sol | 36 ++ .../PriceFeedsAdapterWithoutRounds.sol | 57 +++ .../without-rounds/SinglePriceFeedAdapter.sol | 144 +++++++ .../SinglePriceFeedAdapterWithClearing.sol | 72 ++++ 37 files changed, 3343 insertions(+) create mode 100644 lib/RedStoneBaseContracts/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol create mode 100644 lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol create mode 100644 lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol create mode 100644 lib/RedStoneBaseContracts/@openzeppelin/contracts/utils/math/SafeMath.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/ProxyConnector.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConstants.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneDefaultsLib.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/BitmapLib.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/HardhatLoggerLib.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumbersLib.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol create mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/SignatureLib.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/IRedstoneAdapter.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/RedstoneAdapterBase.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/MergedPriceFeedAdapterCommon.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithRoundsPrimaryProd.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithoutRoundsPrimaryProd.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithRoundsMainDemo.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithoutRoundsMainDemo.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapter.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapterWithoutRoundsMainDemo.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeed.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeedLegacy.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedWithRounds.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedsAdapterWithRounds.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/MergedPriceFeedAdapterWithoutRounds.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedWithoutRounds.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedsAdapterWithoutRounds.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapter.sol create mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapterWithClearing.sol diff --git a/lib/RedStoneBaseContracts/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol b/lib/RedStoneBaseContracts/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol new file mode 100644 index 0000000..cd72a0c --- /dev/null +++ b/lib/RedStoneBaseContracts/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface AggregatorV3Interface { + function decimals() external view returns (uint8); + + function description() external view returns (string memory); + + function version() external view returns (uint256); + + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); + + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); +} diff --git a/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol b/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol new file mode 100644 index 0000000..111d8d1 --- /dev/null +++ b/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol) + +pragma solidity ^0.8.2; + +import "../../utils/AddressUpgradeable.sol"; + +/** + * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed + * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an + * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer + * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. + * + * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be + * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in + * case an upgrade adds a module that needs to be initialized. + * + * For example: + * + * [.hljs-theme-light.nopadding] + * ``` + * contract MyToken is ERC20Upgradeable { + * function initialize() initializer public { + * __ERC20_init("MyToken", "MTK"); + * } + * } + * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { + * function initializeV2() reinitializer(2) public { + * __ERC20Permit_init("MyToken"); + * } + * } + * ``` + * + * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as + * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. + * + * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure + * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. + * + * [CAUTION] + * ==== + * Avoid leaving a contract uninitialized. + * + * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation + * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke + * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: + * + * [.hljs-theme-light.nopadding] + * ``` + * /// @custom:oz-upgrades-unsafe-allow constructor + * constructor() { + * _disableInitializers(); + * } + * ``` + * ==== + */ +abstract contract Initializable { + /** + * @dev Indicates that the contract has been initialized. + * @custom:oz-retyped-from bool + */ + uint8 private _initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private _initializing; + + /** + * @dev Triggered when the contract has been initialized or reinitialized. + */ + event Initialized(uint8 version); + + /** + * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, + * `onlyInitializing` functions can be used to initialize parent contracts. + * + * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a + * constructor. + * + * Emits an {Initialized} event. + */ + modifier initializer() { + bool isTopLevelCall = !_initializing; + require( + (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), + "Initializable: contract is already initialized" + ); + _initialized = 1; + if (isTopLevelCall) { + _initializing = true; + } + _; + if (isTopLevelCall) { + _initializing = false; + emit Initialized(1); + } + } + + /** + * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the + * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be + * used to initialize parent contracts. + * + * A reinitializer may be used after the original initialization step. This is essential to configure modules that + * are added through upgrades and that require initialization. + * + * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` + * cannot be nested. If one is invoked in the context of another, execution will revert. + * + * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in + * a contract, executing them in the right order is up to the developer or operator. + * + * WARNING: setting the version to 255 will prevent any future reinitialization. + * + * Emits an {Initialized} event. + */ + modifier reinitializer(uint8 version) { + require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); + _initialized = version; + _initializing = true; + _; + _initializing = false; + emit Initialized(version); + } + + /** + * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the + * {initializer} and {reinitializer} modifiers, directly or indirectly. + */ + modifier onlyInitializing() { + require(_initializing, "Initializable: contract is not initializing"); + _; + } + + /** + * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. + * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized + * to any version. It is recommended to use this to lock implementation contracts that are designed to be called + * through proxies. + * + * Emits an {Initialized} event the first time it is successfully executed. + */ + function _disableInitializers() internal virtual { + require(!_initializing, "Initializable: contract is initializing"); + if (_initialized < type(uint8).max) { + _initialized = type(uint8).max; + emit Initialized(type(uint8).max); + } + } + + /** + * @dev Returns the highest version that has been initialized. See {reinitializer}. + */ + function _getInitializedVersion() internal view returns (uint8) { + return _initialized; + } + + /** + * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. + */ + function _isInitializing() internal view returns (bool) { + return _initializing; + } +} diff --git a/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol b/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol new file mode 100644 index 0000000..203c05f --- /dev/null +++ b/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) + +pragma solidity ^0.8.1; + +/** + * @dev Collection of functions related to the address type + */ +library AddressUpgradeable { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + * + * [IMPORTANT] + * ==== + * You shouldn't rely on `isContract` to protect against flash loan attacks! + * + * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets + * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract + * constructor. + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize/address.code.length, which returns 0 + // for contracts in construction, since the code is only stored at the end + // of the constructor execution. + + return account.code.length > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + (bool success, ) = recipient.call{value: amount}(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + (bool success, bytes memory returndata) = target.call{value: value}(data); + return verifyCallResultFromTarget(target, success, returndata, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + (bool success, bytes memory returndata) = target.staticcall(data); + return verifyCallResultFromTarget(target, success, returndata, errorMessage); + } + + /** + * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling + * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. + * + * _Available since v4.8._ + */ + function verifyCallResultFromTarget( + address target, + bool success, + bytes memory returndata, + string memory errorMessage + ) internal view returns (bytes memory) { + if (success) { + if (returndata.length == 0) { + // only check isContract if the call was successful and the return data is empty + // otherwise we already know that it was a contract + require(isContract(target), "Address: call to non-contract"); + } + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + /** + * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the + * revert reason or using the provided one. + * + * _Available since v4.3._ + */ + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + function _revert(bytes memory returndata, string memory errorMessage) private pure { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + /// @solidity memory-safe-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } +} diff --git a/lib/RedStoneBaseContracts/@openzeppelin/contracts/utils/math/SafeMath.sol b/lib/RedStoneBaseContracts/@openzeppelin/contracts/utils/math/SafeMath.sol new file mode 100644 index 0000000..550f0e7 --- /dev/null +++ b/lib/RedStoneBaseContracts/@openzeppelin/contracts/utils/math/SafeMath.sol @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) + +pragma solidity ^0.8.0; + +// CAUTION +// This version of SafeMath should only be used with Solidity 0.8 or later, +// because it relies on the compiler's built in overflow checks. + +/** + * @dev Wrappers over Solidity's arithmetic operations. + * + * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler + * now has built in overflow checking. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + uint256 c = a + b; + if (c < a) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the subtraction of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b > a) return (false, 0); + return (true, a - b); + } + } + + /** + * @dev Returns the multiplication of two unsigned integers, with an overflow flag. + * + * _Available since v3.4._ + */ + function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) return (true, 0); + uint256 c = a * b; + if (c / a != b) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the division of two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a / b); + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. + * + * _Available since v3.4._ + */ + function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a % b); + } + } + + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + return a + b; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return a - b; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + return a * b; + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + return a / b; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + return a % b; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {trySub}. + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b <= a, errorMessage); + return a - b; + } + } + + /** + * @dev Returns the integer division of two unsigned integers, reverting with custom message on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b > 0, errorMessage); + return a / b; + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * reverting with custom message when dividing by zero. + * + * CAUTION: This function is deprecated because it requires allocating memory for the error + * message unnecessarily. For custom revert reasons use {tryMod}. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + unchecked { + require(b > 0, errorMessage); + return a % b; + } + } +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol new file mode 100644 index 0000000..f5804ac --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.4; + +import "../../../../@openzeppelin/contracts/utils/math/SafeMath.sol"; + +import "./RedstoneConstants.sol"; + +/** + * @title The base contract with the main logic of data extraction from calldata + * @author The Redstone Oracles team + * @dev This contract was created to reuse the same logic in the RedstoneConsumerBase + * and the ProxyConnector contracts + */ +contract CalldataExtractor is RedstoneConstants { + using SafeMath for uint256; + + error DataPackageTimestampMustNotBeZero(); + error DataPackageTimestampsMustBeEqual(); + error RedstonePayloadMustHaveAtLeastOneDataPackage(); + + function extractTimestampsAndAssertAllAreEqual() public pure returns (uint256 extractedTimestamp) { + uint256 calldataNegativeOffset = _extractByteSizeOfUnsignedMetadata(); + uint256 dataPackagesCount = _extractDataPackagesCountFromCalldata(calldataNegativeOffset); + + if (dataPackagesCount == 0) { + revert RedstonePayloadMustHaveAtLeastOneDataPackage(); + } + + calldataNegativeOffset += DATA_PACKAGES_COUNT_BS; + for (uint256 dataPackageIndex = 0; dataPackageIndex < dataPackagesCount; dataPackageIndex++) { + uint256 dataPackageByteSize = _getDataPackageByteSize(calldataNegativeOffset); + + // Extracting timestamp for the current data package + uint48 dataPackageTimestamp; // uint48, because timestamp uses 6 bytes + uint256 timestampNegativeOffset = (calldataNegativeOffset + TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS); + uint256 timestampOffset = msg.data.length - timestampNegativeOffset; + assembly { + dataPackageTimestamp := calldataload(timestampOffset) + } + + if (dataPackageTimestamp == 0) { + revert DataPackageTimestampMustNotBeZero(); + } + + if (extractedTimestamp == 0) { + extractedTimestamp = dataPackageTimestamp; + } else if (dataPackageTimestamp != extractedTimestamp) { + revert DataPackageTimestampsMustBeEqual(); + } + + calldataNegativeOffset += dataPackageByteSize; + } + } + + function _getDataPackageByteSize(uint256 calldataNegativeOffset) internal pure returns (uint256) { + ( + uint256 dataPointsCount, + uint256 eachDataPointValueByteSize + ) = _extractDataPointsDetailsForDataPackage(calldataNegativeOffset); + + return + dataPointsCount * + (DATA_POINT_SYMBOL_BS + eachDataPointValueByteSize) + + DATA_PACKAGE_WITHOUT_DATA_POINTS_BS; + } + + function _extractByteSizeOfUnsignedMetadata() internal pure returns (uint256) { + // Checking if the calldata ends with the RedStone marker + bool hasValidRedstoneMarker; + assembly { + let calldataLast32Bytes := calldataload(sub(calldatasize(), STANDARD_SLOT_BS)) + hasValidRedstoneMarker := eq( + REDSTONE_MARKER_MASK, + and(calldataLast32Bytes, REDSTONE_MARKER_MASK) + ) + } + if (!hasValidRedstoneMarker) { + revert CalldataMustHaveValidPayload(); + } + + // Using uint24, because unsigned metadata byte size number has 3 bytes + uint24 unsignedMetadataByteSize; + if (REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS > msg.data.length) { + revert CalldataOverOrUnderFlow(); + } + assembly { + unsignedMetadataByteSize := calldataload( + sub(calldatasize(), REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS) + ) + } + uint256 calldataNegativeOffset = unsignedMetadataByteSize + + UNSIGNED_METADATA_BYTE_SIZE_BS + + REDSTONE_MARKER_BS; + if (calldataNegativeOffset + DATA_PACKAGES_COUNT_BS > msg.data.length) { + revert IncorrectUnsignedMetadataSize(); + } + return calldataNegativeOffset; + } + + // We return uint16, because unsigned metadata byte size number has 2 bytes + function _extractDataPackagesCountFromCalldata(uint256 calldataNegativeOffset) + internal + pure + returns (uint16 dataPackagesCount) + { + uint256 calldataNegativeOffsetWithStandardSlot = calldataNegativeOffset + STANDARD_SLOT_BS; + if (calldataNegativeOffsetWithStandardSlot > msg.data.length) { + revert CalldataOverOrUnderFlow(); + } + assembly { + dataPackagesCount := calldataload( + sub(calldatasize(), calldataNegativeOffsetWithStandardSlot) + ) + } + return dataPackagesCount; + } + + function _extractDataPointValueAndDataFeedId( + uint256 calldataNegativeOffsetForDataPackage, + uint256 defaultDataPointValueByteSize, + uint256 dataPointIndex + ) internal pure virtual returns (bytes32 dataPointDataFeedId, uint256 dataPointValue) { + uint256 negativeOffsetToDataPoints = calldataNegativeOffsetForDataPackage + DATA_PACKAGE_WITHOUT_DATA_POINTS_BS; + uint256 dataPointNegativeOffset = negativeOffsetToDataPoints.add( + (1 + dataPointIndex).mul((defaultDataPointValueByteSize + DATA_POINT_SYMBOL_BS)) + ); + uint256 dataPointCalldataOffset = msg.data.length.sub(dataPointNegativeOffset); + assembly { + dataPointDataFeedId := calldataload(dataPointCalldataOffset) + dataPointValue := calldataload(add(dataPointCalldataOffset, DATA_POINT_SYMBOL_BS)) + } + } + + function _extractDataPointsDetailsForDataPackage(uint256 calldataNegativeOffsetForDataPackage) + internal + pure + returns (uint256 dataPointsCount, uint256 eachDataPointValueByteSize) + { + // Using uint24, because data points count byte size number has 3 bytes + uint24 dataPointsCount_; + + // Using uint32, because data point value byte size has 4 bytes + uint32 eachDataPointValueByteSize_; + + // Extract data points count + uint256 negativeCalldataOffset = calldataNegativeOffsetForDataPackage + SIG_BS; + uint256 calldataOffset = msg.data.length.sub(negativeCalldataOffset + STANDARD_SLOT_BS); + assembly { + dataPointsCount_ := calldataload(calldataOffset) + } + + // Extract each data point value size + calldataOffset = calldataOffset.sub(DATA_POINTS_COUNT_BS); + assembly { + eachDataPointValueByteSize_ := calldataload(calldataOffset) + } + + // Prepare returned values + dataPointsCount = dataPointsCount_; + eachDataPointValueByteSize = eachDataPointValueByteSize_; + } +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/ProxyConnector.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/ProxyConnector.sol new file mode 100644 index 0000000..8a3ec87 --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/ProxyConnector.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.4; + +import "./RedstoneConstants.sol"; +import "./CalldataExtractor.sol"; + +/** + * @title The base contract for forwarding redstone payload to other contracts + * @author The Redstone Oracles team + */ +contract ProxyConnector is RedstoneConstants, CalldataExtractor { + error ProxyCalldataFailedWithoutErrMsg(); + error ProxyCalldataFailedWithStringMessage(string message); + error ProxyCalldataFailedWithCustomError(bytes result); + + function proxyCalldata( + address contractAddress, + bytes memory encodedFunction, + bool forwardValue + ) internal returns (bytes memory) { + bytes memory message = _prepareMessage(encodedFunction); + + (bool success, bytes memory result) = + contractAddress.call{value: forwardValue ? msg.value : 0}(message); + + return _prepareReturnValue(success, result); + } + + function proxyDelegateCalldata(address contractAddress, bytes memory encodedFunction) + internal + returns (bytes memory) + { + bytes memory message = _prepareMessage(encodedFunction); + (bool success, bytes memory result) = contractAddress.delegatecall(message); + return _prepareReturnValue(success, result); + } + + function proxyCalldataView(address contractAddress, bytes memory encodedFunction) + internal + view + returns (bytes memory) + { + bytes memory message = _prepareMessage(encodedFunction); + (bool success, bytes memory result) = contractAddress.staticcall(message); + return _prepareReturnValue(success, result); + } + + function _prepareMessage(bytes memory encodedFunction) private pure returns (bytes memory) { + uint256 encodedFunctionBytesCount = encodedFunction.length; + uint256 redstonePayloadByteSize = _getRedstonePayloadByteSize(); + uint256 resultMessageByteSize = encodedFunctionBytesCount + redstonePayloadByteSize; + + if (redstonePayloadByteSize > msg.data.length) { + revert CalldataOverOrUnderFlow(); + } + + bytes memory message; + + assembly { + message := mload(FREE_MEMORY_PTR) // sets message pointer to first free place in memory + + // Saving the byte size of the result message (it's a standard in EVM) + mstore(message, resultMessageByteSize) + + // Copying function and its arguments + for { + let from := add(BYTES_ARR_LEN_VAR_BS, encodedFunction) + let fromEnd := add(from, encodedFunctionBytesCount) + let to := add(BYTES_ARR_LEN_VAR_BS, message) + } lt (from, fromEnd) { + from := add(from, STANDARD_SLOT_BS) + to := add(to, STANDARD_SLOT_BS) + } { + // Copying data from encodedFunction to message (32 bytes at a time) + mstore(to, mload(from)) + } + + // Copying redstone payload to the message bytes + calldatacopy( + add(message, add(BYTES_ARR_LEN_VAR_BS, encodedFunctionBytesCount)), // address + sub(calldatasize(), redstonePayloadByteSize), // offset + redstonePayloadByteSize // bytes length to copy + ) + + // Updating free memory pointer + mstore( + FREE_MEMORY_PTR, + add( + add(message, add(redstonePayloadByteSize, encodedFunctionBytesCount)), + BYTES_ARR_LEN_VAR_BS + ) + ) + } + + return message; + } + + function _getRedstonePayloadByteSize() private pure returns (uint256) { + uint256 calldataNegativeOffset = _extractByteSizeOfUnsignedMetadata(); + uint256 dataPackagesCount = _extractDataPackagesCountFromCalldata(calldataNegativeOffset); + calldataNegativeOffset += DATA_PACKAGES_COUNT_BS; + for (uint256 dataPackageIndex = 0; dataPackageIndex < dataPackagesCount; dataPackageIndex++) { + uint256 dataPackageByteSize = _getDataPackageByteSize(calldataNegativeOffset); + calldataNegativeOffset += dataPackageByteSize; + } + + return calldataNegativeOffset; + } + + function _prepareReturnValue(bool success, bytes memory result) + internal + pure + returns (bytes memory) + { + if (!success) { + + if (result.length == 0) { + revert ProxyCalldataFailedWithoutErrMsg(); + } else { + bool isStringErrorMessage; + assembly { + let first32BytesOfResult := mload(add(result, BYTES_ARR_LEN_VAR_BS)) + isStringErrorMessage := eq(first32BytesOfResult, STRING_ERR_MESSAGE_MASK) + } + + if (isStringErrorMessage) { + string memory receivedErrMsg; + assembly { + receivedErrMsg := add(result, REVERT_MSG_OFFSET) + } + revert ProxyCalldataFailedWithStringMessage(receivedErrMsg); + } else { + revert ProxyCalldataFailedWithCustomError(result); + } + } + } + + return result; + } +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConstants.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConstants.sol new file mode 100644 index 0000000..ad4bf2f --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConstants.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.4; + +/** + * @title The base contract with helpful constants + * @author The Redstone Oracles team + * @dev It mainly contains redstone-related values, which improve readability + * of other contracts (e.g. CalldataExtractor and RedstoneConsumerBase) + */ +contract RedstoneConstants { + // === Abbreviations === + // BS - Bytes size + // PTR - Pointer (memory location) + // SIG - Signature + + // Solidity and YUL constants + uint256 internal constant STANDARD_SLOT_BS = 32; + uint256 internal constant FREE_MEMORY_PTR = 0x40; + uint256 internal constant BYTES_ARR_LEN_VAR_BS = 32; + uint256 internal constant FUNCTION_SIGNATURE_BS = 4; + uint256 internal constant REVERT_MSG_OFFSET = 68; // Revert message structure described here: https://ethereum.stackexchange.com/a/66173/106364 + uint256 internal constant STRING_ERR_MESSAGE_MASK = 0x08c379a000000000000000000000000000000000000000000000000000000000; + + // RedStone protocol consts + uint256 internal constant SIG_BS = 65; + uint256 internal constant TIMESTAMP_BS = 6; + uint256 internal constant DATA_PACKAGES_COUNT_BS = 2; + uint256 internal constant DATA_POINTS_COUNT_BS = 3; + uint256 internal constant DATA_POINT_VALUE_BYTE_SIZE_BS = 4; + uint256 internal constant DATA_POINT_SYMBOL_BS = 32; + uint256 internal constant DEFAULT_DATA_POINT_VALUE_BS = 32; + uint256 internal constant UNSIGNED_METADATA_BYTE_SIZE_BS = 3; + uint256 internal constant REDSTONE_MARKER_BS = 9; // byte size of 0x000002ed57011e0000 + uint256 internal constant REDSTONE_MARKER_MASK = 0x0000000000000000000000000000000000000000000000000002ed57011e0000; + + // Derived values (based on consts) + uint256 internal constant TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS = 104; // SIG_BS + DATA_POINTS_COUNT_BS + DATA_POINT_VALUE_BYTE_SIZE_BS + STANDARD_SLOT_BS + uint256 internal constant DATA_PACKAGE_WITHOUT_DATA_POINTS_BS = 78; // DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS + SIG_BS + uint256 internal constant DATA_PACKAGE_WITHOUT_DATA_POINTS_AND_SIG_BS = 13; // DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS + uint256 internal constant REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS = 41; // REDSTONE_MARKER_BS + STANDARD_SLOT_BS + + // Error messages + error CalldataOverOrUnderFlow(); + error IncorrectUnsignedMetadataSize(); + error InsufficientNumberOfUniqueSigners(uint256 receivedSignersCount, uint256 requiredSignersCount); + error EachSignerMustProvideTheSameValue(); + error EmptyCalldataPointersArr(); + error InvalidCalldataPointer(); + error CalldataMustHaveValidPayload(); + error SignerNotAuthorised(address receivedSigner); +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol new file mode 100644 index 0000000..d779b26 --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.4; + +import "../../../../@openzeppelin/contracts/utils/math/SafeMath.sol"; + +import "./RedstoneConstants.sol"; +import "./RedstoneDefaultsLib.sol"; +import "./CalldataExtractor.sol"; +import "../libs/BitmapLib.sol"; +import "../libs/SignatureLib.sol"; + +/** + * @title The base contract with the main Redstone logic + * @author The Redstone Oracles team + * @dev Do not use this contract directly in consumer contracts, take a + * look at `RedstoneConsumerNumericBase` and `RedstoneConsumerBytesBase` instead + */ +abstract contract RedstoneConsumerBase is CalldataExtractor { + using SafeMath for uint256; + + error GetDataServiceIdNotImplemented(); + + /* ========== VIRTUAL FUNCTIONS (MAY BE OVERRIDDEN IN CHILD CONTRACTS) ========== */ + + /** + * @dev This function must be implemented by the child consumer contract. + * It should return dataServiceId which DataServiceWrapper will use if not provided explicitly . + * If not overridden, value will always have to be provided explicitly in DataServiceWrapper. + * @return dataServiceId being consumed by contract + */ + function getDataServiceId() public view virtual returns (string memory) { + revert GetDataServiceIdNotImplemented(); + } + + /** + * @dev This function must be implemented by the child consumer contract. + * It should return a unique index for a given signer address if the signer + * is authorised, otherwise it should revert + * @param receivedSigner The address of a signer, recovered from ECDSA signature + * @return Unique index for a signer in the range [0..255] + */ + function getAuthorisedSignerIndex(address receivedSigner) public view virtual returns (uint8); + + /** + * @dev This function may be overridden by the child consumer contract. + * It should validate the timestamp against the current time (block.timestamp) + * It should revert with a helpful message if the timestamp is not valid + * @param receivedTimestampMilliseconds Timestamp extracted from calldata + */ + function validateTimestamp(uint256 receivedTimestampMilliseconds) public view virtual { + RedstoneDefaultsLib.validateTimestamp(receivedTimestampMilliseconds); + } + + /** + * @dev This function should be overridden by the child consumer contract. + * @return The minimum required value of unique authorised signers + */ + function getUniqueSignersThreshold() public view virtual returns (uint8) { + return 1; + } + + /** + * @dev This function may be overridden by the child consumer contract. + * It should aggregate values from different signers to a single uint value. + * By default, it calculates the median value + * @param values An array of uint256 values from different signers + * @return Result of the aggregation in the form of a single number + */ + function aggregateValues(uint256[] memory values) public view virtual returns (uint256) { + return RedstoneDefaultsLib.aggregateValues(values); + } + + /* ========== FUNCTIONS WITH IMPLEMENTATION (CAN NOT BE OVERRIDDEN) ========== */ + + /** + * @dev This is an internal helpful function for secure extraction oracle values + * from the tx calldata. Security is achieved by signatures verification, timestamp + * validation, and aggregating values from different authorised signers into a + * single numeric value. If any of the required conditions (e.g. too old timestamp or + * insufficient number of authorised signers) do not match, the function will revert. + * + * Note! You should not call this function in a consumer contract. You can use + * `getOracleNumericValuesFromTxMsg` or `getOracleNumericValueFromTxMsg` instead. + * + * @param dataFeedIds An array of unique data feed identifiers + * @return An array of the extracted and verified oracle values in the same order + * as they are requested in dataFeedIds array + */ + function _securelyExtractOracleValuesFromTxMsg(bytes32[] memory dataFeedIds) + internal + view + returns (uint256[] memory) + { + // Initializing helpful variables and allocating memory + uint256[] memory uniqueSignerCountForDataFeedIds = new uint256[](dataFeedIds.length); + uint256[] memory signersBitmapForDataFeedIds = new uint256[](dataFeedIds.length); + uint256[][] memory valuesForDataFeeds = new uint256[][](dataFeedIds.length); + for (uint256 i = 0; i < dataFeedIds.length; i++) { + // The line below is commented because newly allocated arrays are filled with zeros + // But we left it for better readability + // signersBitmapForDataFeedIds[i] = 0; // <- setting to an empty bitmap + valuesForDataFeeds[i] = new uint256[](getUniqueSignersThreshold()); + } + + // Extracting the number of data packages from calldata + uint256 calldataNegativeOffset = _extractByteSizeOfUnsignedMetadata(); + uint256 dataPackagesCount = _extractDataPackagesCountFromCalldata(calldataNegativeOffset); + calldataNegativeOffset += DATA_PACKAGES_COUNT_BS; + + // Saving current free memory pointer + uint256 freeMemPtr; + assembly { + freeMemPtr := mload(FREE_MEMORY_PTR) + } + + // Data packages extraction in a loop + for (uint256 dataPackageIndex = 0; dataPackageIndex < dataPackagesCount; dataPackageIndex++) { + // Extract data package details and update calldata offset + uint256 dataPackageByteSize = _extractDataPackage( + dataFeedIds, + uniqueSignerCountForDataFeedIds, + signersBitmapForDataFeedIds, + valuesForDataFeeds, + calldataNegativeOffset + ); + calldataNegativeOffset += dataPackageByteSize; + + // Shifting memory pointer back to the "safe" value + assembly { + mstore(FREE_MEMORY_PTR, freeMemPtr) + } + } + + // Validating numbers of unique signers and calculating aggregated values for each dataFeedId + return _getAggregatedValues(valuesForDataFeeds, uniqueSignerCountForDataFeedIds); + } + + /** + * @dev This is a private helpful function, which extracts data for a data package based + * on the given negative calldata offset, verifies them, and in the case of successful + * verification updates the corresponding data package values in memory + * + * @param dataFeedIds an array of unique data feed identifiers + * @param uniqueSignerCountForDataFeedIds an array with the numbers of unique signers + * for each data feed + * @param signersBitmapForDataFeedIds an array of signer bitmaps for data feeds + * @param valuesForDataFeeds 2-dimensional array, valuesForDataFeeds[i][j] contains + * j-th value for the i-th data feed + * @param calldataNegativeOffset negative calldata offset for the given data package + * + * @return An array of the aggregated values + */ + function _extractDataPackage( + bytes32[] memory dataFeedIds, + uint256[] memory uniqueSignerCountForDataFeedIds, + uint256[] memory signersBitmapForDataFeedIds, + uint256[][] memory valuesForDataFeeds, + uint256 calldataNegativeOffset + ) private view returns (uint256) { + uint256 signerIndex; + + ( + uint256 dataPointsCount, + uint256 eachDataPointValueByteSize + ) = _extractDataPointsDetailsForDataPackage(calldataNegativeOffset); + + // We use scopes to resolve problem with too deep stack + { + uint48 extractedTimestamp; + address signerAddress; + bytes32 signedHash; + bytes memory signedMessage; + uint256 signedMessageBytesCount; + + signedMessageBytesCount = dataPointsCount.mul(eachDataPointValueByteSize + DATA_POINT_SYMBOL_BS) + + DATA_PACKAGE_WITHOUT_DATA_POINTS_AND_SIG_BS; //DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS + + uint256 timestampCalldataOffset = msg.data.length.sub( + calldataNegativeOffset + TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS); + + uint256 signedMessageCalldataOffset = msg.data.length.sub( + calldataNegativeOffset + SIG_BS + signedMessageBytesCount); + + assembly { + // Extracting the signed message + signedMessage := extractBytesFromCalldata( + signedMessageCalldataOffset, + signedMessageBytesCount + ) + + // Hashing the signed message + signedHash := keccak256(add(signedMessage, BYTES_ARR_LEN_VAR_BS), signedMessageBytesCount) + + // Extracting timestamp + extractedTimestamp := calldataload(timestampCalldataOffset) + + function initByteArray(bytesCount) -> ptr { + ptr := mload(FREE_MEMORY_PTR) + mstore(ptr, bytesCount) + ptr := add(ptr, BYTES_ARR_LEN_VAR_BS) + mstore(FREE_MEMORY_PTR, add(ptr, bytesCount)) + } + + function extractBytesFromCalldata(offset, bytesCount) -> extractedBytes { + let extractedBytesStartPtr := initByteArray(bytesCount) + calldatacopy( + extractedBytesStartPtr, + offset, + bytesCount + ) + extractedBytes := sub(extractedBytesStartPtr, BYTES_ARR_LEN_VAR_BS) + } + } + + // Validating timestamp + validateTimestamp(extractedTimestamp); + + // Verifying the off-chain signature against on-chain hashed data + signerAddress = SignatureLib.recoverSignerAddress( + signedHash, + calldataNegativeOffset + SIG_BS + ); + signerIndex = getAuthorisedSignerIndex(signerAddress); + } + + // Updating helpful arrays + { + bytes32 dataPointDataFeedId; + uint256 dataPointValue; + for (uint256 dataPointIndex = 0; dataPointIndex < dataPointsCount; dataPointIndex++) { + // Extracting data feed id and value for the current data point + (dataPointDataFeedId, dataPointValue) = _extractDataPointValueAndDataFeedId( + calldataNegativeOffset, + eachDataPointValueByteSize, + dataPointIndex + ); + + for ( + uint256 dataFeedIdIndex = 0; + dataFeedIdIndex < dataFeedIds.length; + dataFeedIdIndex++ + ) { + if (dataPointDataFeedId == dataFeedIds[dataFeedIdIndex]) { + uint256 bitmapSignersForDataFeedId = signersBitmapForDataFeedIds[dataFeedIdIndex]; + + if ( + !BitmapLib.getBitFromBitmap(bitmapSignersForDataFeedId, signerIndex) && /* current signer was not counted for current dataFeedId */ + uniqueSignerCountForDataFeedIds[dataFeedIdIndex] < getUniqueSignersThreshold() + ) { + // Increase unique signer counter + uniqueSignerCountForDataFeedIds[dataFeedIdIndex]++; + + // Add new value + valuesForDataFeeds[dataFeedIdIndex][ + uniqueSignerCountForDataFeedIds[dataFeedIdIndex] - 1 + ] = dataPointValue; + + // Update signers bitmap + signersBitmapForDataFeedIds[dataFeedIdIndex] = BitmapLib.setBitInBitmap( + bitmapSignersForDataFeedId, + signerIndex + ); + } + + // Breaking, as there couldn't be several indexes for the same feed ID + break; + } + } + } + } + + // Return total data package byte size + return + DATA_PACKAGE_WITHOUT_DATA_POINTS_BS + + (eachDataPointValueByteSize + DATA_POINT_SYMBOL_BS) * + dataPointsCount; + } + + /** + * @dev This is a private helpful function, which aggregates values from different + * authorised signers for the given arrays of values for each data feed + * + * @param valuesForDataFeeds 2-dimensional array, valuesForDataFeeds[i][j] contains + * j-th value for the i-th data feed + * @param uniqueSignerCountForDataFeedIds an array with the numbers of unique signers + * for each data feed + * + * @return An array of the aggregated values + */ + function _getAggregatedValues( + uint256[][] memory valuesForDataFeeds, + uint256[] memory uniqueSignerCountForDataFeedIds + ) private view returns (uint256[] memory) { + uint256[] memory aggregatedValues = new uint256[](valuesForDataFeeds.length); + uint256 uniqueSignersThreshold = getUniqueSignersThreshold(); + + for (uint256 dataFeedIndex = 0; dataFeedIndex < valuesForDataFeeds.length; dataFeedIndex++) { + if (uniqueSignerCountForDataFeedIds[dataFeedIndex] < uniqueSignersThreshold) { + revert InsufficientNumberOfUniqueSigners( + uniqueSignerCountForDataFeedIds[dataFeedIndex], + uniqueSignersThreshold); + } + uint256 aggregatedValueForDataFeedId = aggregateValues(valuesForDataFeeds[dataFeedIndex]); + aggregatedValues[dataFeedIndex] = aggregatedValueForDataFeedId; + } + + return aggregatedValues; + } +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol new file mode 100644 index 0000000..057db05 --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.4; + +import "../../../../@openzeppelin/contracts/utils/math/SafeMath.sol"; + +import "./RedstoneConsumerBase.sol"; + +/** + * @title The base contract for Redstone consumers' contracts that allows to + * securely calculate dynamic (array of bytes) redstone oracle values + * @author The Redstone Oracles team + * + * @dev This contract can extend other contracts to allow them + * securely fetch Redstone oracle data from tx calldata in a form of byte arrays. + * + * Note! If you want to use numeric values, use RedstoneConsumerNumericBase contract + * + * We wanted to reuse the core logic from the RedstoneConsumerBase contract, but it + * required few tricks, which are described below: + * + * 1. "Tricky" calldata pointers - we decided to use single uint256 values and store + * the calldata offset in the first 128 bits of those numbers, and the value byte size + * in the last 128 bits of the value. It allowed us to reuse a big part of core logic + * and even slightly optimised memory usage. To optimise gas costs, we left the burden + * of converting tricky calldata pointers to calldata bytes arrays on the consumer + * contracts developers. They can use a helpful `getCalldataBytesFromCalldataPointer` + * function for it + * + * 2. Returning memory pointers instead of actual values - we need to work with + * dynamic bytes arrays in this contract, but the core logic of RedstoneConsumerBase + * contract expects a uint256 number as a result of values aggregation. That's + * why we swtiched to returning a memory pointers instead of actual values. But this is + * more an implementation detail and should not affect end developers during the + * integration with the Redstone protocol + */ +abstract contract RedstoneConsumerBytesBase is RedstoneConsumerBase { + using SafeMath for uint256; + + uint256 constant BITS_COUNT_IN_16_BYTES = 128; + + /** + * @dev This function may be overridden by the child consumer contract. + * It should aggregate values from different signers into a bytes array + * By default, it checks if all the values are identical and returns the first one + * + * @param calldataPointersForValues An array of "tricky" calldata pointers to + * the values provided by different authorised signers. Each tricky calldata pointer + * is a uint256 number, first 128 bits of which represent calldata offset, and the + * last 128 bits - the byte length of the value + * + * @return Result of the aggregation in the form of a bytes array + */ + function aggregateByteValues(uint256[] memory calldataPointersForValues) + public + view + virtual + returns (bytes memory) + { + // Check if all byte arrays are identical + if (calldataPointersForValues.length <= 0) { + revert EmptyCalldataPointersArr(); + } + bytes calldata firstValue = getCalldataBytesFromCalldataPointer(calldataPointersForValues[0]); + bytes32 expectedHash = keccak256(firstValue); + + for (uint256 i = 1; i < calldataPointersForValues.length; i++) { + bytes calldata currentValue = getCalldataBytesFromCalldataPointer( + calldataPointersForValues[i] + ); + if (keccak256(currentValue) != expectedHash) { + revert EachSignerMustProvideTheSameValue(); + } + } + + return firstValue; + } + + /** + * @dev This function may be used to convert a "tricky" calldata pointer into a + * calldata bytes array. You may find it useful while overriding the + * `aggregateByteValues` function + * + * @param trickyCalldataPtr A "tricky" calldata pointer, 128 first bits of which + * represent the offset, and the last 128 bits - the byte length of the value + * + * @return bytesValueInCalldata The corresponding calldata bytes array + */ + function getCalldataBytesFromCalldataPointer(uint256 trickyCalldataPtr) + internal + pure + returns (bytes calldata bytesValueInCalldata) + { + uint256 calldataOffset = _getNumberFromFirst128Bits(trickyCalldataPtr); + uint256 valueByteSize = _getNumberFromLast128Bits(trickyCalldataPtr); + if (calldataOffset + valueByteSize > msg.data.length) { + revert InvalidCalldataPointer(); + } + + assembly { + bytesValueInCalldata.offset := calldataOffset + bytesValueInCalldata.length := valueByteSize + } + } + + /** + * @dev This function can be used in a consumer contract to securely extract an + * oracle value for a given data feed id. Security is achieved by + * signatures verification, timestamp validation, and aggregating bytes values + * from different authorised signers into a single bytes array. If any of the + * required conditions do not match, the function will revert. + * Note! This function expects that tx calldata contains redstone payload in the end + * Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme + * @param dataFeedId bytes32 value that uniquely identifies the data feed + * @return Bytes array with the aggregated oracle value for the given data feed id + */ + function getOracleBytesValueFromTxMsg(bytes32 dataFeedId) internal view returns (bytes memory) { + bytes32[] memory dataFeedIds = new bytes32[](1); + dataFeedIds[0] = dataFeedId; + return getOracleBytesValuesFromTxMsg(dataFeedIds)[0]; + } + + /** + * @dev This function can be used in a consumer contract to securely extract several + * numeric oracle values for a given array of data feed ids. Security is achieved by + * signatures verification, timestamp validation, and aggregating values + * from different authorised signers into a single numeric value. If any of the + * required conditions do not match, the function will revert. + * Note! This function expects that tx calldata contains redstone payload in the end + * Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme + * @param dataFeedIds An array of unique data feed identifiers + * @return arrayOfMemoryPointers TODO + */ + function getOracleBytesValuesFromTxMsg(bytes32[] memory dataFeedIds) + internal + view + returns (bytes[] memory arrayOfMemoryPointers) + { + // The `_securelyExtractOracleValuesFromTxMsg` function contains the main logic + // for the data extraction and validation + uint256[] memory arrayOfExtractedValues = _securelyExtractOracleValuesFromTxMsg(dataFeedIds); + assembly { + arrayOfMemoryPointers := arrayOfExtractedValues + } + } + + /** + * @dev This is a helpful function for the values aggregation + * Unlike in the RedstoneConsumerBase contract, you should not override + * this function. If you want to have a custom aggregation logic, you can + * override the `aggregateByteValues` instead + * + * Note! Unlike in the `RedstoneConsumerBase` this function returns a memory pointer + * to the aggregated bytes array value (instead the value itself) + * + * @param calldataPointersToValues An array of "tricky" calldata pointers to + * the values provided by different authorised signers. Each tricky calldata pointer + * is a uint256 number, first 128 bits of which represent calldata offset, and the + * last 128 bits - the byte length of the value + * + * @return pointerToResultBytesInMemory A memory pointer to the aggregated bytes array + */ + function aggregateValues(uint256[] memory calldataPointersToValues) + public + view + override + returns (uint256 pointerToResultBytesInMemory) + { + bytes memory aggregatedBytes = aggregateByteValues(calldataPointersToValues); + assembly { + pointerToResultBytesInMemory := aggregatedBytes + } + } + + /** + * @dev This function extracts details for a given data point and returns its dataFeedId, + * and a "tricky" calldata pointer for its value + * + * @param calldataNegativeOffsetForDataPackage Calldata offset for the requested data package + * @param dataPointValueByteSize Expected number of bytes for the requested data point value + * @param dataPointIndex Index of the requested data point + * + * @return dataPointDataFeedId a data feed identifier for the extracted data point + * @return dataPointValue a "tricky" calldata pointer for the extracted value + */ + function _extractDataPointValueAndDataFeedId( + uint256 calldataNegativeOffsetForDataPackage, + uint256 dataPointValueByteSize, + uint256 dataPointIndex + ) internal pure override returns (bytes32 dataPointDataFeedId, uint256 dataPointValue) { + uint256 negativeOffsetToDataPoints = calldataNegativeOffsetForDataPackage + DATA_PACKAGE_WITHOUT_DATA_POINTS_BS; + uint256 dataPointNegativeOffset = negativeOffsetToDataPoints + + (1 + dataPointIndex).mul(dataPointValueByteSize + DATA_POINT_SYMBOL_BS); + uint256 dataPointCalldataOffset = msg.data.length.sub(dataPointNegativeOffset); + assembly { + dataPointDataFeedId := calldataload(dataPointCalldataOffset) + dataPointValue := prepareTrickyCalldataPointer( + add(dataPointCalldataOffset, DATA_POINT_SYMBOL_BS), + dataPointValueByteSize + ) + + function prepareTrickyCalldataPointer(calldataOffsetArg, valueByteSize) -> calldataPtr { + calldataPtr := or(shl(BITS_COUNT_IN_16_BYTES, calldataOffsetArg), valueByteSize) + } + } + } + + /// @dev This is a helpful function for "tricky" calldata pointers + function _getNumberFromFirst128Bits(uint256 number) internal pure returns (uint256) { + return number >> 128; + } + + /// @dev This is a helpful function for "tricky" calldata pointers + function _getNumberFromLast128Bits(uint256 number) internal pure returns (uint256) { + return uint128(number); + } +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol new file mode 100644 index 0000000..212fae5 --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.4; + +import "./RedstoneConsumerBase.sol"; + +/** + * @title The base contract for Redstone consumers' contracts that allows to + * securely calculate numeric redstone oracle values + * @author The Redstone Oracles team + * @dev This contract can extend other contracts to allow them + * securely fetch Redstone oracle data from transactions calldata + */ +abstract contract RedstoneConsumerNumericBase is RedstoneConsumerBase { + /** + * @dev This function can be used in a consumer contract to securely extract an + * oracle value for a given data feed id. Security is achieved by + * signatures verification, timestamp validation, and aggregating values + * from different authorised signers into a single numeric value. If any of the + * required conditions do not match, the function will revert. + * Note! This function expects that tx calldata contains redstone payload in the end + * Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme + * @param dataFeedId bytes32 value that uniquely identifies the data feed + * @return Extracted and verified numeric oracle value for the given data feed id + */ + function getOracleNumericValueFromTxMsg(bytes32 dataFeedId) + internal + view + virtual + returns (uint256) + { + bytes32[] memory dataFeedIds = new bytes32[](1); + dataFeedIds[0] = dataFeedId; + return getOracleNumericValuesFromTxMsg(dataFeedIds)[0]; + } + + /** + * @dev This function can be used in a consumer contract to securely extract several + * numeric oracle values for a given array of data feed ids. Security is achieved by + * signatures verification, timestamp validation, and aggregating values + * from different authorised signers into a single numeric value. If any of the + * required conditions do not match, the function will revert. + * Note! This function expects that tx calldata contains redstone payload in the end + * Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme + * @param dataFeedIds An array of unique data feed identifiers + * @return An array of the extracted and verified oracle values in the same order + * as they are requested in the dataFeedIds array + */ + function getOracleNumericValuesFromTxMsg(bytes32[] memory dataFeedIds) + internal + view + virtual + returns (uint256[] memory) + { + return _securelyExtractOracleValuesFromTxMsg(dataFeedIds); + } + + /** + * @dev This function works similarly to the `getOracleNumericValuesFromTxMsg` with the + * only difference that it allows to request oracle data for an array of data feeds + * that may contain duplicates + * + * @param dataFeedIdsWithDuplicates An array of data feed identifiers (duplicates are allowed) + * @return An array of the extracted and verified oracle values in the same order + * as they are requested in the dataFeedIdsWithDuplicates array + */ + function getOracleNumericValuesWithDuplicatesFromTxMsg(bytes32[] memory dataFeedIdsWithDuplicates) internal view returns (uint256[] memory) { + // Building an array without duplicates + bytes32[] memory dataFeedIdsWithoutDuplicates = new bytes32[](dataFeedIdsWithDuplicates.length); + bool alreadyIncluded; + uint256 uniqueDataFeedIdsCount = 0; + + for (uint256 indexWithDup = 0; indexWithDup < dataFeedIdsWithDuplicates.length; indexWithDup++) { + // Checking if current element is already included in `dataFeedIdsWithoutDuplicates` + alreadyIncluded = false; + for (uint256 indexWithoutDup = 0; indexWithoutDup < uniqueDataFeedIdsCount; indexWithoutDup++) { + if (dataFeedIdsWithoutDuplicates[indexWithoutDup] == dataFeedIdsWithDuplicates[indexWithDup]) { + alreadyIncluded = true; + break; + } + } + + // Adding if not included + if (!alreadyIncluded) { + dataFeedIdsWithoutDuplicates[uniqueDataFeedIdsCount] = dataFeedIdsWithDuplicates[indexWithDup]; + uniqueDataFeedIdsCount++; + } + } + + // Overriding dataFeedIdsWithoutDuplicates.length + // Equivalent to: dataFeedIdsWithoutDuplicates.length = uniqueDataFeedIdsCount; + assembly { + mstore(dataFeedIdsWithoutDuplicates, uniqueDataFeedIdsCount) + } + + // Requesting oracle values (without duplicates) + uint256[] memory valuesWithoutDuplicates = getOracleNumericValuesFromTxMsg(dataFeedIdsWithoutDuplicates); + + // Preparing result values array + uint256[] memory valuesWithDuplicates = new uint256[](dataFeedIdsWithDuplicates.length); + for (uint256 indexWithDup = 0; indexWithDup < dataFeedIdsWithDuplicates.length; indexWithDup++) { + for (uint256 indexWithoutDup = 0; indexWithoutDup < dataFeedIdsWithoutDuplicates.length; indexWithoutDup++) { + if (dataFeedIdsWithDuplicates[indexWithDup] == dataFeedIdsWithoutDuplicates[indexWithoutDup]) { + valuesWithDuplicates[indexWithDup] = valuesWithoutDuplicates[indexWithoutDup]; + break; + } + } + } + + return valuesWithDuplicates; + } +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneDefaultsLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneDefaultsLib.sol new file mode 100644 index 0000000..3618ad3 --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneDefaultsLib.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.4; + +import "../libs/NumericArrayLib.sol"; + +/** + * @title Default implementations of virtual redstone consumer base functions + * @author The Redstone Oracles team + */ +library RedstoneDefaultsLib { + uint256 constant DEFAULT_MAX_DATA_TIMESTAMP_DELAY_SECONDS = 3 minutes; + uint256 constant DEFAULT_MAX_DATA_TIMESTAMP_AHEAD_SECONDS = 1 minutes; + + error TimestampFromTooLongFuture(uint256 receivedTimestampSeconds, uint256 blockTimestamp); + error TimestampIsTooOld(uint256 receivedTimestampSeconds, uint256 blockTimestamp); + + function validateTimestamp(uint256 receivedTimestampMilliseconds) internal view { + // Getting data timestamp from future seems quite unlikely + // But we've already spent too much time with different cases + // Where block.timestamp was less than dataPackage.timestamp. + // Some blockchains may case this problem as well. + // That's why we add MAX_BLOCK_TIMESTAMP_DELAY + // and allow data "from future" but with a small delay + uint256 receivedTimestampSeconds = receivedTimestampMilliseconds / 1000; + + if (block.timestamp < receivedTimestampSeconds) { + if ((receivedTimestampSeconds - block.timestamp) > DEFAULT_MAX_DATA_TIMESTAMP_AHEAD_SECONDS) { + revert TimestampFromTooLongFuture(receivedTimestampSeconds, block.timestamp); + } + } else if ((block.timestamp - receivedTimestampSeconds) > DEFAULT_MAX_DATA_TIMESTAMP_DELAY_SECONDS) { + revert TimestampIsTooOld(receivedTimestampSeconds, block.timestamp); + } + } + + function aggregateValues(uint256[] memory values) internal pure returns (uint256) { + return NumericArrayLib.pickMedian(values); + } +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/BitmapLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/BitmapLib.sol new file mode 100644 index 0000000..27d783f --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/BitmapLib.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +library BitmapLib { + function setBitInBitmap(uint256 bitmap, uint256 bitIndex) internal pure returns (uint256) { + return bitmap | (1 << bitIndex); + } + + function getBitFromBitmap(uint256 bitmap, uint256 bitIndex) internal pure returns (bool) { + uint256 bitAtIndex = bitmap & (1 << bitIndex); + return bitAtIndex > 0; + } +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/HardhatLoggerLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/HardhatLoggerLib.sol new file mode 100644 index 0000000..181e31d --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/HardhatLoggerLib.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +import "hardhat/console.sol"; + +library HardhatLoggerLib { + function logUint256Array(uint256[] memory arr) internal view { + for (uint256 i = 0; i < arr.length; i++) { + console.log("\narr", i); + console.log(arr[i]); + } + } +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumbersLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumbersLib.sol new file mode 100644 index 0000000..086a2ca --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumbersLib.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +library NumbersLib { + uint256 constant BITS_COUNT_IN_16_BYTES = 128; + + function getNumberFromFirst16Bytes(uint256 number) internal pure returns (uint256) { + return uint256(number >> BITS_COUNT_IN_16_BYTES); + } + + function getNumberFromLast16Bytes(uint256 number) internal pure returns (uint256) { + return uint256((number << BITS_COUNT_IN_16_BYTES) >> BITS_COUNT_IN_16_BYTES); + } +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol new file mode 100644 index 0000000..fcbffbb --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +import "../../../../@openzeppelin/contracts/utils/math/SafeMath.sol"; + +library NumericArrayLib { + // This function sort array in memory using bubble sort algorithm, + // which performs even better than quick sort for small arrays + + uint256 constant BYTES_ARR_LEN_VAR_BS = 32; + uint256 constant UINT256_VALUE_BS = 32; + + error CanNotPickMedianOfEmptyArray(); + + // This function modifies the array + function pickMedian(uint256[] memory arr) internal pure returns (uint256) { + if (arr.length == 0) { + revert CanNotPickMedianOfEmptyArray(); + } + sort(arr); + uint256 middleIndex = arr.length / 2; + if (arr.length % 2 == 0) { + uint256 sum = SafeMath.add(arr[middleIndex - 1], arr[middleIndex]); + return sum / 2; + } else { + return arr[middleIndex]; + } + } + + function sort(uint256[] memory arr) internal pure { + assembly { + let arrLength := mload(arr) + let valuesPtr := add(arr, BYTES_ARR_LEN_VAR_BS) + let endPtr := add(valuesPtr, mul(arrLength, UINT256_VALUE_BS)) + for { + let arrIPtr := valuesPtr + } lt(arrIPtr, endPtr) { + arrIPtr := add(arrIPtr, UINT256_VALUE_BS) // arrIPtr += 32 + } { + for { + let arrJPtr := valuesPtr + } lt(arrJPtr, arrIPtr) { + arrJPtr := add(arrJPtr, UINT256_VALUE_BS) // arrJPtr += 32 + } { + let arrI := mload(arrIPtr) + let arrJ := mload(arrJPtr) + if lt(arrI, arrJ) { + mstore(arrIPtr, arrJ) + mstore(arrJPtr, arrI) + } + } + } + } + } +} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/SignatureLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/SignatureLib.sol new file mode 100644 index 0000000..b2c6a35 --- /dev/null +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/SignatureLib.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +library SignatureLib { + uint256 constant ECDSA_SIG_R_BS = 32; + uint256 constant ECDSA_SIG_S_BS = 32; + + function recoverSignerAddress(bytes32 signedHash, uint256 signatureCalldataNegativeOffset) + internal + pure + returns (address) + { + bytes32 r; + bytes32 s; + uint8 v; + assembly { + let signatureCalldataStartPos := sub(calldatasize(), signatureCalldataNegativeOffset) + r := calldataload(signatureCalldataStartPos) + signatureCalldataStartPos := add(signatureCalldataStartPos, ECDSA_SIG_R_BS) + s := calldataload(signatureCalldataStartPos) + signatureCalldataStartPos := add(signatureCalldataStartPos, ECDSA_SIG_S_BS) + v := byte(0, calldataload(signatureCalldataStartPos)) // last byte of the signature memory array + } + return ecrecover(signedHash, v, r, s); + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/IRedstoneAdapter.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/IRedstoneAdapter.sol new file mode 100644 index 0000000..36f3575 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/IRedstoneAdapter.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +/** + * @title Interface of RedStone adapter + * @author The Redstone Oracles team + */ +interface IRedstoneAdapter { + + /** + * @notice Updates values of all data feeds supported by the Adapter contract + * @dev This function requires an attached redstone payload to the transaction calldata. + * It also requires each data package to have exactly the same timestamp + * @param dataPackagesTimestamp Timestamp of each signed data package in the redstone payload + */ + function updateDataFeedsValues(uint256 dataPackagesTimestamp) external; + + + /** + * @notice Returns the latest properly reported value of the data feed + * @param dataFeedId The identifier of the requested data feed + * @return value The latest value of the given data feed + */ + function getValueForDataFeed(bytes32 dataFeedId) external view returns (uint256); + + /** + * @notice Returns the latest properly reported values for several data feeds + * @param requestedDataFeedIds The array of identifiers for the requested feeds + * @return values Values of the requested data feeds in the corresponding order + */ + function getValuesForDataFeeds(bytes32[] memory requestedDataFeedIds) external view returns (uint256[] memory); + + /** + * @notice Returns data timestamp from the latest update + * @dev It's virtual, because its implementation can sometimes be different + * (e.g. SinglePriceFeedAdapterWithClearing) + * @return lastDataTimestamp Timestamp of the latest reported data packages + */ + function getDataTimestampFromLatestUpdate() external view returns (uint256 lastDataTimestamp); + + /** + * @notice Returns block timestamp of the latest successful update + * @return blockTimestamp The block timestamp of the latest successful update + */ + function getBlockTimestampFromLatestUpdate() external view returns (uint256 blockTimestamp); + + + /** + * @notice Returns timestamps of the latest successful update + * @return dataTimestamp timestamp (usually in milliseconds) from the signed data packages + * @return blockTimestamp timestamp of the block when the update has happened + */ + function getTimestampsFromLatestUpdate() external view returns (uint128 dataTimestamp, uint128 blockTimestamp); + + /** + * @notice Returns identifiers of all data feeds supported by the Adapter contract + * @return An array of data feed identifiers + */ + function getDataFeedIds() external view returns (bytes32[] memory); + + /** + * @notice Returns the unique index of the given data feed + * @param dataFeedId The data feed identifier + * @return index The index of the data feed + */ + function getDataFeedIndex(bytes32 dataFeedId) external view returns (uint256); + + /** + * @notice Returns minimal required interval (usually in seconds) between subsequent updates + * @return interval The required interval between updates + */ + function getMinIntervalBetweenUpdates() external view returns (uint256); + + /** + * @notice Reverts if the proposed timestamp of data packages it too old or too new + * comparing to the block.timestamp. It also ensures that the proposed timestamp is newer + * Then the one from the previous update + * @param dataPackagesTimestamp The proposed timestamp (usually in milliseconds) + */ + function validateProposedDataPackagesTimestamp(uint256 dataPackagesTimestamp) external view; + + /** + * @notice Reverts if the updater is not authorised + * @dev This function should revert if msg.sender is not allowed to update data feed values + * @param updater The address of the proposed updater + */ + function requireAuthorisedUpdater(address updater) external view; +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/RedstoneAdapterBase.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/RedstoneAdapterBase.sol new file mode 100644 index 0000000..58e342c --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/RedstoneAdapterBase.sol @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {RedstoneConsumerNumericBase, RedstoneDefaultsLib} from "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol"; +import {IRedstoneAdapter} from "./IRedstoneAdapter.sol"; + +/** + * @title Core logic of Redstone Adapter Contract + * @author The Redstone Oracles team + * @dev This contract is used to repeatedly push Redstone data to blockchain storage + * More details here: https://docs.redstone.finance/docs/smart-contract-devs/get-started/redstone-classic + * + * Key details about the contract: + * - Values for data feeds can be updated using the `updateDataFeedsValues` function + * - All data feeds must be updated within a single call, partial updates are not allowed + * - There is a configurable minimum interval between updates + * - Updaters can be restricted by overriding `requireAuthorisedUpdater` function + * - The contract is designed to force values validation, by default it prevents returning zero values + * - All data packages in redstone payload must have the same timestamp, + * equal to `dataPackagesTimestamp` argument of the `updateDataFeedsValues` function + * - Block timestamp abstraction - even though we call it blockTimestamp in many places, + * it's possible to have a custom logic here, e.g. use block number instead of a timestamp + */ +abstract contract RedstoneAdapterBase is RedstoneConsumerNumericBase, IRedstoneAdapter { + // We don't use storage variables to avoid potential problems with upgradable contracts + bytes32 internal constant LATEST_UPDATE_TIMESTAMPS_STORAGE_LOCATION = 0x3d01e4d77237ea0f771f1786da4d4ff757fcba6a92933aa53b1dcef2d6bd6fe2; // keccak256("RedStone.lastUpdateTimestamp"); + uint256 internal constant MIN_INTERVAL_BETWEEN_UPDATES = 0 seconds; + uint256 internal constant BITS_COUNT_IN_16_BYTES = 128; + uint256 internal constant MAX_NUMBER_FOR_128_BITS = 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff; + + error DataTimestampShouldBeNewerThanBefore( + uint256 receivedDataTimestampMilliseconds, + uint256 lastDataTimestampMilliseconds + ); + + error MinIntervalBetweenUpdatesHasNotPassedYet( + uint256 currentBlockTimestamp, + uint256 lastUpdateTimestamp, + uint256 minIntervalBetweenUpdates + ); + + error DataPackageTimestampMismatch(uint256 expectedDataTimestamp, uint256 dataPackageTimestamp); + + error DataFeedValueCannotBeZero(bytes32 dataFeedId); + + error DataFeedIdNotFound(bytes32 dataFeedId); + + error DataTimestampIsTooBig(uint256 dataTimestamp); + + error BlockTimestampIsTooBig(uint256 blockTimestamp); + + /** + * @notice Reverts if the updater is not authorised + * @dev This function should revert if msg.sender is not allowed to update data feed values + * @param updater The address of the proposed updater + */ + function requireAuthorisedUpdater(address updater) public view virtual { + // By default, anyone can update data feed values, but it can be overridden + } + + /** + * @notice Returns identifiers of all data feeds supported by the Adapter contract + * @dev this function must be implemented in derived contracts + * @return An array of data feed identifiers + */ + function getDataFeedIds() public view virtual returns (bytes32[] memory); + + /** + * @notice Returns the unique index of the given data feed + * @dev This function can (and should) be overriden to reduce gas + * costs of other functions + * @param dataFeedId The data feed identifier + * @return index The index of the data feed + */ + function getDataFeedIndex(bytes32 dataFeedId) public view virtual returns (uint256) { + bytes32[] memory dataFeedIds = getDataFeedIds(); + for (uint256 i = 0; i < dataFeedIds.length;) { + if (dataFeedIds[i] == dataFeedId) { + return i; + } + unchecked { i++; } // reduces gas costs + } + revert DataFeedIdNotFound(dataFeedId); + } + + /** + * @notice Updates values of all data feeds supported by the Adapter contract + * @dev This function requires an attached redstone payload to the transaction calldata. + * It also requires each data package to have exactly the same timestamp + * @param dataPackagesTimestamp Timestamp of each signed data package in the redstone payload + */ + function updateDataFeedsValues(uint256 dataPackagesTimestamp) public { + requireAuthorisedUpdater(msg.sender); + _assertMinIntervalBetweenUpdatesPassed(); + validateProposedDataPackagesTimestamp(dataPackagesTimestamp); + _saveTimestampsOfCurrentUpdate(dataPackagesTimestamp); + + bytes32[] memory dataFeedsIdsArray = getDataFeedIds(); + + // It will trigger timestamp validation for each data package + uint256[] memory oracleValues = getOracleNumericValuesFromTxMsg(dataFeedsIdsArray); + + _validateAndUpdateDataFeedsValues(dataFeedsIdsArray, oracleValues); + } + + /** + * @dev Note! This function is not called directly, it's called for each data package . + * in redstone payload and just verifies if each data package has the same timestamp + * as the one that was saved in the storage + * @param receivedTimestampMilliseconds Timestamp from a data package + */ + function validateTimestamp(uint256 receivedTimestampMilliseconds) public view virtual override { + // It means that we are in the special view context and we can skip validation of the + // timestamp. It can be useful for calling view functions, as they can not modify the contract + // state to pass the timestamp validation below + if (msg.sender == address(0)) { + return; + } + + uint256 expectedDataPackageTimestamp = getDataTimestampFromLatestUpdate(); + if (receivedTimestampMilliseconds != expectedDataPackageTimestamp) { + revert DataPackageTimestampMismatch( + expectedDataPackageTimestamp, + receivedTimestampMilliseconds + ); + } + } + + /** + * @dev This function should be implemented by the actual contract + * and should contain the logic of values validation and reporting. + * Usually, values reporting is based on saving them to the contract storage, + * e.g. in PriceFeedsAdapter, but some custom implementations (e.g. GMX keeper adapter + * or Mento Sorted Oracles adapter) may handle values updating in a different way + * @param dataFeedIdsArray Array of the data feeds identifiers (it will always be all data feed ids) + * @param values The reported values that should be validated and reported + */ + function _validateAndUpdateDataFeedsValues(bytes32[] memory dataFeedIdsArray, uint256[] memory values) internal virtual; + + /** + * @dev This function reverts if not enough time passed since the latest update + */ + function _assertMinIntervalBetweenUpdatesPassed() private view { + uint256 currentBlockTimestamp = getBlockTimestamp(); + uint256 blockTimestampFromLatestUpdate = getBlockTimestampFromLatestUpdate(); + uint256 minIntervalBetweenUpdates = getMinIntervalBetweenUpdates(); + if (currentBlockTimestamp < blockTimestampFromLatestUpdate + minIntervalBetweenUpdates) { + revert MinIntervalBetweenUpdatesHasNotPassedYet( + currentBlockTimestamp, + blockTimestampFromLatestUpdate, + minIntervalBetweenUpdates + ); + } + } + + /** + * @notice Returns minimal required interval (usually in seconds) between subsequent updates + * @dev You can override this function to change the required interval between udpates. + * Please do not set it to 0, as it may open many attack vectors + * @return interval The required interval between updates + */ + function getMinIntervalBetweenUpdates() public view virtual returns (uint256) { + return MIN_INTERVAL_BETWEEN_UPDATES; + } + + /** + * @notice Reverts if the proposed timestamp of data packages it too old or too new + * comparing to the block.timestamp. It also ensures that the proposed timestamp is newer + * Then the one from the previous update + * @param dataPackagesTimestamp The proposed timestamp (usually in milliseconds) + */ + function validateProposedDataPackagesTimestamp(uint256 dataPackagesTimestamp) public view { + _preventUpdateWithOlderDataPackages(dataPackagesTimestamp); + validateDataPackagesTimestampOnce(dataPackagesTimestamp); + } + + + /** + * @notice Reverts if the proposed timestamp of data packages it too old or too new + * comparing to the current block timestamp + * @param dataPackagesTimestamp The proposed timestamp (usually in milliseconds) + */ + function validateDataPackagesTimestampOnce(uint256 dataPackagesTimestamp) public view virtual { + uint256 receivedTimestampSeconds = dataPackagesTimestamp / 1000; + + (uint256 maxDataAheadSeconds, uint256 maxDataDelaySeconds) = getAllowedTimestampDiffsInSeconds(); + + uint256 blockTimestamp = getBlockTimestamp(); + + if (blockTimestamp < receivedTimestampSeconds) { + // if ((receivedTimestampSeconds - blockTimestamp) > maxDataAheadSeconds) { + // revert RedstoneDefaultsLib.TimestampFromTooLongFuture(receivedTimestampSeconds, blockTimestamp); + // } + } else if ((blockTimestamp - receivedTimestampSeconds) > maxDataDelaySeconds) { + revert RedstoneDefaultsLib.TimestampIsTooOld(receivedTimestampSeconds, blockTimestamp); + } + } + + /** + * @dev This function can be overriden, e.g. to use block.number instead of block.timestamp + * It can be useful in some L2 chains, as sometimes their different blocks can have the same timestamp + * @return timestamp Timestamp or Block number or any other number that can identify time in the context + * of the given blockchain + */ + function getBlockTimestamp() public view virtual returns (uint256) { + return block.timestamp; + } + + /** + * @dev Helpful function for getting values for timestamp validation + * @return maxDataAheadSeconds Max allowed number of seconds ahead of block.timrstamp + * @return maxDataDelaySeconds Max allowed number of seconds for data delay + */ + function getAllowedTimestampDiffsInSeconds() public view virtual returns (uint256 maxDataAheadSeconds, uint256 maxDataDelaySeconds) { + maxDataAheadSeconds = RedstoneDefaultsLib.DEFAULT_MAX_DATA_TIMESTAMP_AHEAD_SECONDS; + maxDataDelaySeconds = RedstoneDefaultsLib.DEFAULT_MAX_DATA_TIMESTAMP_DELAY_SECONDS; + } + + /** + * @dev Reverts if proposed data packages are not newer than the ones used previously + * @param dataPackagesTimestamp Timestamp od the data packages (usually in milliseconds) + */ + function _preventUpdateWithOlderDataPackages(uint256 dataPackagesTimestamp) internal view { + uint256 dataTimestampFromLatestUpdate = getDataTimestampFromLatestUpdate(); + + if (dataPackagesTimestamp <= dataTimestampFromLatestUpdate) { + revert DataTimestampShouldBeNewerThanBefore( + dataPackagesTimestamp, + dataTimestampFromLatestUpdate + ); + } + } + + /** + * @notice Returns data timestamp from the latest update + * @dev It's virtual, because its implementation can sometimes be different + * (e.g. SinglePriceFeedAdapterWithClearing) + * @return lastDataTimestamp Timestamp of the latest reported data packages + */ + function getDataTimestampFromLatestUpdate() public view virtual returns (uint256 lastDataTimestamp) { + (lastDataTimestamp, ) = getTimestampsFromLatestUpdate(); + } + + /** + * @notice Returns block timestamp of the latest successful update + * @return blockTimestamp The block timestamp of the latest successful update + */ + function getBlockTimestampFromLatestUpdate() public view returns (uint256 blockTimestamp) { + (, blockTimestamp) = getTimestampsFromLatestUpdate(); + } + + /** + * @dev Returns 2 timestamps packed into a single uint256 number + * @return packedTimestamps a single uin256 number with 2 timestamps + */ + function getPackedTimestampsFromLatestUpdate() public view returns (uint256 packedTimestamps) { + assembly { + packedTimestamps := sload(LATEST_UPDATE_TIMESTAMPS_STORAGE_LOCATION) + } + } + + /** + * @notice Returns timestamps of the latest successful update + * @return dataTimestamp timestamp (usually in milliseconds) from the signed data packages + * @return blockTimestamp timestamp of the block when the update has happened + */ + function getTimestampsFromLatestUpdate() public view virtual returns (uint128 dataTimestamp, uint128 blockTimestamp) { + return _unpackTimestamps(getPackedTimestampsFromLatestUpdate()); + } + + + /** + * @dev A helpful function to unpack 2 timestamps from one uin256 number + * @param packedTimestamps a single uin256 number + * @return dataTimestamp fetched from left 128 bits + * @return blockTimestamp fetched from right 128 bits + */ + function _unpackTimestamps(uint256 packedTimestamps) internal pure returns (uint128 dataTimestamp, uint128 blockTimestamp) { + dataTimestamp = uint128(packedTimestamps >> 128); // left 128 bits + blockTimestamp = uint128(packedTimestamps); // right 128 bits + } + + + /** + * @dev Logic of saving timestamps of the current update + * By default, it stores packed timestamps in one storage slot (32 bytes) + * to minimise gas costs + * But it can be overriden (e.g. in SinglePriceFeedAdapter) + * @param dataPackagesTimestamp . + */ + function _saveTimestampsOfCurrentUpdate(uint256 dataPackagesTimestamp) internal virtual { + uint256 blockTimestamp = getBlockTimestamp(); + + if (blockTimestamp > MAX_NUMBER_FOR_128_BITS) { + revert BlockTimestampIsTooBig(blockTimestamp); + } + + if (dataPackagesTimestamp > MAX_NUMBER_FOR_128_BITS) { + revert DataTimestampIsTooBig(dataPackagesTimestamp); + } + + assembly { + let timestamps := or(shl(BITS_COUNT_IN_16_BYTES, dataPackagesTimestamp), blockTimestamp) + sstore(LATEST_UPDATE_TIMESTAMPS_STORAGE_LOCATION, timestamps) + } + } + + /** + * @notice Returns the latest properly reported value of the data feed + * @param dataFeedId The identifier of the requested data feed + * @return value The latest value of the given data feed + */ + function getValueForDataFeed(bytes32 dataFeedId) public view returns (uint256) { + getDataFeedIndex(dataFeedId); // will revert if data feed id is not supported + + // "unsafe" here means "without validation" + uint256 valueForDataFeed = getValueForDataFeedUnsafe(dataFeedId); + + validateDataFeedValue(dataFeedId, valueForDataFeed); + return valueForDataFeed; + } + + /** + * @notice Returns the latest properly reported values for several data feeds + * @param dataFeedIds The array of identifiers for the requested feeds + * @return values Values of the requested data feeds in the corresponding order + */ + function getValuesForDataFeeds(bytes32[] memory dataFeedIds) public view returns (uint256[] memory) { + uint256[] memory values = getValuesForDataFeedsUnsafe(dataFeedIds); + for (uint256 i = 0; i < dataFeedIds.length;) { + bytes32 dataFeedId = dataFeedIds[i]; + getDataFeedIndex(dataFeedId); // will revert if data feed id is not supported + validateDataFeedValue(dataFeedId, values[i]); + unchecked { i++; } // reduces gas costs + } + return values; + } + + + /** + * @dev Reverts if proposed value for the proposed data feed id is invalid + * By default, it just checks if the value is not equal to 0, but it can be extended + * @param dataFeedId The data feed identifier + * @param valueForDataFeed Proposed value for the data feed + */ + function validateDataFeedValue(bytes32 dataFeedId, uint256 valueForDataFeed) public pure virtual { + if (valueForDataFeed == 0) { + revert DataFeedValueCannotBeZero(dataFeedId); + } + } + + /** + * @dev [HIGH RISK] Returns the latest value for a given data feed without validation + * Important! Using this function instead of `getValueForDataFeed` may cause + * significant risk for your smart contracts + * @param dataFeedId The data feed identifier + * @return dataFeedValue Unvalidated value of the latest successful update + */ + function getValueForDataFeedUnsafe(bytes32 dataFeedId) public view virtual returns (uint256); + + /** + * @notice [HIGH RISK] Returns the latest properly reported values for several data feeds without validation + * Important! Using this function instead of `getValuesForDataFeeds` may cause + * significant risk for your smart contracts + * @param requestedDataFeedIds The array of identifiers for the requested feeds + * @return values Unvalidated values of the requested data feeds in the corresponding order + */ + function getValuesForDataFeedsUnsafe(bytes32[] memory requestedDataFeedIds) public view virtual returns (uint256[] memory values) { + values = new uint256[](requestedDataFeedIds.length); + for (uint256 i = 0; i < requestedDataFeedIds.length;) { + values[i] = getValueForDataFeedUnsafe(requestedDataFeedIds[i]); + unchecked { i++; } // reduces gas costs + } + return values; + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/MergedPriceFeedAdapterCommon.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/MergedPriceFeedAdapterCommon.sol new file mode 100644 index 0000000..e47fb7f --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/MergedPriceFeedAdapterCommon.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.14; +import {IRedstoneAdapter} from "../core/IRedstoneAdapter.sol"; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +abstract contract MergedPriceFeedAdapterCommon { + event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); + + error CannotUpdateMoreThanOneDataFeed(); + + function getPriceFeedAdapter() public view virtual returns (IRedstoneAdapter) { + return IRedstoneAdapter(address(this)); + } + + function aggregator() public view virtual returns (address) { + return address(getPriceFeedAdapter()); + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol new file mode 100644 index 0000000..bad9e52 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.14; + +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {IRedstoneAdapter} from "../core/IRedstoneAdapter.sol"; +import {IPriceFeed} from "./interfaces/IPriceFeed.sol"; + +/** + * @title Main logic of the price feed contract + * @author The Redstone Oracles team + * @dev Implementation of common functions for the PriceFeed contract + * that queries data from the specified PriceFeedAdapter + * + * It can be used by projects that have already implemented with Chainlink-like + * price feeds and would like to minimise changes in their existing codebase. + * + * If you are flexible, it's much better (and cheaper in terms of gas) to query + * the PriceFeedAdapter contract directly + */ +abstract contract PriceFeedBase is IPriceFeed, Initializable { + uint256 internal constant INT256_MAX = uint256(type(int256).max); + + error UnsafeUintToIntConversion(uint256 value); + + /** + * @dev Helpful function for upgradable contracts + */ + function initialize() public virtual initializer { + // We don't have storage variables, but we keep this function + // Because it is used for contract setup in upgradable contracts + } + + /** + * @notice Returns data feed identifier for the PriceFeed contract + * @return dataFeedId The identifier of the data feed + */ + function getDataFeedId() public view virtual returns (bytes32); + + /** + * @notice Returns the address of the price feed adapter + * @return address The address of the price feed adapter + */ + function getPriceFeedAdapter() public view virtual returns (IRedstoneAdapter); + + + /** + * @notice Returns the number of decimals for the price feed + * @dev By default, RedStone uses 8 decimals for data feeds + * @return decimals The number of decimals in the price feed values + */ + function decimals() public virtual pure override returns (uint8) { + return 8; + } + + + /** + * @notice Description of the Price Feed + * @return description + */ + function description() public view virtual override returns (string memory) { + return "Redstone Price Feed"; + } + + /** + * @notice Version of the Price Feed + * @dev Currently it has no specific motivation and was added + * only to be compatible with the Chainlink interface + * @return version + */ + function version() public virtual pure override returns (uint256) { + return 1; + } + + + /** + * @notice Returns details of the latest successful update round + * @dev It uses few helpful functions to abstract logic of getting + * latest round id and value + * @return roundId The number of the latest round + * @return answer The latest reported value + * @return startedAt Block timestamp when the latest successful round started + * @return updatedAt Block timestamp of the latest successful round + * @return answeredInRound The number of the latest round + */ + function latestRoundData() + public + view + override + virtual + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + roundId = latestRound(); + answer = latestAnswer(); + + uint256 blockTimestamp = getPriceFeedAdapter().getBlockTimestampFromLatestUpdate(); + + // These values are equal after chainlink’s OCR update + startedAt = blockTimestamp; + updatedAt = blockTimestamp; + + // We want to be compatible with Chainlink's interface + // And in our case the roundId is always equal to answeredInRound + answeredInRound = roundId; + } + + /** + * @notice Old Chainlink function for getting the latest successfully reported value + * @return latestAnswer The latest successfully reported value + */ + function latestAnswer() public virtual view returns (int256) { + bytes32 dataFeedId = getDataFeedId(); + + uint256 uintAnswer = getPriceFeedAdapter().getValueForDataFeed(dataFeedId); + + if (uintAnswer > INT256_MAX) { + revert UnsafeUintToIntConversion(uintAnswer); + } + + return int256(uintAnswer); + } + + /** + * @notice Old Chainlink function for getting the number of latest round + * @return latestRound The number of the latest update round + */ + function latestRound() public view virtual returns (uint80); +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol new file mode 100644 index 0000000..18468e8 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {RedstoneAdapterBase} from "../core/RedstoneAdapterBase.sol"; + +/** + * @title Common logic of the price feeds adapter contracts + * @author The Redstone Oracles team + */ +abstract contract PriceFeedsAdapterBase is RedstoneAdapterBase, Initializable { + + /** + * @dev Helpful function for upgradable contracts + */ + function initialize() public virtual initializer { + // We don't have storage variables, but we keep this function + // Because it is used for contract setup in upgradable contracts + } + + /** + * @dev This function is virtual and may contain additional logic in the derived contract + * E.g. it can check if the updating conditions are met (e.g. if at least one + * value is deviated enough) + * @param dataFeedIdsArray Array of all data feeds identifiers + * @param values The reported values that are validated and reported + */ + function _validateAndUpdateDataFeedsValues( + bytes32[] memory dataFeedIdsArray, + uint256[] memory values + ) internal virtual override { + for (uint256 i = 0; i < dataFeedIdsArray.length;) { + _validateAndUpdateDataFeedValue(dataFeedIdsArray[i], values[i]); + unchecked { i++; } // reduces gas costs + } + } + + /** + * @dev Helpful virtual function for handling value validation and saving in derived + * Price Feed Adapters contracts + * @param dataFeedId The data feed identifier + * @param dataFeedValue Proposed value for the data feed + */ + function _validateAndUpdateDataFeedValue(bytes32 dataFeedId, uint256 dataFeedValue) internal virtual; +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithRoundsPrimaryProd.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithRoundsPrimaryProd.sol new file mode 100644 index 0000000..4fb70f8 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithRoundsPrimaryProd.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {MergedPriceFeedAdapterWithRounds} from "../with-rounds/MergedPriceFeedAdapterWithRounds.sol"; + +abstract contract MergedPriceFeedAdapterWithRoundsPrimaryProd is MergedPriceFeedAdapterWithRounds { + function getUniqueSignersThreshold() public view virtual override returns (uint8) { + return 2; + } + + function getAuthorisedSignerIndex( + address signerAddress + ) public view virtual override returns (uint8) { + if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { return 0; } + else if (signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499) { return 1; } + else if (signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202) { return 2; } + else if (signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE) { return 3; } + else if (signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de) { return 4; } + else { + revert SignerNotAuthorised(signerAddress); + } + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithoutRoundsPrimaryProd.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithoutRoundsPrimaryProd.sol new file mode 100644 index 0000000..2e47bcd --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithoutRoundsPrimaryProd.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {MergedPriceFeedAdapterWithoutRounds} from "../without-rounds/MergedPriceFeedAdapterWithoutRounds.sol"; + +abstract contract MergedPriceFeedAdapterWithoutRoundsPrimaryProd is MergedPriceFeedAdapterWithoutRounds { + function getUniqueSignersThreshold() public view virtual override returns (uint8) { + return 2; + } + + function getAuthorisedSignerIndex( + address signerAddress + ) public view virtual override returns (uint8) { + if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { return 0; } + else if (signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499) { return 1; } + else if (signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202) { return 2; } + else if (signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE) { return 3; } + else if (signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de) { return 4; } + else { + revert SignerNotAuthorised(signerAddress); + } + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithRoundsMainDemo.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithRoundsMainDemo.sol new file mode 100644 index 0000000..d764902 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithRoundsMainDemo.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {PriceFeedsAdapterWithRounds} from "../with-rounds/PriceFeedsAdapterWithRounds.sol"; + +abstract contract PriceFeedsAdapterWithRoundsMainDemo is PriceFeedsAdapterWithRounds { + function getUniqueSignersThreshold() public view virtual override returns (uint8) { + return 1; + } + + function getAuthorisedSignerIndex( + address signerAddress + ) public view virtual override returns (uint8) { + if (signerAddress == 0x0C39486f770B26F5527BBBf942726537986Cd7eb) { + return 0; + } else { + revert SignerNotAuthorised(signerAddress); + } + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithoutRoundsMainDemo.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithoutRoundsMainDemo.sol new file mode 100644 index 0000000..1ca91f5 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithoutRoundsMainDemo.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {PriceFeedsAdapterWithoutRounds} from "../without-rounds/PriceFeedsAdapterWithoutRounds.sol"; + +abstract contract PriceFeedsAdapterWithoutRoundsMainDemo is PriceFeedsAdapterWithoutRounds { + function getUniqueSignersThreshold() public view virtual override returns (uint8) { + return 1; + } + + function getAuthorisedSignerIndex(address signerAddress) + public + view + virtual + override + returns (uint8) + { + if (signerAddress == 0x0C39486f770B26F5527BBBf942726537986Cd7eb) { + return 0; + } else { + revert SignerNotAuthorised(signerAddress); + } + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapter.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapter.sol new file mode 100644 index 0000000..9de4eb6 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapter.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {SinglePriceFeedAdapter} from "../without-rounds/SinglePriceFeedAdapter.sol"; + +contract VSTPriceFeedsAdapter is SinglePriceFeedAdapter { + uint256 internal constant BIT_MASK_TO_CHECK_CIRCUIT_BREAKER_FLAG = 0x0000000000000000000000000100000000000000000000000000000000000000; + + error InvalidSignersCount(uint256 signersCount); + error CircuitBreakerTripped(); + + function getSingleDataFeedId() public pure override returns (bytes32) { + return bytes32("VST"); + } + + function getUniqueSignersThreshold() public view virtual override returns (uint8) { + return 2; // 2 out of 3 + } + + function aggregateValues(uint256[] memory values) public pure override returns (uint256) { + if (values.length != 2) { + revert InvalidSignersCount(values.length); + } + + _checkCircuitBreaker(values[0]); + _checkCircuitBreaker(values[1]); + + return (values[0] + values[1]) / 2; + } + + function _checkCircuitBreaker(uint256 value) internal pure { + if (value & BIT_MASK_TO_CHECK_CIRCUIT_BREAKER_FLAG > 0) { + revert CircuitBreakerTripped(); + } + } + + function getAuthorisedSignerIndex( + address signerAddress + ) public view virtual override returns (uint8) { + if (signerAddress == 0xf7a873ff07E1d021ae808a28e6862f821148c789) { + return 0; + } else if (signerAddress == 0x827Cc644d3f33d55075354875A961aC8B9EB7Cc8) { + return 1; + } else if (signerAddress == 0x1C31b3eA83F48A6E550938d295893514A9e99Eca) { + return 2; + } else { + revert SignerNotAuthorised(signerAddress); + } + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapterWithoutRoundsMainDemo.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapterWithoutRoundsMainDemo.sol new file mode 100644 index 0000000..6bda523 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapterWithoutRoundsMainDemo.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {SinglePriceFeedAdapter} from "../without-rounds/SinglePriceFeedAdapter.sol"; + +contract VSTPriceFeedsAdapterWithoutRoundsMainDemo is SinglePriceFeedAdapter { + function getSingleDataFeedId() public pure override returns (bytes32) { + return bytes32("VST"); + } + + function getUniqueSignersThreshold() public view virtual override returns (uint8) { + return 1; + } + + function getAuthorisedSignerIndex(address signerAddress) + public + view + virtual + override + returns (uint8) + { + if (signerAddress == 0x0C39486f770B26F5527BBBf942726537986Cd7eb) { + return 0; + } else { + revert SignerNotAuthorised(signerAddress); + } + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeed.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeed.sol new file mode 100644 index 0000000..5bc60af --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeed.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; +import {IPriceFeedLegacy} from "./IPriceFeedLegacy.sol"; + +/** + * @title Complete price feed interface + * @author The Redstone Oracles team + * @dev All required public functions that must be implemented + * by each Redstone PriceFeed contract + */ +interface IPriceFeed is IPriceFeedLegacy, AggregatorV3Interface { + /** + * @notice Returns data feed identifier for the PriceFeed contract + * @return dataFeedId The identifier of the data feed + */ + function getDataFeedId() external view returns (bytes32); +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeedLegacy.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeedLegacy.sol new file mode 100644 index 0000000..e45c009 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeedLegacy.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +/** + * @title Interface with the old Chainlink Price Feed functions + * @author The Redstone Oracles team + * @dev There are some projects (e.g. gmx-contracts) that still + * rely on some legacy functions + */ +interface IPriceFeedLegacy { + /** + * @notice Old Chainlink function for getting the number of latest round + * @return latestRound The number of the latest update round + */ + function latestRound() external view returns (uint80); + + + /** + * @notice Old Chainlink function for getting the latest successfully reported value + * @return latestAnswer The latest successfully reported value + */ + function latestAnswer() external view returns (int256); +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol new file mode 100644 index 0000000..1eeddcf --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {PriceFeedBase, PriceFeedWithRounds} from "./PriceFeedWithRounds.sol"; +import {PriceFeedsAdapterBase, PriceFeedsAdapterWithRounds} from "./PriceFeedsAdapterWithRounds.sol"; +import {IRedstoneAdapter} from "../../core/IRedstoneAdapter.sol"; +import {MergedPriceFeedAdapterCommon} from "../MergedPriceFeedAdapterCommon.sol"; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +abstract contract MergedPriceFeedAdapterWithRounds is + MergedPriceFeedAdapterCommon, + PriceFeedWithRounds, + PriceFeedsAdapterWithRounds +{ + function initialize() public override(PriceFeedBase, PriceFeedsAdapterBase) initializer { + // We don't have storage variables, but we keep this function + // Because it is used for contract setup in upgradable contracts + } + + function getPriceFeedAdapter() public view virtual override(MergedPriceFeedAdapterCommon, PriceFeedBase) returns (IRedstoneAdapter) { + return super.getPriceFeedAdapter(); + } + + function getDataFeedIds() public view virtual override returns (bytes32[] memory dataFeedIds) { + dataFeedIds = new bytes32[](1); + dataFeedIds[0] = getDataFeedId(); + } + + function getDataFeedIndex(bytes32 dataFeedId) public view virtual override returns (uint256) { + if (dataFeedId == getDataFeedId()) { + return 0; + } else { + revert DataFeedIdNotFound(dataFeedId); + } + } + + function _emitEventAfterSingleValueUpdate(uint256 newValue) internal virtual { + emit AnswerUpdated(SafeCast.toInt256(newValue), getLatestRoundId(), block.timestamp); + } + + function _validateAndUpdateDataFeedsValues( + bytes32[] memory dataFeedIdsArray, + uint256[] memory values + ) internal virtual override { + if (dataFeedIdsArray.length != 1 || values.length != 1) { + revert CannotUpdateMoreThanOneDataFeed(); + } + PriceFeedsAdapterWithRounds._validateAndUpdateDataFeedsValues(dataFeedIdsArray, values); + _emitEventAfterSingleValueUpdate(values[0]); + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedWithRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedWithRounds.sol new file mode 100644 index 0000000..f757858 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedWithRounds.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {PriceFeedsAdapterWithRounds} from "./PriceFeedsAdapterWithRounds.sol"; +import {PriceFeedBase} from "../PriceFeedBase.sol"; + +/** + * @title Implementation of a price feed contract with rounds support + * @author The Redstone Oracles team + * @dev This contract is abstract. The actual contract instance + * must implement the following functions: + * - getDataFeedId + * - getPriceFeedAdapter + */ +abstract contract PriceFeedWithRounds is PriceFeedBase { + uint256 internal constant UINT80_MAX = uint256(type(uint80).max); + + error UnsafeUint256ToUint80Conversion(uint256 value); + + function getPriceFeedAdapterWithRounds() public view returns(PriceFeedsAdapterWithRounds) { + return PriceFeedsAdapterWithRounds(address(getPriceFeedAdapter())); + } + + /** + * @notice Old Chainlink function for getting the number of latest round + * @return latestRound The number of the latest successful round + */ + function latestRound() public view override returns (uint80) { + uint256 latestRoundUint256 = getPriceFeedAdapterWithRounds().getLatestRoundId(); + + if (latestRoundUint256 > UINT80_MAX) { + revert UnsafeUint256ToUint80Conversion(latestRoundUint256); + } + + return uint80(latestRoundUint256); + } + + /** + * @notice Returns details for the given round + * @param roundId Requested round identifier + */ + function getRoundData(uint80 requestedRoundId) public view override returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { + (uint256 dataFeedValue, uint128 roundDataTimestamp, uint128 roundBlockTimestamp) = getPriceFeedAdapterWithRounds().getRoundDataFromAdapter( + getDataFeedId(), + requestedRoundId + ); + roundId = requestedRoundId; + + if (dataFeedValue > INT256_MAX) { + revert UnsafeUintToIntConversion(dataFeedValue); + } + + answer = int256(dataFeedValue); + startedAt = roundDataTimestamp / 1000; // convert to seconds + updatedAt = roundBlockTimestamp; + + // We want to be compatible with Chainlink's interface + // And in our case the roundId is always equal to answeredInRound + answeredInRound = requestedRoundId; + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedsAdapterWithRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedsAdapterWithRounds.sol new file mode 100644 index 0000000..d560432 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedsAdapterWithRounds.sol @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {PriceFeedsAdapterBase} from "../PriceFeedsAdapterBase.sol"; + +/** + * @title Price feeds adapter contract with rounds support + * @author The Redstone Oracles team + * @dev This contract is abstract. The actual contract instance + * must implement the following functions: + * - getDataFeedIds + * - getUniqueSignersThreshold + * - getAuthorisedSignerIndex + * + * We also recommend to override `getDataFeedIndex` function with hardcoded + * values, as it can significantly reduce gas usage + */ +abstract contract PriceFeedsAdapterWithRounds is PriceFeedsAdapterBase { + bytes32 constant VALUES_MAPPING_STORAGE_LOCATION = 0x4dd0c77efa6f6d590c97573d8c70b714546e7311202ff7c11c484cc841d91bfc; // keccak256("RedStone.oracleValuesMapping"); + bytes32 constant ROUND_TIMESTAMPS_MAPPING_STORAGE_LOCATION = 0x207e00944d909d1224f0c253d58489121d736649f8393199f55eecf4f0cf3eb0; // keccak256("RedStone.roundTimestampMapping"); + bytes32 constant LATEST_ROUND_ID_STORAGE_LOCATION = 0xc68d7f1ee07d8668991a8951e720010c9d44c2f11c06b5cac61fbc4083263938; // keccak256("RedStone.latestRoundId"); + + error RoundNotFound(uint256 roundId); + + /** + * @dev Saved new round data to the storage + * @param dataFeedIdsArray Array of all data feeds identifiers + * @param values The reported values that are validated and reported + */ + function _validateAndUpdateDataFeedsValues( + bytes32[] memory dataFeedIdsArray, + uint256[] memory values + ) internal virtual override { + _incrementLatestRoundId(); + _updatePackedTimestampsForLatestRound(); + + for (uint256 i = 0; i < dataFeedIdsArray.length;) { + _validateAndUpdateDataFeedValue(dataFeedIdsArray[i], values[i]); + unchecked { i++; } // reduces gas costs + } + } + + /** + * @dev Helpful virtual function for handling value validation and updating + * @param dataFeedId The data feed identifier + * @param dataFeedValue Proposed value for the data feed + */ + function _validateAndUpdateDataFeedValue(bytes32 dataFeedId, uint256 dataFeedValue) internal virtual override { + validateDataFeedValue(dataFeedId, dataFeedValue); + bytes32 locationInStorage = _getValueLocationInStorage(dataFeedId, getLatestRoundId()); + assembly { + sstore(locationInStorage, dataFeedValue) + } + } + + /** + * @dev [HIGH RISK] Returns the value for a given data feed from the latest round + * without validation. Important! Using this function instead of `getValueForDataFeed` + * may cause significant risk for your smart contracts + * @param dataFeedId The data feed identifier + * @return dataFeedValue Unvalidated value of the latest successful update + */ + function getValueForDataFeedUnsafe(bytes32 dataFeedId) public view override returns (uint256 dataFeedValue) { + return getValueForDataFeedAndRound(dataFeedId, getLatestRoundId()); + } + + /** + * @dev Returns value for the requested data feed from the given round + * @param dataFeedId The data feed identifier + * @param roundId The number of the requested round + * @return dataFeedValue value for the requested data feed from the given round + */ + function getValueForDataFeedAndRound(bytes32 dataFeedId, uint256 roundId) public view returns (uint256 dataFeedValue) { + bytes32 locationInStorage = _getValueLocationInStorage(dataFeedId, roundId); + assembly { + dataFeedValue := sload(locationInStorage) + } + } + + + /** + * @notice Returns data from the latest successful round + * @return latestRoundId + * @return latestRoundDataTimestamp + * @return latestRoundBlockTimestamp + */ + function getLatestRoundParams() public view returns ( uint256 latestRoundId, uint128 latestRoundDataTimestamp, uint128 latestRoundBlockTimestamp) { + latestRoundId = getLatestRoundId(); + uint256 packedRoundTimestamps = getPackedTimestampsForRound(latestRoundId); + (latestRoundDataTimestamp, latestRoundBlockTimestamp) = _unpackTimestamps( + packedRoundTimestamps + ); + } + + + /** + * @notice Returns details for the given round and data feed + * @param dataFeedId Requested data feed + * @param roundId Requested round identifier + * @return dataFeedValue + * @return roundDataTimestamp + * @return roundBlockTimestamp + */ + function getRoundDataFromAdapter(bytes32 dataFeedId, uint256 roundId) public view returns (uint256 dataFeedValue, uint128 roundDataTimestamp, uint128 roundBlockTimestamp) { + if (roundId > getLatestRoundId() || roundId == 0) { + revert RoundNotFound(roundId); + } + + dataFeedValue = getValueForDataFeedAndRound(dataFeedId, roundId); + validateDataFeedValue(dataFeedId, dataFeedValue); + uint256 packedRoundTimestamps = getPackedTimestampsForRound(roundId); + (roundDataTimestamp, roundBlockTimestamp) = _unpackTimestamps(packedRoundTimestamps); + } + + + /** + * @dev Helpful function for getting storage location for requested value + * @param dataFeedId Requested data feed identifier + * @param roundId Requested round number + * @return locationInStorage + */ + function _getValueLocationInStorage(bytes32 dataFeedId, uint256 roundId) private pure returns (bytes32) { + return keccak256(abi.encode(dataFeedId, roundId, VALUES_MAPPING_STORAGE_LOCATION)); + } + + + /** + * @dev Helpful function for getting storage location for round timestamps + * @param roundId Requested round number + * @return locationInStorage + */ + function _getRoundTimestampsLocationInStorage(uint256 roundId) private pure returns (bytes32) { + return keccak256(abi.encode(roundId, ROUND_TIMESTAMPS_MAPPING_STORAGE_LOCATION)); + } + + + /** + * @notice Returns latest successful round number + * @return latestRoundId + */ + function getLatestRoundId() public view returns (uint256 latestRoundId) { + assembly { + latestRoundId := sload(LATEST_ROUND_ID_STORAGE_LOCATION) + } + } + + /** + * @dev Helpful function for incrementing the latest round number by 1 in + * the contract storage + */ + function _incrementLatestRoundId() private { + uint256 latestRoundId = getLatestRoundId(); + assembly { + sstore(LATEST_ROUND_ID_STORAGE_LOCATION, add(latestRoundId, 1)) + } + } + + /** + * @notice Returns timestamps related to the given round packed into one number + * @param roundId Requested round number + * @return roundTimestamps + */ + function getPackedTimestampsForRound(uint256 roundId) public view returns (uint256 roundTimestamps) { + bytes32 locationInStorage = _getRoundTimestampsLocationInStorage(roundId); + assembly { + roundTimestamps := sload(locationInStorage) + } + } + + + /** + * @dev Saves packed timestamps (data and block.timestamp) in the contract storage + */ + function _updatePackedTimestampsForLatestRound() private { + uint256 packedTimestamps = getPackedTimestampsFromLatestUpdate(); + uint256 latestRoundId = getLatestRoundId(); + bytes32 locationInStorage = _getRoundTimestampsLocationInStorage(latestRoundId); + assembly { + sstore(locationInStorage, packedTimestamps) + } + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/MergedPriceFeedAdapterWithoutRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/MergedPriceFeedAdapterWithoutRounds.sol new file mode 100644 index 0000000..1ee201a --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/MergedPriceFeedAdapterWithoutRounds.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {PriceFeedsAdapterBase, PriceFeedsAdapterWithoutRounds} from "./PriceFeedsAdapterWithoutRounds.sol"; +import {PriceFeedBase, PriceFeedWithoutRounds} from "./PriceFeedWithoutRounds.sol"; +import {IRedstoneAdapter} from "../../core/IRedstoneAdapter.sol"; +import {MergedPriceFeedAdapterCommon} from "../MergedPriceFeedAdapterCommon.sol"; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +abstract contract MergedPriceFeedAdapterWithoutRounds is + MergedPriceFeedAdapterCommon, + PriceFeedsAdapterWithoutRounds, + PriceFeedWithoutRounds +{ + + function initialize() public override(PriceFeedBase, PriceFeedsAdapterBase) initializer { + // We don't have storage variables, but we keep this function + // Because it is used for contract setup in upgradable contracts + } + + function getPriceFeedAdapter() public view virtual override(MergedPriceFeedAdapterCommon, PriceFeedBase) returns (IRedstoneAdapter) { + return super.getPriceFeedAdapter(); + } + + function getDataFeedIds() public view virtual override returns (bytes32[] memory dataFeedIds) { + dataFeedIds = new bytes32[](1); + dataFeedIds[0] = getDataFeedId(); + } + + function getDataFeedIndex(bytes32 dataFeedId) public view virtual override returns (uint256) { + if (dataFeedId == getDataFeedId()) { + return 0; + } else { + revert DataFeedIdNotFound(dataFeedId); + } + } + + function _emitEventAfterSingleValueUpdate(uint256 newValue) internal virtual { + emit AnswerUpdated(SafeCast.toInt256(newValue), latestRound(), block.timestamp); + } + + function _validateAndUpdateDataFeedsValues( + bytes32[] memory dataFeedIdsArray, + uint256[] memory values + ) internal virtual override { + if (dataFeedIdsArray.length != 1 || values.length != 1) { + revert CannotUpdateMoreThanOneDataFeed(); + } + _validateAndUpdateDataFeedValue(dataFeedIdsArray[0], values[0]); + _emitEventAfterSingleValueUpdate(values[0]); + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedWithoutRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedWithoutRounds.sol new file mode 100644 index 0000000..3291f8b --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedWithoutRounds.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.14; + +import {PriceFeedBase} from "../PriceFeedBase.sol"; + +/** + * @title Implementation of a price feed contract without rounds support + * @author The Redstone Oracles team + * @dev This contract is abstract. The actual contract instance + * must implement the following functions: + * - getDataFeedId + * - getPriceFeedAdapter + */ +abstract contract PriceFeedWithoutRounds is PriceFeedBase { + uint80 constant DEFAULT_ROUND = 1; + + error GetRoundDataCanBeOnlyCalledWithLatestRound(uint80 requestedRoundId); + + /** + * @dev We always return 1, since we do not support rounds in this contract + */ + function latestRound() public pure override returns (uint80) { + return DEFAULT_ROUND; + } + + /** + * @dev There are possible use cases that some contracts don't need values from old rounds + * but still rely on `getRoundData` or `latestRounud` functions + */ + function getRoundData(uint80 requestedRoundId) public view override returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { + if (requestedRoundId != latestRound()) { + revert GetRoundDataCanBeOnlyCalledWithLatestRound(requestedRoundId); + } + return latestRoundData(); + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedsAdapterWithoutRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedsAdapterWithoutRounds.sol new file mode 100644 index 0000000..335a8e8 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedsAdapterWithoutRounds.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {PriceFeedsAdapterBase} from "../PriceFeedsAdapterBase.sol"; + +/** + * @title Implementation of a price feeds adapter without rounds support + * @author The Redstone Oracles team + * @dev This contract is abstract, the following functions should be + * implemented in the actual contract before deployment: + * - getDataFeedIds + * - getUniqueSignersThreshold + * - getAuthorisedSignerIndex + * + * We also recommend to override `getDataFeedIndex` function with hardcoded + * values, as it can significantly reduce gas usage + */ +abstract contract PriceFeedsAdapterWithoutRounds is PriceFeedsAdapterBase { + bytes32 constant VALUES_MAPPING_STORAGE_LOCATION = 0x4dd0c77efa6f6d590c97573d8c70b714546e7311202ff7c11c484cc841d91bfc; // keccak256("RedStone.oracleValuesMapping"); + + /** + * @dev Helpful virtual function for handling value validation and saving + * @param dataFeedId The data feed identifier + * @param dataFeedValue Proposed value for the data feed + */ + function _validateAndUpdateDataFeedValue(bytes32 dataFeedId, uint256 dataFeedValue) internal override virtual { + validateDataFeedValue(dataFeedId, dataFeedValue); + bytes32 locationInStorage = _getValueLocationInStorage(dataFeedId); + assembly { + sstore(locationInStorage, dataFeedValue) + } + } + + /** + * @dev [HIGH RISK] Returns the latest value for a given data feed without validation + * Important! Using this function instead of `getValueForDataFeed` may cause + * significant risk for your smart contracts + * @param dataFeedId The data feed identifier + * @return dataFeedValue Unvalidated value of the latest successful update + */ + function getValueForDataFeedUnsafe(bytes32 dataFeedId) public view virtual override returns (uint256 dataFeedValue) { + bytes32 locationInStorage = _getValueLocationInStorage(dataFeedId); + assembly { + dataFeedValue := sload(locationInStorage) + } + } + + /** + * @dev Helpful function for getting storage location for the requested data feed + * @param dataFeedId Requested data feed identifier + * @return locationInStorage + */ + function _getValueLocationInStorage(bytes32 dataFeedId) private pure returns (bytes32) { + return keccak256(abi.encode(dataFeedId, VALUES_MAPPING_STORAGE_LOCATION)); + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapter.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapter.sol new file mode 100644 index 0000000..52dd184 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapter.sol @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {PriceFeedsAdapterBase} from "../PriceFeedsAdapterBase.sol"; + +/** + * @title Price feed adapter for one specific data feed without rounds support + * @author The Redstone Oracles team + * @dev This version works only with a single data feed. It's abstract and + * the following functions should be implemented in the actual contract + * before deployment: + * - getSingleDataFeedId + * - getUniqueSignersThreshold + * - getAuthorisedSignerIndex + * + * This contract stores the value along with timestamps in a single storage slot + * 32 bytes = 6 bytes (Data timestamp ) + 6 bytes (Block timestamp) + 20 bytes (Value) + */ +abstract contract SinglePriceFeedAdapter is PriceFeedsAdapterBase { + + bytes32 internal constant DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION = 0x632f4a585e47073d66129e9ebce395c9b39d8a1fc5b15d4d7df2e462fb1fccfa; // keccak256("RedStone.singlePriceFeedAdapter"); + uint256 internal constant MAX_VALUE_WITH_20_BYTES = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff; + uint256 internal constant BIT_MASK_TO_CLEAR_LAST_20_BYTES = 0xffffffffffffffffffffffff0000000000000000000000000000000000000000; + uint256 internal constant MAX_NUMBER_FOR_48_BITS = 0x0000000000000000000000000000000000000000000000000000ffffffffffff; + + error DataFeedValueTooBig(uint256 valueForDataFeed); + + /** + * @notice Returns the only data feed identifer supported by the adapter + * @dev This function should be overriden in the derived contracts, + * but `getDataFeedIds` and `getDataFeedIndex` should not (and can not) + * @return dataFeedId The only data feed identifer supported by the adapter + */ + function getSingleDataFeedId() public view virtual returns (bytes32); + + /** + * @notice Returns identifiers of all data feeds supported by the Adapter contract + * In this case - an array with only one element + * @return dataFeedIds + */ + function getDataFeedIds() public view virtual override returns (bytes32[] memory dataFeedIds) { + dataFeedIds = new bytes32[](1); + dataFeedIds[0] = getSingleDataFeedId(); + } + + /** + * @dev Returns 0 if dataFeedId is the one, otherwise reverts + * @param dataFeedId The identifier of the requested data feed + */ + function getDataFeedIndex(bytes32 dataFeedId) public virtual view override returns(uint256) { + if (dataFeedId == getSingleDataFeedId()) { + return 0; + } + revert DataFeedIdNotFound(dataFeedId); + } + + /** + * @dev Reverts if proposed value for the proposed data feed id is invalid + * By default, it checks if the value is not equal to 0 and if it fits to 20 bytes + * Because other 12 bytes are used for storing the packed timestamps + * @param dataFeedId The data feed identifier + * @param valueForDataFeed Proposed value for the data feed + */ + function validateDataFeedValue(bytes32 dataFeedId, uint256 valueForDataFeed) public pure virtual override { + if (valueForDataFeed == 0) { + revert DataFeedValueCannotBeZero(dataFeedId); + } + if (valueForDataFeed > MAX_VALUE_WITH_20_BYTES) { + revert DataFeedValueTooBig(valueForDataFeed); + } + } + + /** + * @dev [HIGH RISK] Returns the latest value for a given data feed without validation + * Important! Using this function instead of `getValueForDataFeed` may cause + * significant risk for your smart contracts + * @return dataFeedValue Unvalidated value of the latest successful update + */ + function getValueForDataFeedUnsafe(bytes32) public view virtual override returns (uint256 dataFeedValue) { + uint160 dataFeedValueCompressed; + assembly { + dataFeedValueCompressed := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) + } + return uint256(dataFeedValueCompressed); + } + + /** + * @notice Returns timestamps of the latest successful update + * @dev Timestamps here use only 6 bytes each and are packed together with the value + * @return dataTimestamp timestamp (usually in milliseconds) from the signed data packages + * @return blockTimestamp timestamp of the block when the update has happened + */ + function getTimestampsFromLatestUpdate() public view virtual override returns (uint128 dataTimestamp, uint128 blockTimestamp) { + uint256 latestUpdateDetails; + assembly { + latestUpdateDetails := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) + } + dataTimestamp = uint128(latestUpdateDetails >> 208); // first 48 bits + blockTimestamp = uint128((latestUpdateDetails << 48) >> 208); // next 48 bits + } + + /** + * @dev Validates and saves the value in the contract storage + * It uses only 20 right bytes of the corresponding storage slot + * @param dataFeedId The data feed identifier + * @param dataFeedValue Proposed value for the data feed + */ + function _validateAndUpdateDataFeedValue(bytes32 dataFeedId, uint256 dataFeedValue) internal virtual override { + validateDataFeedValue(dataFeedId, dataFeedValue); + assembly { + let curValueFromStorage := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) + curValueFromStorage := and(curValueFromStorage, BIT_MASK_TO_CLEAR_LAST_20_BYTES) // clear dataFeedValue bits + curValueFromStorage := or(curValueFromStorage, dataFeedValue) + sstore(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION, curValueFromStorage) + } + } + + /** + * @dev Helpful function that packs and saves timestamps in the 12 left bytes of the + * storage slot reserved for storing details about the latest update + * @param dataPackagesTimestamp Timestamp from the signed data packages, + * extracted from the RedStone payload in calldata + */ + function _saveTimestampsOfCurrentUpdate(uint256 dataPackagesTimestamp) internal virtual override { + uint256 blockTimestamp = getBlockTimestamp(); + + if (dataPackagesTimestamp > MAX_NUMBER_FOR_48_BITS) { + revert DataTimestampIsTooBig(dataPackagesTimestamp); + } + + if (blockTimestamp > MAX_NUMBER_FOR_48_BITS) { + revert BlockTimestampIsTooBig(blockTimestamp); + } + + uint256 timestampsPacked = dataPackagesTimestamp << 208; // 48 first bits for dataPackagesTimestamp + timestampsPacked |= (blockTimestamp << 160); // 48 next bits for blockTimestamp + assembly { + let latestUpdateDetails := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) + latestUpdateDetails := and(latestUpdateDetails, MAX_VALUE_WITH_20_BYTES) // clear timestamp bits + sstore(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION, or(latestUpdateDetails, timestampsPacked)) + } + } +} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapterWithClearing.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapterWithClearing.sol new file mode 100644 index 0000000..2c88441 --- /dev/null +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapterWithClearing.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.14; + +import {SinglePriceFeedAdapter} from "./SinglePriceFeedAdapter.sol"; + +/** + * @title [HIGH RISK] Price feed adapter for one specific data feed without + * rounds support, with storage clearing feature + * @author The Redstone Oracles team + * @dev This contract has a significant security risk, as it allows to + * update oracle data with older timestamps then the previous one. It can + * open many opportunities for attackers to manipulate the values and use it + * for arbitrage. Use it only if you know what you are doing very well + */ +abstract contract SinglePriceFeedAdapterWithClearing is SinglePriceFeedAdapter { + + bytes32 internal constant TEMP_DATA_TIMESTAMP_STORAGE_LOCATION = 0x9ba2e81f7980c774323961547312ae2319fc1970bb8ec60c86c869e9a1c1c0d2; // keccak256("RedStone.tempDataTimestampStorageLocation"); + uint256 internal constant MAX_VALUE_WITH_26_BYTES = 0x000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint256 internal constant BIT_MASK_TO_CLEAR_LAST_26_BYTES = 0xffffffffffff0000000000000000000000000000000000000000000000000000; + + function validateDataFeedValue(bytes32 dataFeedId, uint256 valueForDataFeed) public pure virtual override { + if (valueForDataFeed == 0) { + revert DataFeedValueCannotBeZero(dataFeedId); + } + if (valueForDataFeed > MAX_VALUE_WITH_26_BYTES) { + revert DataFeedValueTooBig(valueForDataFeed); + } + } + + function getValueForDataFeedUnsafe(bytes32) public view override virtual returns (uint256 dataFeedValue) { + uint208 dataFeedValueCompressed; + assembly { + dataFeedValueCompressed := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) + } + return uint256(dataFeedValueCompressed); + } + + function getTimestampsFromLatestUpdate() public view override virtual returns (uint128 dataTimestamp, uint128 blockTimestamp) { + uint256 latestUpdateDetails; + assembly { + latestUpdateDetails := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) + } + blockTimestamp = uint128(latestUpdateDetails >> 208); // first 48 bits + dataTimestamp = blockTimestamp * 1000; // It's a hack, because we don't store dataTimestamp in storage in this version of adapter + } + + function getDataTimestampFromLatestUpdate() public view virtual override returns (uint256 lastDataTimestamp) { + assembly { + lastDataTimestamp := sload(TEMP_DATA_TIMESTAMP_STORAGE_LOCATION) + } + } + + function _validateAndUpdateDataFeedValue(bytes32 dataFeedId, uint256 dataFeedValue) virtual internal override { + validateDataFeedValue(dataFeedId, dataFeedValue); + uint256 blockTimestampCompressedAndShifted = getBlockTimestamp() << 208; // Move value to the first 48 bits + assembly { + // Save timestamp and data feed value + let timestampAndValue := or(blockTimestampCompressedAndShifted, dataFeedValue) + sstore(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION, timestampAndValue) + + // Clear temp data timestamp, it refunds 19.9k gas + sstore(TEMP_DATA_TIMESTAMP_STORAGE_LOCATION, 0) + } + } + + function _saveTimestampsOfCurrentUpdate(uint256 dataPackagesTimestamp) virtual internal override { + assembly { + sstore(TEMP_DATA_TIMESTAMP_STORAGE_LOCATION, dataPackagesTimestamp) + } + } +} From d40df802f03b2180c71adbf7784163dd07d5e601 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 10:26:12 +0100 Subject: [PATCH 02/31] feat: remappings Signed-off-by: Pablo Maldonado --- remappings.txt | 2 ++ src/oracles/red.sol | 51 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/oracles/red.sol diff --git a/remappings.txt b/remappings.txt index 551d766..9b4cf8f 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,3 +1,5 @@ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ openzeppelin-contracts/=lib/openzeppelin-contracts/ +redstone-oracles-monorepo/=lib/RedStoneBaseContracts/redstone-oracles-monorepo +@openzeppelin/=lib/openzeppelin-contracts \ No newline at end of file diff --git a/src/oracles/red.sol b/src/oracles/red.sol new file mode 100644 index 0000000..4e868d7 --- /dev/null +++ b/src/oracles/red.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.17; + +import "redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol"; +contract RedstonePriceFeedWithRounds is MergedPriceFeedAdapterWithRounds { + bytes32 private immutable dataFeedId; + + error SignerNotAuthorised(address signer); + + constructor(bytes32 _dataFeedId) { + dataFeedId = _dataFeedId; + } + + function getDataFeedId() public view override returns (bytes32) { + return dataFeedId; + } + + function getDataServiceId() public pure override returns (string memory) { + return "redstone-primary-prod"; + } + + function getUniqueSignersThreshold() public pure override returns (uint8) { + return 3; + } + + function getAuthorisedSignerIndex( + address signerAddress + ) public view virtual override returns (uint8) { + if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { + return 0; + } else if ( + signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499 + ) { + return 1; + } else if ( + signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202 + ) { + return 2; + } else if ( + signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE + ) { + return 3; + } else if ( + signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de + ) { + return 4; + } else { + revert SignerNotAuthorised(signerAddress); + } + } +} From ca1a2e691680bf2efc8f3734b7d3c042a8142ed2 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 10:26:15 +0100 Subject: [PATCH 03/31] forge install: openzeppelin-contracts-upgradeable v5.0.2 --- .gitmodules | 3 +++ lib/openzeppelin-contracts-upgradeable | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/openzeppelin-contracts-upgradeable diff --git a/.gitmodules b/.gitmodules index e80ffd8..a3b6416 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/openzeppelin/openzeppelin-contracts +[submodule "lib/openzeppelin-contracts-upgradeable"] + path = lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 0000000..723f8ca --- /dev/null +++ b/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit 723f8cab09cdae1aca9ec9cc1cfa040c2d4b06c1 From 84fbaf7b88846cb25ecdd70a432e03d4c880c8ed Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 11:02:12 +0100 Subject: [PATCH 04/31] fixes Signed-off-by: Pablo Maldonado --- .../evm-connector/contracts/core/CalldataExtractor.sol | 2 +- .../evm-connector/contracts/core/RedstoneConsumerBase.sol | 2 +- .../contracts/core/RedstoneConsumerBytesBase.sol | 2 +- .../evm-connector/contracts/libs/NumericArrayLib.sol | 2 +- .../on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol | 2 +- .../contracts/price-feeds/PriceFeedsAdapterBase.sol | 2 +- lib/openzeppelin-contracts-upgradeable | 2 +- remappings.txt | 5 +++-- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol index f5804ac..d1c5f7a 100644 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.4; -import "../../../../@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "./RedstoneConstants.sol"; diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol index d779b26..b15630a 100644 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.4; -import "../../../../@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "./RedstoneConstants.sol"; import "./RedstoneDefaultsLib.sol"; diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol index 057db05..f096021 100644 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.4; -import "../../../../@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "./RedstoneConsumerBase.sol"; diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol index fcbffbb..25ccdf0 100644 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol +++ b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.4; -import "../../../../@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; library NumericArrayLib { // This function sort array in memory using bubble sort algorithm, diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol index bad9e52..88e9ff4 100644 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.14; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {Initializable} from "openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol"; import {IRedstoneAdapter} from "../core/IRedstoneAdapter.sol"; import {IPriceFeed} from "./interfaces/IPriceFeed.sol"; diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol index 18468e8..5974b7e 100644 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol +++ b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.14; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {Initializable} from "openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol"; import {RedstoneAdapterBase} from "../core/RedstoneAdapterBase.sol"; /** diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable index 723f8ca..5c0b797 160000 --- a/lib/openzeppelin-contracts-upgradeable +++ b/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit 723f8cab09cdae1aca9ec9cc1cfa040c2d4b06c1 +Subproject commit 5c0b7972905f2cba8aafb6fafc056d5105947299 diff --git a/remappings.txt b/remappings.txt index 9b4cf8f..d07604c 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,5 +1,6 @@ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ -openzeppelin-contracts/=lib/openzeppelin-contracts/ redstone-oracles-monorepo/=lib/RedStoneBaseContracts/redstone-oracles-monorepo -@openzeppelin/=lib/openzeppelin-contracts \ No newline at end of file +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ +openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ + From 35fecdcc928a676d276621f0bc839a0bd028326c Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 11:02:17 +0100 Subject: [PATCH 05/31] forge install: openzeppelin-contracts-upgradeable release-v4.5 --- lib/openzeppelin-contracts-upgradeable | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable index 5c0b797..5ffedf4 160000 --- a/lib/openzeppelin-contracts-upgradeable +++ b/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit 5c0b7972905f2cba8aafb6fafc056d5105947299 +Subproject commit 5ffedf46f77bed03e714913df1beff2d71611f0a From 3f422bfd560aebb2f43254e2809a24df24743dd2 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 11:34:10 +0100 Subject: [PATCH 06/31] fixes Signed-off-by: Pablo Maldonado --- ...ed.sol => RedstonePriceFeedWithRounds.sol} | 2 - test/unit/RedStoneOracle.sol | 42 ++++++++++++++++ test/unit/getRedstonePayload.js | 48 +++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) rename src/oracles/{red.sol => RedstonePriceFeedWithRounds.sol} (96%) create mode 100644 test/unit/RedStoneOracle.sol create mode 100644 test/unit/getRedstonePayload.js diff --git a/src/oracles/red.sol b/src/oracles/RedstonePriceFeedWithRounds.sol similarity index 96% rename from src/oracles/red.sol rename to src/oracles/RedstonePriceFeedWithRounds.sol index 4e868d7..5927f9b 100644 --- a/src/oracles/red.sol +++ b/src/oracles/RedstonePriceFeedWithRounds.sol @@ -5,8 +5,6 @@ import "redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feed contract RedstonePriceFeedWithRounds is MergedPriceFeedAdapterWithRounds { bytes32 private immutable dataFeedId; - error SignerNotAuthorised(address signer); - constructor(bytes32 _dataFeedId) { dataFeedId = _dataFeedId; } diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol new file mode 100644 index 0000000..977e044 --- /dev/null +++ b/test/unit/RedStoneOracle.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.17; +// import {RedstoneConsumerNumericBase} from "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol"; + +import {CommonTest} from "../Common.sol"; + +import {BaseController} from "../../src/controllers/BaseController.sol"; +import {ChainlinkSourceAdapter} from "../../src/adapters/source-adapters/ChainlinkSourceAdapter.sol"; +import {DecimalLib} from "../../src/adapters/lib/DecimalLib.sol"; +import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3Source.sol"; +import {RedstonePriceFeedWithRounds} from "../../src/oracles/RedstonePriceFeedWithRounds.sol"; + +contract MockRedstonePayload is CommonTest { + function getRedstonePayload( + // dataFeedId:value:decimals + string memory priceFeed + ) public returns (bytes memory) { + string[] memory args = new string[](3); + args[0] = "node"; + args[1] = "./test/unit/getRedstonePayload.js"; + args[2] = priceFeed; + + return vm.ffi(args); + } +} + +contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { + RedstonePriceFeedWithRounds redstoneOracle; + + function setUp() public { + redstoneOracle = new RedstonePriceFeedWithRounds(bytes32("BTC")); + } + + function testPushPrice() public { + + bytes memory redstonePayload = getRedstonePayload("BTC:120:8"); + + + + // redstoneOracle.updateDataFeedsValues(); + } +} diff --git a/test/unit/getRedstonePayload.js b/test/unit/getRedstonePayload.js new file mode 100644 index 0000000..a538cf9 --- /dev/null +++ b/test/unit/getRedstonePayload.js @@ -0,0 +1,48 @@ +const { appendFileSync } = require('fs'); +const { DataPackage, NumericDataPoint, RedstonePayload } = require('./lib/redstone-oracles-monorepo/packages/protocol/dist/src/index'); + +const args = process.argv.slice(2); + +const exit = (code, message) => { + process.stderr.write(message); + appendFileSync("./getRedstonePayload.log.txt", message); + process.exit(code); +} + +if (args.length === 0) { + exit(1, "You have to provide at least on dataFeed"); +} + +const dataFeeds = args[0].split(','); + +if (dataFeeds.length === 0) { + exit(2, "You have to provide at least on dataFeed"); +} + +const timestampMilliseconds = Date.now(); + +const PRIVATE_KEY_1 = '0x548e7c2fae09cc353ffe54ed40609d88a99fab24acfc81bfbf5cd9c11741643d'; + +const dataPoints = dataFeeds.map(arg => { + const [dataFeedId, value, decimals] = arg.split(':'); + + if (!dataFeedId || !value || !decimals) { + exit(3, "Input should have format: dataFeedId:value:decimals (example: BTC:120:8)"); + } + + return new NumericDataPoint({ dataFeedId, value: parseInt(value), decimals: parseInt(decimals) }) +}); + + +// Prepare unsigned data package +const dataPackage = new DataPackage(dataPoints, timestampMilliseconds); + +// Prepare signed data packages +const signedDataPackages = [ + dataPackage.sign(PRIVATE_KEY_1), +]; + +const payload = RedstonePayload.prepare(signedDataPackages, ""); + +process.stdout.write("0x" + payload) +process.exit(0); \ No newline at end of file From 94ef06cdaf7adfd84025aa4b12bb5a557386b3ad Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 12:31:09 +0100 Subject: [PATCH 07/31] wip Signed-off-by: Pablo Maldonado --- .gitignore | 2 + redstone/getRedstonePayload.js | 66 ++ redstone/getRedstonePayload.log.txt | 1 + redstone/package.json | 16 + redstone/yarn.lock | 992 ++++++++++++++++++++ remappings.txt | 2 +- src/oracles/RedstonePriceFeedWithRounds.sol | 4 +- test/unit/RedStoneOracle.sol | 60 +- test/unit/getRedstonePayload.js | 48 - 9 files changed, 1136 insertions(+), 55 deletions(-) create mode 100644 redstone/getRedstonePayload.js create mode 100644 redstone/getRedstonePayload.log.txt create mode 100644 redstone/package.json create mode 100644 redstone/yarn.lock delete mode 100644 test/unit/getRedstonePayload.js diff --git a/.gitignore b/.gitignore index 4908036..ba13553 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,5 @@ lcov.info scripts/node_modules scripts/dist scripts/contract-types + +node_modules diff --git a/redstone/getRedstonePayload.js b/redstone/getRedstonePayload.js new file mode 100644 index 0000000..19ae745 --- /dev/null +++ b/redstone/getRedstonePayload.js @@ -0,0 +1,66 @@ +const { appendFileSync } = require("fs"); +const { + DataPackage, + NumericDataPoint, + RedstonePayload, +} = require("@redstone-finance/protocol"); +const web3 = require("web3"); + +const args = process.argv.slice(2); + +const exit = (code, message) => { + process.stderr.write(message); + appendFileSync("./getRedstonePayload.log.txt", message); + process.exit(code); +}; + +if (args.length === 0) { + exit(1, "You have to provide at least on dataFeed"); +} + +const dataFeeds = args[0].split(","); + +if (dataFeeds.length === 0) { + exit(2, "You have to provide at least on dataFeed"); +} + +const timestampMilliseconds = Date.now(); + +const PRIVATE_KEY_1 = + "0x548e7c2fae09cc353ffe54ed40609d88a99fab24acfc81bfbf5cd9c11741643d"; + +const dataPoints = dataFeeds.map((arg) => { + const [dataFeedId, value, decimals] = arg.split(":"); + + if (!dataFeedId || !value || !decimals) { + exit( + 3, + "Input should have format: dataFeedId:value:decimals (example: BTC:120:8)" + ); + } + + return new NumericDataPoint({ + dataFeedId, + value: parseInt(value), + decimals: parseInt(decimals), + }); +}); + +// Prepare unsigned data package +const dataPackage = new DataPackage(dataPoints, timestampMilliseconds); + +// Prepare signed data packages +const signedDataPackages = [dataPackage.sign(PRIVATE_KEY_1)]; + +const payload = RedstonePayload.prepare(signedDataPackages, ""); + +// process.stdout.write("0x" + payload) + +const encodedData = web3.eth.abi.encodeParameters( + ["bytes", "uint256"], + ["0x" + payload, timestampMilliseconds] +); + +process.stdout.write(encodedData); + +process.exit(0); diff --git a/redstone/getRedstonePayload.log.txt b/redstone/getRedstonePayload.log.txt new file mode 100644 index 0000000..2de6487 --- /dev/null +++ b/redstone/getRedstonePayload.log.txt @@ -0,0 +1 @@ +You have to provide at least on dataFeed \ No newline at end of file diff --git a/redstone/package.json b/redstone/package.json new file mode 100644 index 0000000..63dcb4b --- /dev/null +++ b/redstone/package.json @@ -0,0 +1,16 @@ +{ + "name": "redstone", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@redstone-finance/protocol": "^0.5.1", + "web3": "^4.8.0" + } +} diff --git a/redstone/yarn.lock b/redstone/yarn.lock new file mode 100644 index 0000000..15d8a97 --- /dev/null +++ b/redstone/yarn.lock @@ -0,0 +1,992 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@adraffy/ens-normalize@^1.8.8": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" + integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== + +"@ethereumjs/rlp@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" + integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== + +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" + integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/contracts@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" + integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + +"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" + integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" + integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" + integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + +"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/providers@5.7.2": + version "5.7.2" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" + integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + bech32 "1.1.4" + ws "7.4.6" + +"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" + integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" + integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + hash.js "1.1.7" + +"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/solidity@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" + integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/units@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" + integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/wallet@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" + integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/json-wallets" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" + integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@noble/curves@1.3.0", "@noble/curves@~1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e" + integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA== + dependencies: + "@noble/hashes" "1.3.3" + +"@noble/hashes@1.3.3", "@noble/hashes@~1.3.2": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + +"@redstone-finance/protocol@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@redstone-finance/protocol/-/protocol-0.5.1.tgz#6fc47001ee386157a989d95c26dea6f2152b3422" + integrity sha512-MJ8BYuQ34xakfD7SA2OidtrLKDm5J5bZLrWZlsYj8hBs14h/vpFQsdbYJGbiIrN17aIb0c7rST0Q314T30BzJg== + dependencies: + ethers "^5.7.2" + +"@scure/base@~1.1.4": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d" + integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g== + +"@scure/bip32@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8" + integrity sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ== + dependencies: + "@noble/curves" "~1.3.0" + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.4" + +"@scure/bip39@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.2.tgz#f3426813f4ced11a47489cbcf7294aa963966527" + integrity sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA== + dependencies: + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.4" + +"@types/node@*": + version "20.12.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.12.tgz#7cbecdf902085cec634fdb362172dfe12b8f2050" + integrity sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw== + dependencies: + undici-types "~5.26.4" + +"@types/ws@8.5.3": + version "8.5.3" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" + integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== + dependencies: + "@types/node" "*" + +abitype@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.7.1.tgz#16db20abe67de80f6183cf75f3de1ff86453b745" + integrity sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ== + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +bech32@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + +bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +call-bind@^1.0.2, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +crc-32@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +cross-fetch@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" + integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== + dependencies: + node-fetch "^2.6.12" + +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +elliptic@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +ethereum-cryptography@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a" + integrity sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA== + dependencies: + "@noble/curves" "1.3.0" + "@noble/hashes" "1.3.3" + "@scure/bip32" "1.3.3" + "@scure/bip39" "1.2.2" + +ethers@^5.7.2: + version "5.7.2" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" + integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== + dependencies: + "@ethersproject/abi" "5.7.0" + "@ethersproject/abstract-provider" "5.7.0" + "@ethersproject/abstract-signer" "5.7.0" + "@ethersproject/address" "5.7.0" + "@ethersproject/base64" "5.7.0" + "@ethersproject/basex" "5.7.0" + "@ethersproject/bignumber" "5.7.0" + "@ethersproject/bytes" "5.7.0" + "@ethersproject/constants" "5.7.0" + "@ethersproject/contracts" "5.7.0" + "@ethersproject/hash" "5.7.0" + "@ethersproject/hdnode" "5.7.0" + "@ethersproject/json-wallets" "5.7.0" + "@ethersproject/keccak256" "5.7.0" + "@ethersproject/logger" "5.7.0" + "@ethersproject/networks" "5.7.1" + "@ethersproject/pbkdf2" "5.7.0" + "@ethersproject/properties" "5.7.0" + "@ethersproject/providers" "5.7.2" + "@ethersproject/random" "5.7.0" + "@ethersproject/rlp" "5.7.0" + "@ethersproject/sha2" "5.7.0" + "@ethersproject/signing-key" "5.7.0" + "@ethersproject/solidity" "5.7.0" + "@ethersproject/strings" "5.7.0" + "@ethersproject/transactions" "5.7.0" + "@ethersproject/units" "5.7.0" + "@ethersproject/wallet" "5.7.0" + "@ethersproject/web" "5.7.1" + "@ethersproject/wordlists" "5.7.0" + +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-typed-array@^1.1.3: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +isomorphic-ws@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== + +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +node-fetch@^2.6.12: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +scrypt-js@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +util@^0.12.5: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + +web3-core@^4.3.0, web3-core@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.3.2.tgz#f24b11d6a57dee527de8d42c89de2a439f0c4bed" + integrity sha512-uIMVd/j4BgOnwfpY8ZT+QKubOyM4xohEhFZXz9xB8wimXWMMlYVlIK/TbfHqFolS9uOerdSGhsMbcK9lETae8g== + dependencies: + web3-errors "^1.1.4" + web3-eth-accounts "^4.1.0" + web3-eth-iban "^4.0.7" + web3-providers-http "^4.1.0" + web3-providers-ws "^4.0.7" + web3-types "^1.3.1" + web3-utils "^4.1.0" + web3-validator "^2.0.3" + optionalDependencies: + web3-providers-ipc "^4.0.7" + +web3-errors@^1.1.3, web3-errors@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.1.4.tgz#5667a0a5f66fc936e101ef32032ccc1e8ca4d5a1" + integrity sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ== + dependencies: + web3-types "^1.3.1" + +web3-eth-abi@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.2.1.tgz#b1260dace8380221f12f4274af240c1dfed1045c" + integrity sha512-IE91WUhhiDpBtbkl/DHUoZz7z7T5FXvl3zPLkrxT+dNlOT+wni+US/67jQCLvJRbqf9ApQ26lVYry0bovFgyqA== + dependencies: + abitype "0.7.1" + web3-errors "^1.1.4" + web3-types "^1.6.0" + web3-utils "^4.2.3" + web3-validator "^2.0.5" + +web3-eth-accounts@^4.1.0, web3-eth-accounts@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.2.tgz#652d6e3daf4d6cb3fe67cec6a878e768f6e8b8e8" + integrity sha512-y0JynDeTDnclyuE9mShXLeEj+BCrPHxPHOyPCgTchUBQsALF9+0OhP7WiS3IqUuu0Hle5bjG2f5ddeiPtNEuLg== + dependencies: + "@ethereumjs/rlp" "^4.0.1" + crc-32 "^1.2.2" + ethereum-cryptography "^2.0.0" + web3-errors "^1.1.4" + web3-types "^1.6.0" + web3-utils "^4.2.3" + web3-validator "^2.0.5" + +web3-eth-contract@^4.3.0, web3-eth-contract@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.4.0.tgz#21760ef39ab95b34c55e7eaee316e0632e56cd21" + integrity sha512-pZ/w6Lb6ZDUUs7f5GCKXiHDAGGvt2tdwiHkvgmQTRnq9b0MEsUpteDyPYspHxKzQWLgbeK37jPb8zbQe4kE/Hg== + dependencies: + web3-core "^4.3.2" + web3-errors "^1.1.4" + web3-eth "^4.6.0" + web3-eth-abi "^4.2.1" + web3-types "^1.6.0" + web3-utils "^4.2.3" + web3-validator "^2.0.5" + +web3-eth-ens@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-4.2.0.tgz#8734b034efd48a735f7052fef0205653a78b84cb" + integrity sha512-qYj34te2UctoObt8rlEIY/t2MuTMiMiiHhO2JAHRGqSLCQ7b8DM3RpvkiiSB0N0ZyEn+CetZqJCTYb8DNKBS/g== + dependencies: + "@adraffy/ens-normalize" "^1.8.8" + web3-core "^4.3.2" + web3-errors "^1.1.4" + web3-eth "^4.5.0" + web3-eth-contract "^4.3.0" + web3-net "^4.0.7" + web3-types "^1.5.0" + web3-utils "^4.2.2" + web3-validator "^2.0.5" + +web3-eth-iban@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-4.0.7.tgz#ee504f845d7b6315f0be78fcf070ccd5d38e4aaf" + integrity sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ== + dependencies: + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + web3-validator "^2.0.3" + +web3-eth-personal@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz#b51628c560de550ca8b354fa784f9556aae6065c" + integrity sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw== + dependencies: + web3-core "^4.3.0" + web3-eth "^4.3.1" + web3-rpc-methods "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + web3-validator "^2.0.3" + +web3-eth@^4.3.1, web3-eth@^4.5.0, web3-eth@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.6.0.tgz#75c177e2bde88a613a6996fab515f104e16921da" + integrity sha512-8KtxlGsomovoFULqEpfixgmCpaJ2YIJGxbXUfezh2coXHjVgEopQhARYtKGClyV5kkdCIqwHS8Gvsm6TVNqH6Q== + dependencies: + setimmediate "^1.0.5" + web3-core "^4.3.2" + web3-errors "^1.1.4" + web3-eth-abi "^4.2.1" + web3-eth-accounts "^4.1.2" + web3-net "^4.0.7" + web3-providers-ws "^4.0.7" + web3-rpc-methods "^1.2.0" + web3-types "^1.6.0" + web3-utils "^4.2.3" + web3-validator "^2.0.5" + +web3-net@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-4.0.7.tgz#ed2c1bd700cf94be93a6dbd8bd8aa413d8681942" + integrity sha512-SzEaXFrBjY25iQGk5myaOfO9ZyfTwQEa4l4Ps4HDNVMibgZji3WPzpjq8zomVHMwi8bRp6VV7YS71eEsX7zLow== + dependencies: + web3-core "^4.3.0" + web3-rpc-methods "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + +web3-providers-http@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-4.1.0.tgz#8d7afda67d1d8542ca85b30f60a3d1fe1993b561" + integrity sha512-6qRUGAhJfVQM41E5t+re5IHYmb5hSaLc02BE2MaRQsz2xKA6RjmHpOA5h/+ojJxEpI9NI2CrfDKOAgtJfoUJQg== + dependencies: + cross-fetch "^4.0.0" + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + +web3-providers-ipc@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-4.0.7.tgz#9ec4c8565053af005a5170ba80cddeb40ff3e3d3" + integrity sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g== + dependencies: + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + +web3-providers-ws@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-4.0.7.tgz#7a78a0dcf077e0e802da524fbb37d080b356c14b" + integrity sha512-n4Dal9/rQWjS7d6LjyEPM2R458V8blRm0eLJupDEJOOIBhGYlxw5/4FthZZ/cqB7y/sLVi7K09DdYx2MeRtU5w== + dependencies: + "@types/ws" "8.5.3" + isomorphic-ws "^5.0.0" + web3-errors "^1.1.3" + web3-types "^1.3.0" + web3-utils "^4.0.7" + ws "^8.8.1" + +web3-rpc-methods@^1.1.3, web3-rpc-methods@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.2.0.tgz#761dcb036ab16edb2b03e80c11e3f5df24690345" + integrity sha512-CWJ/g4I4WyYvLkf21wCZAehdhU/VjX/OAPHnqF5/FPDJlogOsOnGXHqi1Z5AP+ocdt395PNubd8jyMMJoYGSBA== + dependencies: + web3-core "^4.3.2" + web3-types "^1.5.0" + web3-validator "^2.0.4" + +web3-types@^1.3.0, web3-types@^1.3.1, web3-types@^1.5.0, web3-types@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.6.0.tgz#ebe7f140c31f7cc0ad15f238ad7e7ac72797ff3b" + integrity sha512-qgOtADqlD5hw+KPKBUGaXAcdNLL0oh6qTeVgXwewCfbL/lG9R+/GrgMQB1gbTJ3cit8hMwtH8KX2Em6OwO0HRw== + +web3-utils@^4.0.7, web3-utils@^4.1.0, web3-utils@^4.2.2, web3-utils@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.2.3.tgz#e1d30c4b087cd95f4307baeb80e3160f174e1cfd" + integrity sha512-m5plKTC2YtQntHITQRyIePw52UVP1IrShhmA2FACtn4zmc5ADmrXOlQWiPzxFP/18eRJsAaUAw2+CQn1u4WPxQ== + dependencies: + ethereum-cryptography "^2.0.0" + eventemitter3 "^5.0.1" + web3-errors "^1.1.4" + web3-types "^1.6.0" + web3-validator "^2.0.5" + +web3-validator@^2.0.3, web3-validator@^2.0.4, web3-validator@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.5.tgz#de1984bdb34f292251b86400dba7169700db0849" + integrity sha512-2gLOSW8XqEN5pw5jVUm20EB7A8SbQiekpAtiI0JBmCIV0a2rp97v8FgWY5E3UEqnw5WFfEqvcDVW92EyynDTyQ== + dependencies: + ethereum-cryptography "^2.0.0" + util "^0.12.5" + web3-errors "^1.1.4" + web3-types "^1.5.0" + zod "^3.21.4" + +web3@^4.8.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/web3/-/web3-4.8.0.tgz#c7c7d2a7616ae387f8b2e3a3e416153a4bff479a" + integrity sha512-kQSF2NlHk8yjS3SRiJW3S+U5ibkEmVRhB4/GYsVwGvdAkFC2b+EIE1Ob7J56OmqW9VBZgkx1+SuWqo5JTIJSYQ== + dependencies: + web3-core "^4.3.2" + web3-errors "^1.1.4" + web3-eth "^4.6.0" + web3-eth-abi "^4.2.1" + web3-eth-accounts "^4.1.2" + web3-eth-contract "^4.4.0" + web3-eth-ens "^4.2.0" + web3-eth-iban "^4.0.7" + web3-eth-personal "^4.0.8" + web3-net "^4.0.7" + web3-providers-http "^4.1.0" + web3-providers-ws "^4.0.7" + web3-rpc-methods "^1.2.0" + web3-types "^1.6.0" + web3-utils "^4.2.3" + web3-validator "^2.0.5" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-typed-array@^1.1.14, which-typed-array@^1.1.2: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +ws@7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + +ws@^8.8.1: + version "8.17.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea" + integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== + +zod@^3.21.4: + version "3.23.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== diff --git a/remappings.txt b/remappings.txt index d07604c..374f0be 100644 --- a/remappings.txt +++ b/remappings.txt @@ -3,4 +3,4 @@ forge-std/=lib/forge-std/src/ redstone-oracles-monorepo/=lib/RedStoneBaseContracts/redstone-oracles-monorepo @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ - +@redstone-finance/=lib/RedStoneBaseContracts/@redstone-finance/ diff --git a/src/oracles/RedstonePriceFeedWithRounds.sol b/src/oracles/RedstonePriceFeedWithRounds.sol index 5927f9b..421c089 100644 --- a/src/oracles/RedstonePriceFeedWithRounds.sol +++ b/src/oracles/RedstonePriceFeedWithRounds.sol @@ -18,13 +18,13 @@ contract RedstonePriceFeedWithRounds is MergedPriceFeedAdapterWithRounds { } function getUniqueSignersThreshold() public pure override returns (uint8) { - return 3; + return 1; } function getAuthorisedSignerIndex( address signerAddress ) public view virtual override returns (uint8) { - if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { + if (signerAddress == 0x71d00abE308806A3bF66cE05CF205186B0059503) { return 0; } else if ( signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499 diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol index 977e044..badce5c 100644 --- a/test/unit/RedStoneOracle.sol +++ b/test/unit/RedStoneOracle.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; -// import {RedstoneConsumerNumericBase} from "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol"; +import {RedstoneConsumerNumericBase} from "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol"; import {CommonTest} from "../Common.sol"; @@ -17,25 +17,77 @@ contract MockRedstonePayload is CommonTest { ) public returns (bytes memory) { string[] memory args = new string[](3); args[0] = "node"; - args[1] = "./test/unit/getRedstonePayload.js"; + args[1] = "./redstone/getRedstonePayload.js"; args[2] = priceFeed; return vm.ffi(args); } } -contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { +contract RedstoneOracleAdapterTest is + CommonTest, + MockRedstonePayload, + RedstoneConsumerNumericBase +{ RedstonePriceFeedWithRounds redstoneOracle; function setUp() public { redstoneOracle = new RedstonePriceFeedWithRounds(bytes32("BTC")); } + function getAuthorisedSignerIndex( + address signerAddress + ) public view virtual override returns (uint8) { + if (signerAddress == 0x71d00abE308806A3bF66cE05CF205186B0059503) { + return 0; + } else if ( + signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499 + ) { + return 1; + } else if ( + signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202 + ) { + return 2; + } else if ( + signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE + ) { + return 3; + } else if ( + signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de + ) { + return 4; + } else { + revert SignerNotAuthorised(signerAddress); + } + } + function testPushPrice() public { + bytes memory data = getRedstonePayload("BTC:120:8"); + + ( + bytes memory redstonePayload, + uint256 timestampMilliseconds + ) = abi.decode(data, (bytes, uint256)); + + bytes32[] memory dataFeedIds = new bytes32[](1); + dataFeedIds[0] = bytes32("BTC"); - bytes memory redstonePayload = getRedstonePayload("BTC:120:8"); + // getOracleNumericValuesFromTxMsg() + // uint256[] memory values = getOracleNumericValuesFromTxMsg(dataFeedIds); + bytes memory encodedFunction = abi.encodeWithSignature( + "updateDataFeedsValues(uint256)", + timestampMilliseconds + ); + bytes memory encodedFunctionWithRedstonePayload = abi.encodePacked( + encodedFunction, + redstonePayload + ); + // // Securely getting oracle value + (bool success, ) = address(redstoneOracle).call( + encodedFunctionWithRedstonePayload + ); // redstoneOracle.updateDataFeedsValues(); } diff --git a/test/unit/getRedstonePayload.js b/test/unit/getRedstonePayload.js deleted file mode 100644 index a538cf9..0000000 --- a/test/unit/getRedstonePayload.js +++ /dev/null @@ -1,48 +0,0 @@ -const { appendFileSync } = require('fs'); -const { DataPackage, NumericDataPoint, RedstonePayload } = require('./lib/redstone-oracles-monorepo/packages/protocol/dist/src/index'); - -const args = process.argv.slice(2); - -const exit = (code, message) => { - process.stderr.write(message); - appendFileSync("./getRedstonePayload.log.txt", message); - process.exit(code); -} - -if (args.length === 0) { - exit(1, "You have to provide at least on dataFeed"); -} - -const dataFeeds = args[0].split(','); - -if (dataFeeds.length === 0) { - exit(2, "You have to provide at least on dataFeed"); -} - -const timestampMilliseconds = Date.now(); - -const PRIVATE_KEY_1 = '0x548e7c2fae09cc353ffe54ed40609d88a99fab24acfc81bfbf5cd9c11741643d'; - -const dataPoints = dataFeeds.map(arg => { - const [dataFeedId, value, decimals] = arg.split(':'); - - if (!dataFeedId || !value || !decimals) { - exit(3, "Input should have format: dataFeedId:value:decimals (example: BTC:120:8)"); - } - - return new NumericDataPoint({ dataFeedId, value: parseInt(value), decimals: parseInt(decimals) }) -}); - - -// Prepare unsigned data package -const dataPackage = new DataPackage(dataPoints, timestampMilliseconds); - -// Prepare signed data packages -const signedDataPackages = [ - dataPackage.sign(PRIVATE_KEY_1), -]; - -const payload = RedstonePayload.prepare(signedDataPackages, ""); - -process.stdout.write("0x" + payload) -process.exit(0); \ No newline at end of file From e2ad14b5e55a2ddfba4c94c3930a2bf4cecfb820 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 12:38:05 +0100 Subject: [PATCH 08/31] wip Signed-off-by: Pablo Maldonado --- test/unit/RedStoneOracle.sol | 46 +++++++++++++----------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol index badce5c..787d741 100644 --- a/test/unit/RedStoneOracle.sol +++ b/test/unit/RedStoneOracle.sol @@ -37,42 +37,17 @@ contract RedstoneOracleAdapterTest is function getAuthorisedSignerIndex( address signerAddress - ) public view virtual override returns (uint8) { - if (signerAddress == 0x71d00abE308806A3bF66cE05CF205186B0059503) { - return 0; - } else if ( - signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499 - ) { - return 1; - } else if ( - signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202 - ) { - return 2; - } else if ( - signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE - ) { - return 3; - } else if ( - signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de - ) { - return 4; - } else { - revert SignerNotAuthorised(signerAddress); - } - } + ) public view virtual override returns (uint8) {} function testPushPrice() public { bytes memory data = getRedstonePayload("BTC:120:8"); - ( - bytes memory redstonePayload, - uint256 timestampMilliseconds - ) = abi.decode(data, (bytes, uint256)); + (bytes memory redstonePayload, uint256 timestampMilliseconds) = abi + .decode(data, (bytes, uint256)); bytes32[] memory dataFeedIds = new bytes32[](1); dataFeedIds[0] = bytes32("BTC"); - // getOracleNumericValuesFromTxMsg() // uint256[] memory values = getOracleNumericValuesFromTxMsg(dataFeedIds); bytes memory encodedFunction = abi.encodeWithSignature( @@ -84,11 +59,22 @@ contract RedstoneOracleAdapterTest is redstonePayload ); - // // Securely getting oracle value (bool success, ) = address(redstoneOracle).call( encodedFunctionWithRedstonePayload ); - // redstoneOracle.updateDataFeedsValues(); + ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) = redstoneOracle.latestRoundData(); + + assertEq(roundId, 1); + assertEq(answer, 120 * 10 ** 8); + // assertEq(startedAt, timestampMilliseconds); + // assertEq(updatedAt, timestampMilliseconds); + assertEq(answeredInRound, 1); } } From da4e998188f32e66173f3d6c1071123aba13bd68 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 12:49:25 +0100 Subject: [PATCH 09/31] ffi Signed-off-by: Pablo Maldonado --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d68a29b..29ca00b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -77,7 +77,7 @@ jobs: - name: "Run the tests" env: RPC_MAINNET: ${{ secrets.RPC_MAINNET }} - run: "forge test --fork-url $RPC_MAINNET" + run: "forge test --fork-url $RPC_MAINNET --ffi" - name: "Add test summary" run: | From 7ab11ab4b5686daa376b79ca91c5325171e27aa7 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 16:39:17 +0100 Subject: [PATCH 10/31] working Signed-off-by: Pablo Maldonado --- getRedstonePayload.log.txt | 1 + redstone/getRedstonePayload.js | 101 +- redstone/getRedstonePayload.log.txt | 2 +- redstone/package.json | 1 + redstone/yarn.lock | 992 -------------------- src/oracles/RedstonePriceFeedWithRounds.sol | 4 +- test/unit/RedStoneOracle.sol | 81 +- 7 files changed, 134 insertions(+), 1048 deletions(-) create mode 100644 getRedstonePayload.log.txt delete mode 100644 redstone/yarn.lock diff --git a/getRedstonePayload.log.txt b/getRedstonePayload.log.txt new file mode 100644 index 0000000..edea00b --- /dev/null +++ b/getRedstonePayload.log.txt @@ -0,0 +1 @@ +An error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableYou have to provide at least one dataFeedAn error occurred: provided input is not valid bytes value \ No newline at end of file diff --git a/redstone/getRedstonePayload.js b/redstone/getRedstonePayload.js index 19ae745..75db876 100644 --- a/redstone/getRedstonePayload.js +++ b/redstone/getRedstonePayload.js @@ -5,7 +5,7 @@ const { RedstonePayload, } = require("@redstone-finance/protocol"); const web3 = require("web3"); - +const sdk = require("@redstone-finance/sdk"); const args = process.argv.slice(2); const exit = (code, message) => { @@ -14,53 +14,82 @@ const exit = (code, message) => { process.exit(code); }; -if (args.length === 0) { - exit(1, "You have to provide at least on dataFeed"); -} +const parsePrice = (value) => { + const hexString = web3.utils.bytesToHex(value); + const bigNumberPrice = BigInt(hexString); + return Number(bigNumberPrice); +}; -const dataFeeds = args[0].split(","); +const main = async () => { + if (args.length === 0) { + exit(1, "You have to provide at least on dataFeed"); + } -if (dataFeeds.length === 0) { - exit(2, "You have to provide at least on dataFeed"); -} + const dataFeeds = args[0].split(","); -const timestampMilliseconds = Date.now(); + if (dataFeeds.length === 0) { + exit(2, "You have to provide at least on dataFeed"); + } -const PRIVATE_KEY_1 = - "0x548e7c2fae09cc353ffe54ed40609d88a99fab24acfc81bfbf5cd9c11741643d"; + const timestampMilliseconds = Date.now(); -const dataPoints = dataFeeds.map((arg) => { - const [dataFeedId, value, decimals] = arg.split(":"); + const PRIVATE_KEY_1 = + "0x548e7c2fae09cc353ffe54ed40609d88a99fab24acfc81bfbf5cd9c11741643d"; - if (!dataFeedId || !value || !decimals) { - exit( - 3, - "Input should have format: dataFeedId:value:decimals (example: BTC:120:8)" - ); - } + const dataPoints = dataFeeds.map((arg) => { + const [dataFeedId, value, decimals] = arg.split(":"); + + if (!dataFeedId || !value || !decimals) { + exit( + 3, + "Input should have format: dataFeedId:value:decimals (example: BTC:120:8)" + ); + } - return new NumericDataPoint({ - dataFeedId, - value: parseInt(value), - decimals: parseInt(decimals), + return new NumericDataPoint({ + dataFeedId, + value: parseInt(value), + decimals: parseInt(decimals), + }); }); -}); -// Prepare unsigned data package -const dataPackage = new DataPackage(dataPoints, timestampMilliseconds); + // Prepare unsigned data package + const dataPackage = new DataPackage(dataPoints, timestampMilliseconds); -// Prepare signed data packages -const signedDataPackages = [dataPackage.sign(PRIVATE_KEY_1)]; + // Prepare signed data packages + const signedDataPackages = [dataPackage.sign(PRIVATE_KEY_1)]; -const payload = RedstonePayload.prepare(signedDataPackages, ""); + const getLatestSignedPrice = await sdk.requestDataPackages({ + dataServiceId: "redstone-primary-prod", + uniqueSignersCount: 3, + dataFeeds: ["BTC"], + urls: ["https://oracle-gateway-1.a.redstone.finance"], + }); + + // const payload = RedstonePayload.prepare(signedDataPackages, ""); + const payload = RedstonePayload.prepare(getLatestSignedPrice["BTC"], ""); + + // process.stdout.write("0x" + payload) -// process.stdout.write("0x" + payload) + const timestampMS = + getLatestSignedPrice["BTC"][0].dataPackage.timestampMilliseconds; -const encodedData = web3.eth.abi.encodeParameters( - ["bytes", "uint256"], - ["0x" + payload, timestampMilliseconds] -); + const encodedData = web3.eth.abi.encodeParameters( + ["bytes", "uint256", "uint256"], + [ + "0x" + payload, + timestampMS, + parsePrice( + getLatestSignedPrice["BTC"][0].dataPackage.dataPoints[0].value + ), + ] + ); -process.stdout.write(encodedData); + process.stdout.write(encodedData); -process.exit(0); + process.exit(0); +}; + +main().catch((error) => { + exit(4, `An error occurred: ${error.message}`); +}); diff --git a/redstone/getRedstonePayload.log.txt b/redstone/getRedstonePayload.log.txt index 2de6487..5c809eb 100644 --- a/redstone/getRedstonePayload.log.txt +++ b/redstone/getRedstonePayload.log.txt @@ -1 +1 @@ -You have to provide at least on dataFeed \ No newline at end of file +You have to provide at least on dataFeedYou have to provide at least on dataFeedYou have to provide at least one dataFeedAn error occurred: Cannot read properties of undefined (reading 'split')You have to provide at least one dataFeedYou have to provide at least one dataFeedYou have to provide at least one dataFeed \ No newline at end of file diff --git a/redstone/package.json b/redstone/package.json index 63dcb4b..2982762 100644 --- a/redstone/package.json +++ b/redstone/package.json @@ -11,6 +11,7 @@ "license": "ISC", "dependencies": { "@redstone-finance/protocol": "^0.5.1", + "@redstone-finance/sdk": "^0.5.1", "web3": "^4.8.0" } } diff --git a/redstone/yarn.lock b/redstone/yarn.lock deleted file mode 100644 index 15d8a97..0000000 --- a/redstone/yarn.lock +++ /dev/null @@ -1,992 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@adraffy/ens-normalize@^1.8.8": - version "1.10.1" - resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" - integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== - -"@ethereumjs/rlp@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" - integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== - -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" - integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== - dependencies: - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/hash" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" - integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/networks" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/web" "^5.7.0" - -"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" - integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== - dependencies: - "@ethersproject/abstract-provider" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - -"@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" - integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - -"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" - integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== - dependencies: - "@ethersproject/bytes" "^5.7.0" - -"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" - integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - -"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" - integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - bn.js "^5.2.1" - -"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" - integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== - dependencies: - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" - integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - -"@ethersproject/contracts@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" - integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== - dependencies: - "@ethersproject/abi" "^5.7.0" - "@ethersproject/abstract-provider" "^5.7.0" - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - -"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" - integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== - dependencies: - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/base64" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" - integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== - dependencies: - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/basex" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/pbkdf2" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/sha2" "^5.7.0" - "@ethersproject/signing-key" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/wordlists" "^5.7.0" - -"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" - integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== - dependencies: - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/hdnode" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/pbkdf2" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/random" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - aes-js "3.0.0" - scrypt-js "3.0.1" - -"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" - integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== - dependencies: - "@ethersproject/bytes" "^5.7.0" - js-sha3 "0.8.0" - -"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" - integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== - -"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" - integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== - dependencies: - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" - integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/sha2" "^5.7.0" - -"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" - integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== - dependencies: - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/providers@5.7.2": - version "5.7.2" - resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" - integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== - dependencies: - "@ethersproject/abstract-provider" "^5.7.0" - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/base64" "^5.7.0" - "@ethersproject/basex" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/hash" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/networks" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/random" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - "@ethersproject/sha2" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/web" "^5.7.0" - bech32 "1.1.4" - ws "7.4.6" - -"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" - integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" - integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" - integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - hash.js "1.1.7" - -"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" - integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - bn.js "^5.2.1" - elliptic "6.5.4" - hash.js "1.1.7" - -"@ethersproject/solidity@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" - integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/sha2" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" - integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" - integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== - dependencies: - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - "@ethersproject/signing-key" "^5.7.0" - -"@ethersproject/units@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" - integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/wallet@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" - integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA== - dependencies: - "@ethersproject/abstract-provider" "^5.7.0" - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/hash" "^5.7.0" - "@ethersproject/hdnode" "^5.7.0" - "@ethersproject/json-wallets" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/random" "^5.7.0" - "@ethersproject/signing-key" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/wordlists" "^5.7.0" - -"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" - integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== - dependencies: - "@ethersproject/base64" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" - integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/hash" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@noble/curves@1.3.0", "@noble/curves@~1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e" - integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA== - dependencies: - "@noble/hashes" "1.3.3" - -"@noble/hashes@1.3.3", "@noble/hashes@~1.3.2": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" - integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== - -"@redstone-finance/protocol@^0.5.1": - version "0.5.1" - resolved "https://registry.yarnpkg.com/@redstone-finance/protocol/-/protocol-0.5.1.tgz#6fc47001ee386157a989d95c26dea6f2152b3422" - integrity sha512-MJ8BYuQ34xakfD7SA2OidtrLKDm5J5bZLrWZlsYj8hBs14h/vpFQsdbYJGbiIrN17aIb0c7rST0Q314T30BzJg== - dependencies: - ethers "^5.7.2" - -"@scure/base@~1.1.4": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d" - integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g== - -"@scure/bip32@1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8" - integrity sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ== - dependencies: - "@noble/curves" "~1.3.0" - "@noble/hashes" "~1.3.2" - "@scure/base" "~1.1.4" - -"@scure/bip39@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.2.tgz#f3426813f4ced11a47489cbcf7294aa963966527" - integrity sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA== - dependencies: - "@noble/hashes" "~1.3.2" - "@scure/base" "~1.1.4" - -"@types/node@*": - version "20.12.12" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.12.tgz#7cbecdf902085cec634fdb362172dfe12b8f2050" - integrity sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw== - dependencies: - undici-types "~5.26.4" - -"@types/ws@8.5.3": - version "8.5.3" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" - integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== - dependencies: - "@types/node" "*" - -abitype@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.7.1.tgz#16db20abe67de80f6183cf75f3de1ff86453b745" - integrity sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ== - -aes-js@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" - integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== - -available-typed-arrays@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" - integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== - dependencies: - possible-typed-array-names "^1.0.0" - -bech32@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" - integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== - -bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - -brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== - -call-bind@^1.0.2, call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" - -crc-32@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" - integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== - -cross-fetch@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" - integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== - dependencies: - node-fetch "^2.6.12" - -define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - -elliptic@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -ethereum-cryptography@^2.0.0: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a" - integrity sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA== - dependencies: - "@noble/curves" "1.3.0" - "@noble/hashes" "1.3.3" - "@scure/bip32" "1.3.3" - "@scure/bip39" "1.2.2" - -ethers@^5.7.2: - version "5.7.2" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" - integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== - dependencies: - "@ethersproject/abi" "5.7.0" - "@ethersproject/abstract-provider" "5.7.0" - "@ethersproject/abstract-signer" "5.7.0" - "@ethersproject/address" "5.7.0" - "@ethersproject/base64" "5.7.0" - "@ethersproject/basex" "5.7.0" - "@ethersproject/bignumber" "5.7.0" - "@ethersproject/bytes" "5.7.0" - "@ethersproject/constants" "5.7.0" - "@ethersproject/contracts" "5.7.0" - "@ethersproject/hash" "5.7.0" - "@ethersproject/hdnode" "5.7.0" - "@ethersproject/json-wallets" "5.7.0" - "@ethersproject/keccak256" "5.7.0" - "@ethersproject/logger" "5.7.0" - "@ethersproject/networks" "5.7.1" - "@ethersproject/pbkdf2" "5.7.0" - "@ethersproject/properties" "5.7.0" - "@ethersproject/providers" "5.7.2" - "@ethersproject/random" "5.7.0" - "@ethersproject/rlp" "5.7.0" - "@ethersproject/sha2" "5.7.0" - "@ethersproject/signing-key" "5.7.0" - "@ethersproject/solidity" "5.7.0" - "@ethersproject/strings" "5.7.0" - "@ethersproject/transactions" "5.7.0" - "@ethersproject/units" "5.7.0" - "@ethersproject/wallet" "5.7.0" - "@ethersproject/web" "5.7.1" - "@ethersproject/wordlists" "5.7.0" - -eventemitter3@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" - integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== - -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - -has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -hasown@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== - dependencies: - function-bind "^1.1.2" - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -inherits@^2.0.3, inherits@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-arguments@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-callable@^1.1.3: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - -is-typed-array@^1.1.3: - version "1.1.13" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" - integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== - dependencies: - which-typed-array "^1.1.14" - -isomorphic-ws@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" - integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== - -js-sha3@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" - integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== - -node-fetch@^2.6.12: - version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== - dependencies: - whatwg-url "^5.0.0" - -possible-typed-array-names@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" - integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== - -scrypt-js@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" - integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== - -set-function-length@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - -util@^0.12.5: - version "0.12.5" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" - integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - which-typed-array "^1.1.2" - -web3-core@^4.3.0, web3-core@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-4.3.2.tgz#f24b11d6a57dee527de8d42c89de2a439f0c4bed" - integrity sha512-uIMVd/j4BgOnwfpY8ZT+QKubOyM4xohEhFZXz9xB8wimXWMMlYVlIK/TbfHqFolS9uOerdSGhsMbcK9lETae8g== - dependencies: - web3-errors "^1.1.4" - web3-eth-accounts "^4.1.0" - web3-eth-iban "^4.0.7" - web3-providers-http "^4.1.0" - web3-providers-ws "^4.0.7" - web3-types "^1.3.1" - web3-utils "^4.1.0" - web3-validator "^2.0.3" - optionalDependencies: - web3-providers-ipc "^4.0.7" - -web3-errors@^1.1.3, web3-errors@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/web3-errors/-/web3-errors-1.1.4.tgz#5667a0a5f66fc936e101ef32032ccc1e8ca4d5a1" - integrity sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ== - dependencies: - web3-types "^1.3.1" - -web3-eth-abi@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-4.2.1.tgz#b1260dace8380221f12f4274af240c1dfed1045c" - integrity sha512-IE91WUhhiDpBtbkl/DHUoZz7z7T5FXvl3zPLkrxT+dNlOT+wni+US/67jQCLvJRbqf9ApQ26lVYry0bovFgyqA== - dependencies: - abitype "0.7.1" - web3-errors "^1.1.4" - web3-types "^1.6.0" - web3-utils "^4.2.3" - web3-validator "^2.0.5" - -web3-eth-accounts@^4.1.0, web3-eth-accounts@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-4.1.2.tgz#652d6e3daf4d6cb3fe67cec6a878e768f6e8b8e8" - integrity sha512-y0JynDeTDnclyuE9mShXLeEj+BCrPHxPHOyPCgTchUBQsALF9+0OhP7WiS3IqUuu0Hle5bjG2f5ddeiPtNEuLg== - dependencies: - "@ethereumjs/rlp" "^4.0.1" - crc-32 "^1.2.2" - ethereum-cryptography "^2.0.0" - web3-errors "^1.1.4" - web3-types "^1.6.0" - web3-utils "^4.2.3" - web3-validator "^2.0.5" - -web3-eth-contract@^4.3.0, web3-eth-contract@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-4.4.0.tgz#21760ef39ab95b34c55e7eaee316e0632e56cd21" - integrity sha512-pZ/w6Lb6ZDUUs7f5GCKXiHDAGGvt2tdwiHkvgmQTRnq9b0MEsUpteDyPYspHxKzQWLgbeK37jPb8zbQe4kE/Hg== - dependencies: - web3-core "^4.3.2" - web3-errors "^1.1.4" - web3-eth "^4.6.0" - web3-eth-abi "^4.2.1" - web3-types "^1.6.0" - web3-utils "^4.2.3" - web3-validator "^2.0.5" - -web3-eth-ens@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-4.2.0.tgz#8734b034efd48a735f7052fef0205653a78b84cb" - integrity sha512-qYj34te2UctoObt8rlEIY/t2MuTMiMiiHhO2JAHRGqSLCQ7b8DM3RpvkiiSB0N0ZyEn+CetZqJCTYb8DNKBS/g== - dependencies: - "@adraffy/ens-normalize" "^1.8.8" - web3-core "^4.3.2" - web3-errors "^1.1.4" - web3-eth "^4.5.0" - web3-eth-contract "^4.3.0" - web3-net "^4.0.7" - web3-types "^1.5.0" - web3-utils "^4.2.2" - web3-validator "^2.0.5" - -web3-eth-iban@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-4.0.7.tgz#ee504f845d7b6315f0be78fcf070ccd5d38e4aaf" - integrity sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ== - dependencies: - web3-errors "^1.1.3" - web3-types "^1.3.0" - web3-utils "^4.0.7" - web3-validator "^2.0.3" - -web3-eth-personal@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz#b51628c560de550ca8b354fa784f9556aae6065c" - integrity sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw== - dependencies: - web3-core "^4.3.0" - web3-eth "^4.3.1" - web3-rpc-methods "^1.1.3" - web3-types "^1.3.0" - web3-utils "^4.0.7" - web3-validator "^2.0.3" - -web3-eth@^4.3.1, web3-eth@^4.5.0, web3-eth@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-4.6.0.tgz#75c177e2bde88a613a6996fab515f104e16921da" - integrity sha512-8KtxlGsomovoFULqEpfixgmCpaJ2YIJGxbXUfezh2coXHjVgEopQhARYtKGClyV5kkdCIqwHS8Gvsm6TVNqH6Q== - dependencies: - setimmediate "^1.0.5" - web3-core "^4.3.2" - web3-errors "^1.1.4" - web3-eth-abi "^4.2.1" - web3-eth-accounts "^4.1.2" - web3-net "^4.0.7" - web3-providers-ws "^4.0.7" - web3-rpc-methods "^1.2.0" - web3-types "^1.6.0" - web3-utils "^4.2.3" - web3-validator "^2.0.5" - -web3-net@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-4.0.7.tgz#ed2c1bd700cf94be93a6dbd8bd8aa413d8681942" - integrity sha512-SzEaXFrBjY25iQGk5myaOfO9ZyfTwQEa4l4Ps4HDNVMibgZji3WPzpjq8zomVHMwi8bRp6VV7YS71eEsX7zLow== - dependencies: - web3-core "^4.3.0" - web3-rpc-methods "^1.1.3" - web3-types "^1.3.0" - web3-utils "^4.0.7" - -web3-providers-http@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-4.1.0.tgz#8d7afda67d1d8542ca85b30f60a3d1fe1993b561" - integrity sha512-6qRUGAhJfVQM41E5t+re5IHYmb5hSaLc02BE2MaRQsz2xKA6RjmHpOA5h/+ojJxEpI9NI2CrfDKOAgtJfoUJQg== - dependencies: - cross-fetch "^4.0.0" - web3-errors "^1.1.3" - web3-types "^1.3.0" - web3-utils "^4.0.7" - -web3-providers-ipc@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-4.0.7.tgz#9ec4c8565053af005a5170ba80cddeb40ff3e3d3" - integrity sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g== - dependencies: - web3-errors "^1.1.3" - web3-types "^1.3.0" - web3-utils "^4.0.7" - -web3-providers-ws@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-4.0.7.tgz#7a78a0dcf077e0e802da524fbb37d080b356c14b" - integrity sha512-n4Dal9/rQWjS7d6LjyEPM2R458V8blRm0eLJupDEJOOIBhGYlxw5/4FthZZ/cqB7y/sLVi7K09DdYx2MeRtU5w== - dependencies: - "@types/ws" "8.5.3" - isomorphic-ws "^5.0.0" - web3-errors "^1.1.3" - web3-types "^1.3.0" - web3-utils "^4.0.7" - ws "^8.8.1" - -web3-rpc-methods@^1.1.3, web3-rpc-methods@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/web3-rpc-methods/-/web3-rpc-methods-1.2.0.tgz#761dcb036ab16edb2b03e80c11e3f5df24690345" - integrity sha512-CWJ/g4I4WyYvLkf21wCZAehdhU/VjX/OAPHnqF5/FPDJlogOsOnGXHqi1Z5AP+ocdt395PNubd8jyMMJoYGSBA== - dependencies: - web3-core "^4.3.2" - web3-types "^1.5.0" - web3-validator "^2.0.4" - -web3-types@^1.3.0, web3-types@^1.3.1, web3-types@^1.5.0, web3-types@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/web3-types/-/web3-types-1.6.0.tgz#ebe7f140c31f7cc0ad15f238ad7e7ac72797ff3b" - integrity sha512-qgOtADqlD5hw+KPKBUGaXAcdNLL0oh6qTeVgXwewCfbL/lG9R+/GrgMQB1gbTJ3cit8hMwtH8KX2Em6OwO0HRw== - -web3-utils@^4.0.7, web3-utils@^4.1.0, web3-utils@^4.2.2, web3-utils@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-4.2.3.tgz#e1d30c4b087cd95f4307baeb80e3160f174e1cfd" - integrity sha512-m5plKTC2YtQntHITQRyIePw52UVP1IrShhmA2FACtn4zmc5ADmrXOlQWiPzxFP/18eRJsAaUAw2+CQn1u4WPxQ== - dependencies: - ethereum-cryptography "^2.0.0" - eventemitter3 "^5.0.1" - web3-errors "^1.1.4" - web3-types "^1.6.0" - web3-validator "^2.0.5" - -web3-validator@^2.0.3, web3-validator@^2.0.4, web3-validator@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/web3-validator/-/web3-validator-2.0.5.tgz#de1984bdb34f292251b86400dba7169700db0849" - integrity sha512-2gLOSW8XqEN5pw5jVUm20EB7A8SbQiekpAtiI0JBmCIV0a2rp97v8FgWY5E3UEqnw5WFfEqvcDVW92EyynDTyQ== - dependencies: - ethereum-cryptography "^2.0.0" - util "^0.12.5" - web3-errors "^1.1.4" - web3-types "^1.5.0" - zod "^3.21.4" - -web3@^4.8.0: - version "4.8.0" - resolved "https://registry.yarnpkg.com/web3/-/web3-4.8.0.tgz#c7c7d2a7616ae387f8b2e3a3e416153a4bff479a" - integrity sha512-kQSF2NlHk8yjS3SRiJW3S+U5ibkEmVRhB4/GYsVwGvdAkFC2b+EIE1Ob7J56OmqW9VBZgkx1+SuWqo5JTIJSYQ== - dependencies: - web3-core "^4.3.2" - web3-errors "^1.1.4" - web3-eth "^4.6.0" - web3-eth-abi "^4.2.1" - web3-eth-accounts "^4.1.2" - web3-eth-contract "^4.4.0" - web3-eth-ens "^4.2.0" - web3-eth-iban "^4.0.7" - web3-eth-personal "^4.0.8" - web3-net "^4.0.7" - web3-providers-http "^4.1.0" - web3-providers-ws "^4.0.7" - web3-rpc-methods "^1.2.0" - web3-types "^1.6.0" - web3-utils "^4.2.3" - web3-validator "^2.0.5" - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -which-typed-array@^1.1.14, which-typed-array@^1.1.2: - version "1.1.15" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" - integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.2" - -ws@7.4.6: - version "7.4.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" - integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== - -ws@^8.8.1: - version "8.17.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea" - integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== - -zod@^3.21.4: - version "3.23.8" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" - integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== diff --git a/src/oracles/RedstonePriceFeedWithRounds.sol b/src/oracles/RedstonePriceFeedWithRounds.sol index 421c089..5927f9b 100644 --- a/src/oracles/RedstonePriceFeedWithRounds.sol +++ b/src/oracles/RedstonePriceFeedWithRounds.sol @@ -18,13 +18,13 @@ contract RedstonePriceFeedWithRounds is MergedPriceFeedAdapterWithRounds { } function getUniqueSignersThreshold() public pure override returns (uint8) { - return 1; + return 3; } function getAuthorisedSignerIndex( address signerAddress ) public view virtual override returns (uint8) { - if (signerAddress == 0x71d00abE308806A3bF66cE05CF205186B0059503) { + if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { return 0; } else if ( signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499 diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol index 787d741..2e53460 100644 --- a/test/unit/RedStoneOracle.sol +++ b/test/unit/RedStoneOracle.sol @@ -10,15 +10,18 @@ import {DecimalLib} from "../../src/adapters/lib/DecimalLib.sol"; import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3Source.sol"; import {RedstonePriceFeedWithRounds} from "../../src/oracles/RedstonePriceFeedWithRounds.sol"; +import "forge-std/console.sol"; + contract MockRedstonePayload is CommonTest { function getRedstonePayload( // dataFeedId:value:decimals string memory priceFeed ) public returns (bytes memory) { - string[] memory args = new string[](3); + string[] memory args = new string[](4); args[0] = "node"; - args[1] = "./redstone/getRedstonePayload.js"; - args[2] = priceFeed; + args[1] = "--no-warnings"; + args[2] = "./redstone/getRedstonePayload.js"; + args[3] = priceFeed; return vm.ffi(args); } @@ -35,20 +38,28 @@ contract RedstoneOracleAdapterTest is redstoneOracle = new RedstonePriceFeedWithRounds(bytes32("BTC")); } - function getAuthorisedSignerIndex( - address signerAddress - ) public view virtual override returns (uint8) {} - function testPushPrice() public { bytes memory data = getRedstonePayload("BTC:120:8"); - (bytes memory redstonePayload, uint256 timestampMilliseconds) = abi - .decode(data, (bytes, uint256)); + (bytes memory redstonePayload, uint256 timestampMilliseconds, uint256 updatePrice) = abi + .decode(data, (bytes, uint256,uint256)); + + bytes memory encodedFunctionNumericValues = abi.encodeWithSignature( + "getOracleNumericValueFromTxMsg(bytes32)", + bytes32("BTC") + ); + + bytes memory encodedFunctionNumericWithRedstonePayload = abi + .encodePacked(encodedFunctionNumericValues, redstonePayload); - bytes32[] memory dataFeedIds = new bytes32[](1); - dataFeedIds[0] = bytes32("BTC"); + (bool success2, bytes memory dataa) = address(redstoneOracle).call( + encodedFunctionNumericWithRedstonePayload + ); - // uint256[] memory values = getOracleNumericValuesFromTxMsg(dataFeedIds); + uint256 oracleValue; + if (success2) { + oracleValue = abi.decode(dataa, (uint256)); + } bytes memory encodedFunction = abi.encodeWithSignature( "updateDataFeedsValues(uint256)", @@ -62,6 +73,12 @@ contract RedstoneOracleAdapterTest is (bool success, ) = address(redstoneOracle).call( encodedFunctionWithRedstonePayload ); + // (bool success3, ) = address(redstoneOracle).call( + // encodedFunctionWithRedstonePayload + // ); + + assert(success); + // assert(success3); ( uint80 roundId, @@ -71,10 +88,40 @@ contract RedstoneOracleAdapterTest is uint80 answeredInRound ) = redstoneOracle.latestRoundData(); - assertEq(roundId, 1); - assertEq(answer, 120 * 10 ** 8); - // assertEq(startedAt, timestampMilliseconds); - // assertEq(updatedAt, timestampMilliseconds); - assertEq(answeredInRound, 1); + console.logInt(answer); + console.logUint(oracleValue); + console.logBytes(dataa); + + // assertEq(roundId, 1); + assertEq(uint256(answer), updatePrice); + // // assertEq(startedAt, timestampMilliseconds); + // // assertEq(updatedAt, timestampMilliseconds); + // assertEq(answeredInRound, 1); + } + + function getAuthorisedSignerIndex( + address signerAddress + ) public view virtual override returns (uint8) { + if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { + return 0; + } else if ( + signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499 + ) { + return 1; + } else if ( + signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202 + ) { + return 2; + } else if ( + signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE + ) { + return 3; + } else if ( + signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de + ) { + return 4; + } else { + revert SignerNotAuthorised(signerAddress); + } } } From 26c33a1019c15a2c156d99d61c0cf3a05a7de328 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 17:14:28 +0100 Subject: [PATCH 11/31] refactor: cleanup Signed-off-by: Pablo Maldonado --- .github/workflows/test.yml | 5 ++ getRedstonePayload.log.txt | 3 +- redstone/getRedstonePayload.js | 76 ++++++++-------------- test/unit/RedStoneOracle.sol | 113 ++++++++++++++------------------- 4 files changed, 78 insertions(+), 119 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 29ca00b..5ccec5b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,6 +71,11 @@ jobs: with: version: "nightly-de33b6af53005037b463318d2628b5cfcaf39916" + - name: "Navigate to redstone and install dependencies" + run: | + cd redstone + yarn install + - name: "Show the Foundry config" run: "forge config" diff --git a/getRedstonePayload.log.txt b/getRedstonePayload.log.txt index edea00b..6f38c81 100644 --- a/getRedstonePayload.log.txt +++ b/getRedstonePayload.log.txt @@ -1 +1,2 @@ -An error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableYou have to provide at least one dataFeedAn error occurred: provided input is not valid bytes value \ No newline at end of file +An error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableYou have to provide at least one dataFeedAn error occurred: provided input is not valid bytes valueAn error occurred: Request failed {"reqParams":{"dataServiceId":"redstone-primary-prod","uniqueSignersCount":3,"dataFeeds":["BTC:120:8"],"urls":["https://oracle-gateway-1.a.redstone.finance"]}}, Original error: AggregateError: , errors: Error: Requested data feed id is not included in response: BTC:120:8 + at parseAndValidateDataPackagesResponse (/Users/salvadorpablomaldonadoturci/ghq/github.com/UMAprotocol/oval-contracts/redsto... \ No newline at end of file diff --git a/redstone/getRedstonePayload.js b/redstone/getRedstonePayload.js index 75db876..3ce90bc 100644 --- a/redstone/getRedstonePayload.js +++ b/redstone/getRedstonePayload.js @@ -1,9 +1,5 @@ const { appendFileSync } = require("fs"); -const { - DataPackage, - NumericDataPoint, - RedstonePayload, -} = require("@redstone-finance/protocol"); +const { RedstonePayload } = require("@redstone-finance/protocol"); const web3 = require("web3"); const sdk = require("@redstone-finance/sdk"); const args = process.argv.slice(2); @@ -20,69 +16,47 @@ const parsePrice = (value) => { return Number(bigNumberPrice); }; -const main = async () => { - if (args.length === 0) { - exit(1, "You have to provide at least on dataFeed"); +const pickMedian = (arr) => { + if (arr.length === 0) { + throw new Error("Cannot pick median of empty array"); } - - const dataFeeds = args[0].split(","); - - if (dataFeeds.length === 0) { - exit(2, "You have to provide at least on dataFeed"); + arr.sort((a, b) => a - b); + const middleIndex = Math.floor(arr.length / 2); + if (arr.length % 2 === 0) { + return (arr[middleIndex - 1] + arr[middleIndex]) / 2; + } else { + return arr[middleIndex]; } +}; - const timestampMilliseconds = Date.now(); - - const PRIVATE_KEY_1 = - "0x548e7c2fae09cc353ffe54ed40609d88a99fab24acfc81bfbf5cd9c11741643d"; - - const dataPoints = dataFeeds.map((arg) => { - const [dataFeedId, value, decimals] = arg.split(":"); - - if (!dataFeedId || !value || !decimals) { - exit( - 3, - "Input should have format: dataFeedId:value:decimals (example: BTC:120:8)" - ); - } - - return new NumericDataPoint({ - dataFeedId, - value: parseInt(value), - decimals: parseInt(decimals), - }); - }); - - // Prepare unsigned data package - const dataPackage = new DataPackage(dataPoints, timestampMilliseconds); +const main = async () => { + if (args.length === 0) { + exit(1, "You have to provide a data Feed"); + } - // Prepare signed data packages - const signedDataPackages = [dataPackage.sign(PRIVATE_KEY_1)]; + const dataFeed = args[0]; const getLatestSignedPrice = await sdk.requestDataPackages({ dataServiceId: "redstone-primary-prod", uniqueSignersCount: 3, - dataFeeds: ["BTC"], + dataFeeds: [dataFeed], urls: ["https://oracle-gateway-1.a.redstone.finance"], }); - // const payload = RedstonePayload.prepare(signedDataPackages, ""); - const payload = RedstonePayload.prepare(getLatestSignedPrice["BTC"], ""); + const prices = getLatestSignedPrice[dataFeed].map((dataPackage) => + parsePrice(dataPackage.dataPackage.dataPoints[0].value) + ); + + const medianPrice = pickMedian(prices); - // process.stdout.write("0x" + payload) + const payload = RedstonePayload.prepare(getLatestSignedPrice[dataFeed], ""); const timestampMS = - getLatestSignedPrice["BTC"][0].dataPackage.timestampMilliseconds; + getLatestSignedPrice[dataFeed][0].dataPackage.timestampMilliseconds; const encodedData = web3.eth.abi.encodeParameters( ["bytes", "uint256", "uint256"], - [ - "0x" + payload, - timestampMS, - parsePrice( - getLatestSignedPrice["BTC"][0].dataPackage.dataPoints[0].value - ), - ] + ["0x" + payload, timestampMS, medianPrice] ); process.stdout.write(encodedData); diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol index 2e53460..74b096b 100644 --- a/test/unit/RedStoneOracle.sol +++ b/test/unit/RedStoneOracle.sol @@ -10,7 +10,9 @@ import {DecimalLib} from "../../src/adapters/lib/DecimalLib.sol"; import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3Source.sol"; import {RedstonePriceFeedWithRounds} from "../../src/oracles/RedstonePriceFeedWithRounds.sol"; -import "forge-std/console.sol"; +import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3Source.sol"; + +import {TestedSourceAdapter} from "../fork/adapters/ChainlinkSourceAdapter.sol"; contract MockRedstonePayload is CommonTest { function getRedstonePayload( @@ -27,39 +29,25 @@ contract MockRedstonePayload is CommonTest { } } -contract RedstoneOracleAdapterTest is - CommonTest, - MockRedstonePayload, - RedstoneConsumerNumericBase -{ +contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { RedstonePriceFeedWithRounds redstoneOracle; + TestedSourceAdapter sourceAdapter; function setUp() public { redstoneOracle = new RedstonePriceFeedWithRounds(bytes32("BTC")); - } - - function testPushPrice() public { - bytes memory data = getRedstonePayload("BTC:120:8"); - - (bytes memory redstonePayload, uint256 timestampMilliseconds, uint256 updatePrice) = abi - .decode(data, (bytes, uint256,uint256)); - - bytes memory encodedFunctionNumericValues = abi.encodeWithSignature( - "getOracleNumericValueFromTxMsg(bytes32)", - bytes32("BTC") + sourceAdapter = new TestedSourceAdapter( + IAggregatorV3Source(address(redstoneOracle)) ); + } - bytes memory encodedFunctionNumericWithRedstonePayload = abi - .encodePacked(encodedFunctionNumericValues, redstonePayload); + function pushPrice() internal returns (uint256) { + bytes memory data = getRedstonePayload("BTC"); - (bool success2, bytes memory dataa) = address(redstoneOracle).call( - encodedFunctionNumericWithRedstonePayload - ); - - uint256 oracleValue; - if (success2) { - oracleValue = abi.decode(dataa, (uint256)); - } + ( + bytes memory redstonePayload, + uint256 timestampMilliseconds, + uint256 updatePrice + ) = abi.decode(data, (bytes, uint256, uint256)); bytes memory encodedFunction = abi.encodeWithSignature( "updateDataFeedsValues(uint256)", @@ -70,16 +58,13 @@ contract RedstoneOracleAdapterTest is redstonePayload ); - (bool success, ) = address(redstoneOracle).call( - encodedFunctionWithRedstonePayload - ); - // (bool success3, ) = address(redstoneOracle).call( - // encodedFunctionWithRedstonePayload - // ); + address(redstoneOracle).call(encodedFunctionWithRedstonePayload); - assert(success); - // assert(success3); + return updatePrice; + } + function testPushPrice() public { + uint256 updatePrice = pushPrice(); ( uint80 roundId, int256 answer, @@ -88,40 +73,34 @@ contract RedstoneOracleAdapterTest is uint80 answeredInRound ) = redstoneOracle.latestRoundData(); - console.logInt(answer); - console.logUint(oracleValue); - console.logBytes(dataa); - - // assertEq(roundId, 1); + assertEq(roundId, 1); assertEq(uint256(answer), updatePrice); - // // assertEq(startedAt, timestampMilliseconds); - // // assertEq(updatedAt, timestampMilliseconds); - // assertEq(answeredInRound, 1); + // assertEq(startedAt, timestampMilliseconds); + // assertEq(updatedAt, timestampMilliseconds); + assertEq(answeredInRound, 1); + } + + function testCorrectlyStandardizesOutputs() public { + uint256 pushedPrice = pushPrice(); + ( + , + int256 latestChainlinkAnswer, + , + uint256 latestChainlinkTimestamp, + + ) = redstoneOracle.latestRoundData(); + ( + int256 latestSourceAnswer, + uint256 latestSourceTimestamp + ) = sourceAdapter.getLatestSourceData(); + assertTrue( + scaleChainlinkTo18(latestChainlinkAnswer) == latestSourceAnswer + ); + assertTrue(pushedPrice == uint256(latestChainlinkAnswer)); + assertTrue(latestSourceTimestamp == latestChainlinkTimestamp); } - function getAuthorisedSignerIndex( - address signerAddress - ) public view virtual override returns (uint8) { - if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { - return 0; - } else if ( - signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499 - ) { - return 1; - } else if ( - signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202 - ) { - return 2; - } else if ( - signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE - ) { - return 3; - } else if ( - signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de - ) { - return 4; - } else { - revert SignerNotAuthorised(signerAddress); - } + function scaleChainlinkTo18(int256 input) public pure returns (int256) { + return (input * 10 ** 18) / 10 ** 8; } } From 0a1d27fb414a8768d104f0970c4f32c3121c50e8 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 17:15:35 +0100 Subject: [PATCH 12/31] refactor: cleanup2 Signed-off-by: Pablo Maldonado --- redstone/getRedstonePayload.log.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 redstone/getRedstonePayload.log.txt diff --git a/redstone/getRedstonePayload.log.txt b/redstone/getRedstonePayload.log.txt deleted file mode 100644 index 5c809eb..0000000 --- a/redstone/getRedstonePayload.log.txt +++ /dev/null @@ -1 +0,0 @@ -You have to provide at least on dataFeedYou have to provide at least on dataFeedYou have to provide at least one dataFeedAn error occurred: Cannot read properties of undefined (reading 'split')You have to provide at least one dataFeedYou have to provide at least one dataFeedYou have to provide at least one dataFeed \ No newline at end of file From 1c71533954a5a9bd560ba7d3bcfe8f6927d76f78 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 17:27:44 +0100 Subject: [PATCH 13/31] feat: timestamp Signed-off-by: Pablo Maldonado --- test/unit/RedStoneOracle.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol index 74b096b..dcff987 100644 --- a/test/unit/RedStoneOracle.sol +++ b/test/unit/RedStoneOracle.sol @@ -16,7 +16,6 @@ import {TestedSourceAdapter} from "../fork/adapters/ChainlinkSourceAdapter.sol"; contract MockRedstonePayload is CommonTest { function getRedstonePayload( - // dataFeedId:value:decimals string memory priceFeed ) public returns (bytes memory) { string[] memory args = new string[](4); @@ -64,6 +63,7 @@ contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { } function testPushPrice() public { + vm.warp(1234); uint256 updatePrice = pushPrice(); ( uint80 roundId, @@ -75,8 +75,8 @@ contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { assertEq(roundId, 1); assertEq(uint256(answer), updatePrice); - // assertEq(startedAt, timestampMilliseconds); - // assertEq(updatedAt, timestampMilliseconds); + assertEq(startedAt, 1234); + assertEq(updatedAt, 1234); assertEq(answeredInRound, 1); } From e890b381164adbfde58e659183e413ee94af2666 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 17:32:41 +0100 Subject: [PATCH 14/31] refactor: clean Signed-off-by: Pablo Maldonado --- getRedstonePayload.log.txt | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 getRedstonePayload.log.txt diff --git a/getRedstonePayload.log.txt b/getRedstonePayload.log.txt deleted file mode 100644 index 6f38c81..0000000 --- a/getRedstonePayload.log.txt +++ /dev/null @@ -1,2 +0,0 @@ -An error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableYou have to provide at least one dataFeedAn error occurred: provided input is not valid bytes valueAn error occurred: Request failed {"reqParams":{"dataServiceId":"redstone-primary-prod","uniqueSignersCount":3,"dataFeeds":["BTC:120:8"],"urls":["https://oracle-gateway-1.a.redstone.finance"]}}, Original error: AggregateError: , errors: Error: Requested data feed id is not included in response: BTC:120:8 - at parseAndValidateDataPackagesResponse (/Users/salvadorpablomaldonadoturci/ghq/github.com/UMAprotocol/oval-contracts/redsto... \ No newline at end of file From ff7c301c95455359c550cf1b1fa76de895d38b8e Mon Sep 17 00:00:00 2001 From: chrismaree Date: Sun, 19 May 2024 14:37:10 +0200 Subject: [PATCH 15/31] more tests Signed-off-by: chrismaree --- .gitignore | 2 + redstone/package.json | 17 -- scripts/getRedstonePayload.log.txt | 2 + scripts/package.json | 6 +- .../RedstoneHelpers}/getRedstonePayload.js | 10 +- scripts/yarn.lock | 63 ++++++++ .../ChainlinkSourceAdapter.sol | 1 + test/fork/adapters/ChainlinkSourceAdapter.sol | 26 +-- .../RedStoneAsChainlinkSourceAdapter.sol | 149 ++++++++++++++++++ test/unit/RedStoneOracle.sol | 59 ++----- 10 files changed, 255 insertions(+), 80 deletions(-) delete mode 100644 redstone/package.json create mode 100644 scripts/getRedstonePayload.log.txt rename {redstone => scripts/src/RedstoneHelpers}/getRedstonePayload.js (87%) create mode 100644 test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol diff --git a/.gitignore b/.gitignore index ba13553..f4e6715 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ scripts/dist scripts/contract-types node_modules + +log.txt diff --git a/redstone/package.json b/redstone/package.json deleted file mode 100644 index 2982762..0000000 --- a/redstone/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "redstone", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "@redstone-finance/protocol": "^0.5.1", - "@redstone-finance/sdk": "^0.5.1", - "web3": "^4.8.0" - } -} diff --git a/scripts/getRedstonePayload.log.txt b/scripts/getRedstonePayload.log.txt new file mode 100644 index 0000000..090e102 --- /dev/null +++ b/scripts/getRedstonePayload.log.txt @@ -0,0 +1,2 @@ +You have to provide a data FeedAn error occurred: Request failed {"reqParams":{"dataServiceId":"redstone-primary-prod","uniqueSignersCount":3,"dataFeeds":["--no-warnings"],"urls":["https://oracle-gateway-1.a.redstone.finance"]}}, Original error: AggregateError: , errors: Error: Requested data feed id is not included in response: --no-warnings + at parseAndValidateDataPackagesResponse (/Users/chris/uma/oval-contracts/scripts/node_modules/@redstone-finance/sdk/dist... \ No newline at end of file diff --git a/scripts/package.json b/scripts/package.json index 099f6d0..8b8e457 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -20,6 +20,8 @@ "typescript": "^5.2.2", "axios": "^1.5.1", "dotenv": "^16.3.1", - "ethers": "^5.7.2" + "ethers": "^5.7.2", + "@redstone-finance/protocol": "^0.5.1", + "@redstone-finance/sdk": "^0.5.1" } -} +} \ No newline at end of file diff --git a/redstone/getRedstonePayload.js b/scripts/src/RedstoneHelpers/getRedstonePayload.js similarity index 87% rename from redstone/getRedstonePayload.js rename to scripts/src/RedstoneHelpers/getRedstonePayload.js index 3ce90bc..bac95db 100644 --- a/redstone/getRedstonePayload.js +++ b/scripts/src/RedstoneHelpers/getRedstonePayload.js @@ -1,6 +1,6 @@ const { appendFileSync } = require("fs"); const { RedstonePayload } = require("@redstone-finance/protocol"); -const web3 = require("web3"); +const ethers = require("ethers"); const sdk = require("@redstone-finance/sdk"); const args = process.argv.slice(2); @@ -11,9 +11,9 @@ const exit = (code, message) => { }; const parsePrice = (value) => { - const hexString = web3.utils.bytesToHex(value); - const bigNumberPrice = BigInt(hexString); - return Number(bigNumberPrice); + const hexString = ethers.utils.hexlify(value); + const bigNumberPrice = ethers.BigNumber.from(hexString); + return bigNumberPrice.toNumber(); }; const pickMedian = (arr) => { @@ -54,7 +54,7 @@ const main = async () => { const timestampMS = getLatestSignedPrice[dataFeed][0].dataPackage.timestampMilliseconds; - const encodedData = web3.eth.abi.encodeParameters( + const encodedData = ethers.utils.defaultAbiCoder.encode( ["bytes", "uint256", "uint256"], ["0x" + payload, timestampMS, medianPrice] ); diff --git a/scripts/yarn.lock b/scripts/yarn.lock index 2dfbc83..75e7e53 100644 --- a/scripts/yarn.lock +++ b/scripts/yarn.lock @@ -344,6 +344,40 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@redstone-finance/oracles-smartweave-contracts@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@redstone-finance/oracles-smartweave-contracts/-/oracles-smartweave-contracts-0.5.1.tgz#fa68f76fb9258e4f037cde4f03b0ad87e8a8e8a2" + integrity sha512-1FwvgRzuAbQyLpeY9WJqViEp2mB3mNJWO78ogjGgCXGNeXsODoUTBWHRpS0GHpWoW8UKR+MC/t0ZSB+E0XsHCA== + +"@redstone-finance/protocol@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@redstone-finance/protocol/-/protocol-0.5.1.tgz#6fc47001ee386157a989d95c26dea6f2152b3422" + integrity sha512-MJ8BYuQ34xakfD7SA2OidtrLKDm5J5bZLrWZlsYj8hBs14h/vpFQsdbYJGbiIrN17aIb0c7rST0Q314T30BzJg== + dependencies: + ethers "^5.7.2" + +"@redstone-finance/sdk@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@redstone-finance/sdk/-/sdk-0.5.1.tgz#3676bf4ffce2081b841367f9c98c42651684a226" + integrity sha512-ElJioHHwJWSRUJqcOV+Iog6b501veZ5/QarfxPMaGGne6ZRKlXGDuj8dDglIZvkXwwSN2j6nCcU5km2RHdLN/Q== + dependencies: + "@redstone-finance/oracles-smartweave-contracts" "^0.5.1" + "@redstone-finance/protocol" "^0.5.1" + "@redstone-finance/utils" "^0.5.1" + axios "^1.6.2" + ethers "^5.7.2" + +"@redstone-finance/utils@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@redstone-finance/utils/-/utils-0.5.1.tgz#f91ca100ce1a1c870892db40249aebabaa0e411a" + integrity sha512-ui8scibt3mnHC9FjLVxT2k0GrDjlptIvAvNDCRsxm0jL+uttLgMmEp0kZkZ62aWFtSS0ORNDN8cER43yNNWWmQ== + dependencies: + axios "^1.6.2" + consola "^2.15.3" + decimal.js "^10.4.3" + ethers "^5.7.2" + zod "^3.22.4" + "@typechain/ethers-v5@^11.1.2": version "11.1.2" resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-11.1.2.tgz#82510c1744f37a2f906b9e0532ac18c0b74ffe69" @@ -407,6 +441,15 @@ axios@^1.5.1: form-data "^4.0.0" proxy-from-env "^1.1.0" +axios@^1.6.2: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -513,6 +556,11 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +consola@^2.15.3: + version "2.15.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== + debug@^4.3.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -520,6 +568,11 @@ debug@^4.3.1: dependencies: ms "2.1.2" +decimal.js@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + deep-extend@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -601,6 +654,11 @@ follow-redirects@^1.15.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -876,3 +934,8 @@ ws@7.4.6: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + +zod@^3.22.4: + version "3.23.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== diff --git a/src/adapters/source-adapters/ChainlinkSourceAdapter.sol b/src/adapters/source-adapters/ChainlinkSourceAdapter.sol index dd4241d..3f6a6ba 100644 --- a/src/adapters/source-adapters/ChainlinkSourceAdapter.sol +++ b/src/adapters/source-adapters/ChainlinkSourceAdapter.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.17; import {DecimalLib} from "../lib/DecimalLib.sol"; import {IAggregatorV3Source} from "../../interfaces/chainlink/IAggregatorV3Source.sol"; import {DiamondRootOval} from "../../DiamondRootOval.sol"; +import "forge-std/console.sol"; /** * @title ChainlinkSourceAdapter contract to read data from Chainlink aggregator and standardize it for Oval. diff --git a/test/fork/adapters/ChainlinkSourceAdapter.sol b/test/fork/adapters/ChainlinkSourceAdapter.sol index 096221f..d44a665 100644 --- a/test/fork/adapters/ChainlinkSourceAdapter.sol +++ b/test/fork/adapters/ChainlinkSourceAdapter.sol @@ -45,26 +45,26 @@ contract ChainlinkSourceAdapterTest is CommonTest { uint256 targetTime = block.timestamp - 1 hours; (uint80 latestRound,,,,) = chainlink.latestRoundData(); (int256 lookBackPrice, uint256 lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 10); - (, int256 answer, uint256 startedAt,,) = chainlink.getRoundData(latestRound - 1); - assertTrue(startedAt <= targetTime); // The time from the chainlink source is at least 1 hours old. + (, int256 answer,, uint256 updatedAt,) = chainlink.getRoundData(latestRound - 1); + assertTrue(updatedAt <= targetTime); // The time from the chainlink source is at least 1 hours old. assertTrue(scaleChainlinkTo18(answer) == lookBackPrice); - assertTrue(startedAt == lookBackTimestamp); + assertTrue(updatedAt == lookBackTimestamp); // Next, try looking back 2 hours. Equally, we should get the price from 2 rounds ago. targetTime = block.timestamp - 2 hours; (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 10); - (, answer, startedAt,,) = chainlink.getRoundData(latestRound - 2); - assertTrue(startedAt <= targetTime); // The time from the chainlink source is at least 2 hours old. + (, answer,, updatedAt,) = chainlink.getRoundData(latestRound - 2); + assertTrue(updatedAt <= targetTime); // The time from the chainlink source is at least 2 hours old. assertTrue(scaleChainlinkTo18(answer) == lookBackPrice); - assertTrue(startedAt == lookBackTimestamp); + assertTrue(updatedAt == lookBackTimestamp); // Now, try 3 hours old. again, The value should be at least 3 hours old. However, for this lookback the chainlink // souce was updated 2x in the interval. Therefore, we should get the price from 4 rounds ago. targetTime = block.timestamp - 3 hours; (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 10); - (, answer, startedAt,,) = chainlink.getRoundData(latestRound - 4); - assertTrue(startedAt <= block.timestamp - 3 hours); // The time from the chainlink source is at least 3 hours old. - assertTrue(startedAt > block.timestamp - 4 hours); // Time from chainlink source is at not more than 4 hours. + (, answer,, updatedAt,) = chainlink.getRoundData(latestRound - 4); + assertTrue(updatedAt <= block.timestamp - 3 hours); // The time from the chainlink source is at least 3 hours old. + assertTrue(updatedAt > block.timestamp - 4 hours); // Time from chainlink source is at not more than 4 hours. } function testCorrectlyBoundsMaxLookBack() public { @@ -74,19 +74,19 @@ contract ChainlinkSourceAdapterTest is CommonTest { uint256 targetTime = block.timestamp - 2 hours; (int256 lookBackPrice, uint256 lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 2); (uint80 latestRound,,,,) = chainlink.latestRoundData(); - (, int256 answer, uint256 startedAt,,) = chainlink.getRoundData(latestRound - 2); + (, int256 answer,, uint256 updatedAt,) = chainlink.getRoundData(latestRound - 2); assertTrue(scaleChainlinkTo18(answer) == lookBackPrice); - assertTrue(startedAt == lookBackTimestamp); + assertTrue(updatedAt == lookBackTimestamp); // Now, lookback longer than 2 hours. should get the same value as before. targetTime = block.timestamp - 3 hours; (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 2); assertTrue(scaleChainlinkTo18(answer) == lookBackPrice); - assertTrue(startedAt == lookBackTimestamp); + assertTrue(updatedAt == lookBackTimestamp); targetTime = block.timestamp - 10 hours; (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 2); assertTrue(scaleChainlinkTo18(answer) == lookBackPrice); - assertTrue(startedAt == lookBackTimestamp); + assertTrue(updatedAt == lookBackTimestamp); } function testNonHistoricalData() public { diff --git a/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol b/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol new file mode 100644 index 0000000..e6b51b5 --- /dev/null +++ b/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.17; + +// These tests show that we can treat redstone exactly as chainlink and use it within the Oval ecosystem without the +// need for a new adapter. This assumes the use of Redstone Classic. + +import {CommonTest} from "../../Common.sol"; +import {BaseController} from "../../../src/controllers/BaseController.sol"; + +import {DecimalLib} from "../../../src/adapters/lib/DecimalLib.sol"; +import {ChainlinkSourceAdapter} from "../../../src/adapters/source-adapters/ChainlinkSourceAdapter.sol"; + +import {IAggregatorV3Source} from "../../../src/interfaces/chainlink/IAggregatorV3Source.sol"; +import {RedstonePriceFeedWithRounds} from "../../../src/oracles/RedstonePriceFeedWithRounds.sol"; + +contract TestedSourceAdapter is ChainlinkSourceAdapter { + constructor(IAggregatorV3Source source) ChainlinkSourceAdapter(source) {} + + function internalLatestData() public view override returns (int256, uint256) {} + + function canUnlock(address caller, uint256 cachedLatestTimestamp) public view virtual override returns (bool) {} + + function lockWindow() public view virtual override returns (uint256) {} + + function maxTraversal() public view virtual override returns (uint256) {} +} + +contract RedstoneAsChainlinkSourceAdapterTest is CommonTest { + uint256 targetBlock = 19889008; + + RedstonePriceFeedWithRounds redstone; + TestedSourceAdapter sourceAdapter; + + function setUp() public { + vm.createSelectFork("mainnet", targetBlock); + redstone = RedstonePriceFeedWithRounds(0xdDb6F90fFb4d3257dd666b69178e5B3c5Bf41136); // Redstone weETH + sourceAdapter = new TestedSourceAdapter(IAggregatorV3Source(address(redstone))); + } + + function testCorrectlyStandardizesOutputs() public { + (, int256 latestRedstoneAnswer,, uint256 latestRedstoneTimestamp,) = redstone.latestRoundData(); + (int256 latestSourceAnswer, uint256 latestSourceTimestamp) = sourceAdapter.getLatestSourceData(); + assertTrue(scaleRedstoneTo18(latestRedstoneAnswer) == latestSourceAnswer); + assertTrue(latestSourceTimestamp == latestRedstoneTimestamp); + } + + function testCanApplyRedstoneUpdateToSource() public { + (uint80 roundId, int256 latestAnswer, uint256 latestTimestamp,,) = redstone.latestRoundData(); + // Values read from the contract at block before applying the update. + assertTrue(roundId == 2009); + assertTrue(latestAnswer == 316882263951); + assertTrue(latestTimestamp == 1715934227); + applyKnownRedstoneUpdate(); + (roundId, latestAnswer, latestTimestamp,,) = redstone.latestRoundData(); + assertTrue(roundId == 2010); + assertTrue(latestAnswer == 313659742144); + assertTrue(latestTimestamp == block.timestamp); + } + + function testCorrectlyLooksBackThroughRounds() public { + // Try fetching the price from some periods in the past and make sure it returns the corespending value for + // given historic lookback. By looking at the contract history on-chain around the blocknumber, we can see + // how many rounds back we expect to look. Looking 1 hour back shows no updates were applied in that interval. + // we should be able to query one hour ago and get the latest round data from both the redstone source and the + // adapter. + uint256 targetTime = block.timestamp - 1 hours; + (uint80 latestRound,,,,) = redstone.latestRoundData(); + + (int256 lookBackPrice, uint256 lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 10); + (, int256 answer,, uint256 updatedAt,) = redstone.getRoundData(latestRound); + assertTrue(updatedAt <= targetTime); + assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); + assertTrue(updatedAt == lookBackTimestamp); + + // Next, try looking back 2 hours. by looking on-chain we can see only one update was applied. Therefore we + // should get the values from latestRound -1 (one update applied relative to the "latest" round). + targetTime = block.timestamp - 2 hours; + (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 10); + (, answer,, updatedAt,) = redstone.getRoundData(latestRound - 1); + assertTrue(updatedAt <= targetTime); + assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); + assertTrue(updatedAt == lookBackTimestamp); + + // Next, try land at 2 rounds ago. Again, by looking on-chain, we can see this is ~2 23 mins before the current + // fork timestamp. We should be able to show the value is the oldest value within this interval. + targetTime = block.timestamp - 2 hours - 23 minutes; + (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 10); + (, answer,, updatedAt,) = redstone.getRoundData(latestRound - 2); + assertTrue(updatedAt <= targetTime); + assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); + assertTrue(updatedAt == lookBackTimestamp); + + // Now, try 3 hours old. On-chain there were 5 updates in this interval. we should be able to show the value is + // the oldest value within this interval. + targetTime = block.timestamp - 3 hours; + (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 10); + (, answer,, updatedAt,) = redstone.getRoundData(latestRound - 5); + assertTrue(updatedAt <= targetTime); + assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); + assertTrue(updatedAt == lookBackTimestamp); + } + + function testCorrectlyBoundsMaxLookBack() public { + // If we limit how far we can lookback the source should correctly return the oldest data it can find, up to + // that limit. From the previous tests we showed that looking back 2 hours 23 hours returns the price from round + // 2. If we try look back longer than this we should get the price from round 2, no matter how far we look back, + // if we bound the maximum lookback to 2 rounds. + uint256 targetTime = block.timestamp - 2 hours - 23 minutes; + (int256 lookBackPrice, uint256 lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 2); + (uint80 latestRound,,,,) = redstone.latestRoundData(); + (, int256 answer,, uint256 updatedAt,) = redstone.getRoundData(latestRound - 2); + assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); + assertTrue(updatedAt == lookBackTimestamp); + + // Now, lookback longer than 2 hours. should get the same value as before. + targetTime = block.timestamp - 3 hours; + (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 2); + assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); + assertTrue(updatedAt == lookBackTimestamp); + targetTime = block.timestamp - 10 hours; + (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 2); + assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); + assertTrue(updatedAt == lookBackTimestamp); + } + + function testNonHistoricalData() public { + uint256 targetTime = block.timestamp - 1 hours; + + (, int256 answer,, uint256 updatedAt,) = redstone.latestRoundData(); + + (int256 lookBackPrice, uint256 lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 0); + assertEq(lookBackPrice / 10 ** 10, answer); + assertEq(lookBackTimestamp, updatedAt); + } + + function applyKnownRedstoneUpdate() internal { + // Update payload taken from: https://etherscan.io/tx/0xbcde8a894337a7e1f29dcad1f78cb0246c1b29305b5c62e43cf0e1801acc11c9 + bytes memory updatePayload = + hex"c14c92040000000000000000000000000000000000000000000000000000018f86086c107765455448000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000490793d7c0018f86086c1000000020000001f6c1ccae51e44aa9e5f57431f59cbd228c190ba072e1eb0af683fef58e02ac0b7f6432b819356ed51210945643f7362871371344395bf48df443663ee525dcef1c7765455448000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000490793d7c0018f86086c100000002000000180353e157cebadd3ba9a25140b95360e0deb02f23274d275301e53680c135c7c0033dcf794366503d9d500d9003842d03abe812bc9535b644e1b5f0cee72845f1c00023137313539343036363233373123302e332e3623646174612d7061636b616765732d77726170706572000029000002ed57011e0000"; + vm.prank(0x517a67D809549093bD3Ef7C6195546B8BDF24C04); // Permissioned Redstone updater. + + (bool success,) = address(redstone).call(updatePayload); + require(success, "Failed to update Redstone data"); + } + + function scaleRedstoneTo18(int256 input) public pure returns (int256) { + return (input * 10 ** 18) / 10 ** 8; + } +} diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol index dcff987..f5563c7 100644 --- a/test/unit/RedStoneOracle.sol +++ b/test/unit/RedStoneOracle.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; -import {RedstoneConsumerNumericBase} from "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol"; + +import {RedstoneConsumerNumericBase} from + "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol"; import {CommonTest} from "../Common.sol"; @@ -15,13 +17,11 @@ import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3S import {TestedSourceAdapter} from "../fork/adapters/ChainlinkSourceAdapter.sol"; contract MockRedstonePayload is CommonTest { - function getRedstonePayload( - string memory priceFeed - ) public returns (bytes memory) { + function getRedstonePayload(string memory priceFeed) public returns (bytes memory) { string[] memory args = new string[](4); args[0] = "node"; args[1] = "--no-warnings"; - args[2] = "./redstone/getRedstonePayload.js"; + args[2] = "./scripts/src/RedstoneHelpers/getRedstonePayload.js"; args[3] = priceFeed; return vm.ffi(args); @@ -34,28 +34,17 @@ contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { function setUp() public { redstoneOracle = new RedstonePriceFeedWithRounds(bytes32("BTC")); - sourceAdapter = new TestedSourceAdapter( - IAggregatorV3Source(address(redstoneOracle)) - ); + sourceAdapter = new TestedSourceAdapter(IAggregatorV3Source(address(redstoneOracle))); } function pushPrice() internal returns (uint256) { bytes memory data = getRedstonePayload("BTC"); - ( - bytes memory redstonePayload, - uint256 timestampMilliseconds, - uint256 updatePrice - ) = abi.decode(data, (bytes, uint256, uint256)); - - bytes memory encodedFunction = abi.encodeWithSignature( - "updateDataFeedsValues(uint256)", - timestampMilliseconds - ); - bytes memory encodedFunctionWithRedstonePayload = abi.encodePacked( - encodedFunction, - redstonePayload - ); + (bytes memory redstonePayload, uint256 timestampMilliseconds, uint256 updatePrice) = + abi.decode(data, (bytes, uint256, uint256)); + + bytes memory encodedFunction = abi.encodeWithSignature("updateDataFeedsValues(uint256)", timestampMilliseconds); + bytes memory encodedFunctionWithRedstonePayload = abi.encodePacked(encodedFunction, redstonePayload); address(redstoneOracle).call(encodedFunctionWithRedstonePayload); @@ -65,13 +54,8 @@ contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { function testPushPrice() public { vm.warp(1234); uint256 updatePrice = pushPrice(); - ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ) = redstoneOracle.latestRoundData(); + (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) = + redstoneOracle.latestRoundData(); assertEq(roundId, 1); assertEq(uint256(answer), updatePrice); @@ -82,20 +66,9 @@ contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { function testCorrectlyStandardizesOutputs() public { uint256 pushedPrice = pushPrice(); - ( - , - int256 latestChainlinkAnswer, - , - uint256 latestChainlinkTimestamp, - - ) = redstoneOracle.latestRoundData(); - ( - int256 latestSourceAnswer, - uint256 latestSourceTimestamp - ) = sourceAdapter.getLatestSourceData(); - assertTrue( - scaleChainlinkTo18(latestChainlinkAnswer) == latestSourceAnswer - ); + (, int256 latestChainlinkAnswer,, uint256 latestChainlinkTimestamp,) = redstoneOracle.latestRoundData(); + (int256 latestSourceAnswer, uint256 latestSourceTimestamp) = sourceAdapter.getLatestSourceData(); + assertTrue(scaleChainlinkTo18(latestChainlinkAnswer) == latestSourceAnswer); assertTrue(pushedPrice == uint256(latestChainlinkAnswer)); assertTrue(latestSourceTimestamp == latestChainlinkTimestamp); } From 7744fe06fb9eaf2b33579b6ad19012cd83e9c530 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Sun, 19 May 2024 14:24:19 +0100 Subject: [PATCH 16/31] fix: ci Signed-off-by: Pablo Maldonado --- .github/workflows/test.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5ccec5b..29ca00b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,11 +71,6 @@ jobs: with: version: "nightly-de33b6af53005037b463318d2628b5cfcaf39916" - - name: "Navigate to redstone and install dependencies" - run: | - cd redstone - yarn install - - name: "Show the Foundry config" run: "forge config" From cfc7eee38c4c1aff2cef40db30dfb9cb29978e9d Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Sun, 19 May 2024 14:27:12 +0100 Subject: [PATCH 17/31] fix: ci bis Signed-off-by: Pablo Maldonado --- .github/workflows/test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 29ca00b..ef3071c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,6 +71,11 @@ jobs: with: version: "nightly-de33b6af53005037b463318d2628b5cfcaf39916" + - name: "Navigate to scripts and install dependencies" + run: | + cd scripts + yarn install + - name: "Show the Foundry config" run: "forge config" From 0a7c2a35f2e37e095d92fb23ca7fdd5810219a4e Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 11:44:00 +0100 Subject: [PATCH 18/31] refactor: clean libs Signed-off-by: Pablo Maldonado --- .gitmodules | 3 - .../v0.8/interfaces/AggregatorV3Interface.sol | 32 -- .../proxy/utils/Initializable.sol | 165 -------- .../utils/AddressUpgradeable.sol | 219 ---------- .../contracts/utils/math/SafeMath.sol | 227 ----------- .../contracts/core/CalldataExtractor.sol | 163 -------- .../contracts/core/ProxyConnector.sol | 141 ------- .../contracts/core/RedstoneConstants.sol | 52 --- .../contracts/core/RedstoneConsumerBase.sol | 310 -------------- .../core/RedstoneConsumerBytesBase.sol | 217 ---------- .../core/RedstoneConsumerNumericBase.sol | 112 ------ .../contracts/core/RedstoneDefaultsLib.sol | 39 -- .../contracts/libs/BitmapLib.sol | 14 - .../contracts/libs/HardhatLoggerLib.sol | 14 - .../contracts/libs/NumbersLib.sol | 15 - .../contracts/libs/NumericArrayLib.sol | 56 --- .../contracts/libs/SignatureLib.sol | 27 -- .../contracts/core/IRedstoneAdapter.sol | 89 ----- .../contracts/core/RedstoneAdapterBase.sol | 377 ------------------ .../MergedPriceFeedAdapterCommon.sol | 18 - .../contracts/price-feeds/PriceFeedBase.sol | 133 ------ .../price-feeds/PriceFeedsAdapterBase.sol | 46 --- ...dPriceFeedAdapterWithRoundsPrimaryProd.sol | 24 -- ...iceFeedAdapterWithoutRoundsPrimaryProd.sol | 24 -- .../PriceFeedsAdapterWithRoundsMainDemo.sol | 21 - ...PriceFeedsAdapterWithoutRoundsMainDemo.sol | 25 -- .../data-services/VSTPriceFeedsAdapter.sol | 51 --- ...PriceFeedsAdapterWithoutRoundsMainDemo.sol | 29 -- .../price-feeds/interfaces/IPriceFeed.sol | 20 - .../interfaces/IPriceFeedLegacy.sol | 24 -- .../MergedPriceFeedAdapterWithRounds.sol | 52 --- .../with-rounds/PriceFeedWithRounds.sol | 62 --- .../PriceFeedsAdapterWithRounds.sol | 183 --------- .../MergedPriceFeedAdapterWithoutRounds.sol | 53 --- .../without-rounds/PriceFeedWithoutRounds.sol | 36 -- .../PriceFeedsAdapterWithoutRounds.sol | 57 --- .../without-rounds/SinglePriceFeedAdapter.sol | 144 ------- .../SinglePriceFeedAdapterWithClearing.sol | 72 ---- lib/openzeppelin-contracts-upgradeable | 1 - 39 files changed, 3347 deletions(-) delete mode 100644 lib/RedStoneBaseContracts/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol delete mode 100644 lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol delete mode 100644 lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol delete mode 100644 lib/RedStoneBaseContracts/@openzeppelin/contracts/utils/math/SafeMath.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/ProxyConnector.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConstants.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneDefaultsLib.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/BitmapLib.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/HardhatLoggerLib.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumbersLib.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol delete mode 100644 lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/SignatureLib.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/IRedstoneAdapter.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/RedstoneAdapterBase.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/MergedPriceFeedAdapterCommon.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithRoundsPrimaryProd.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithoutRoundsPrimaryProd.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithRoundsMainDemo.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithoutRoundsMainDemo.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapter.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapterWithoutRoundsMainDemo.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeed.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeedLegacy.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedWithRounds.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedsAdapterWithRounds.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/MergedPriceFeedAdapterWithoutRounds.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedWithoutRounds.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedsAdapterWithoutRounds.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapter.sol delete mode 100644 lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapterWithClearing.sol delete mode 160000 lib/openzeppelin-contracts-upgradeable diff --git a/.gitmodules b/.gitmodules index a3b6416..e80ffd8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,3 @@ [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/openzeppelin/openzeppelin-contracts -[submodule "lib/openzeppelin-contracts-upgradeable"] - path = lib/openzeppelin-contracts-upgradeable - url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable diff --git a/lib/RedStoneBaseContracts/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol b/lib/RedStoneBaseContracts/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol deleted file mode 100644 index cd72a0c..0000000 --- a/lib/RedStoneBaseContracts/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface AggregatorV3Interface { - function decimals() external view returns (uint8); - - function description() external view returns (string memory); - - function version() external view returns (uint256); - - function getRoundData(uint80 _roundId) - external - view - returns ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ); - - function latestRoundData() - external - view - returns ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ); -} diff --git a/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol b/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol deleted file mode 100644 index 111d8d1..0000000 --- a/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol) - -pragma solidity ^0.8.2; - -import "../../utils/AddressUpgradeable.sol"; - -/** - * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed - * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an - * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer - * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. - * - * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be - * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in - * case an upgrade adds a module that needs to be initialized. - * - * For example: - * - * [.hljs-theme-light.nopadding] - * ``` - * contract MyToken is ERC20Upgradeable { - * function initialize() initializer public { - * __ERC20_init("MyToken", "MTK"); - * } - * } - * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { - * function initializeV2() reinitializer(2) public { - * __ERC20Permit_init("MyToken"); - * } - * } - * ``` - * - * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as - * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. - * - * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure - * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. - * - * [CAUTION] - * ==== - * Avoid leaving a contract uninitialized. - * - * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation - * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke - * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: - * - * [.hljs-theme-light.nopadding] - * ``` - * /// @custom:oz-upgrades-unsafe-allow constructor - * constructor() { - * _disableInitializers(); - * } - * ``` - * ==== - */ -abstract contract Initializable { - /** - * @dev Indicates that the contract has been initialized. - * @custom:oz-retyped-from bool - */ - uint8 private _initialized; - - /** - * @dev Indicates that the contract is in the process of being initialized. - */ - bool private _initializing; - - /** - * @dev Triggered when the contract has been initialized or reinitialized. - */ - event Initialized(uint8 version); - - /** - * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, - * `onlyInitializing` functions can be used to initialize parent contracts. - * - * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a - * constructor. - * - * Emits an {Initialized} event. - */ - modifier initializer() { - bool isTopLevelCall = !_initializing; - require( - (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), - "Initializable: contract is already initialized" - ); - _initialized = 1; - if (isTopLevelCall) { - _initializing = true; - } - _; - if (isTopLevelCall) { - _initializing = false; - emit Initialized(1); - } - } - - /** - * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the - * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be - * used to initialize parent contracts. - * - * A reinitializer may be used after the original initialization step. This is essential to configure modules that - * are added through upgrades and that require initialization. - * - * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` - * cannot be nested. If one is invoked in the context of another, execution will revert. - * - * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in - * a contract, executing them in the right order is up to the developer or operator. - * - * WARNING: setting the version to 255 will prevent any future reinitialization. - * - * Emits an {Initialized} event. - */ - modifier reinitializer(uint8 version) { - require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); - _initialized = version; - _initializing = true; - _; - _initializing = false; - emit Initialized(version); - } - - /** - * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the - * {initializer} and {reinitializer} modifiers, directly or indirectly. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - - /** - * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. - * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized - * to any version. It is recommended to use this to lock implementation contracts that are designed to be called - * through proxies. - * - * Emits an {Initialized} event the first time it is successfully executed. - */ - function _disableInitializers() internal virtual { - require(!_initializing, "Initializable: contract is initializing"); - if (_initialized < type(uint8).max) { - _initialized = type(uint8).max; - emit Initialized(type(uint8).max); - } - } - - /** - * @dev Returns the highest version that has been initialized. See {reinitializer}. - */ - function _getInitializedVersion() internal view returns (uint8) { - return _initialized; - } - - /** - * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. - */ - function _isInitializing() internal view returns (bool) { - return _initializing; - } -} diff --git a/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol b/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol deleted file mode 100644 index 203c05f..0000000 --- a/lib/RedStoneBaseContracts/@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol +++ /dev/null @@ -1,219 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) - -pragma solidity ^0.8.1; - -/** - * @dev Collection of functions related to the address type - */ -library AddressUpgradeable { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResultFromTarget(target, success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResultFromTarget(target, success, returndata, errorMessage); - } - - /** - * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling - * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. - * - * _Available since v4.8._ - */ - function verifyCallResultFromTarget( - address target, - bool success, - bytes memory returndata, - string memory errorMessage - ) internal view returns (bytes memory) { - if (success) { - if (returndata.length == 0) { - // only check isContract if the call was successful and the return data is empty - // otherwise we already know that it was a contract - require(isContract(target), "Address: call to non-contract"); - } - return returndata; - } else { - _revert(returndata, errorMessage); - } - } - - /** - * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason or using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - _revert(returndata, errorMessage); - } - } - - function _revert(bytes memory returndata, string memory errorMessage) private pure { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - /// @solidity memory-safe-assembly - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } -} diff --git a/lib/RedStoneBaseContracts/@openzeppelin/contracts/utils/math/SafeMath.sol b/lib/RedStoneBaseContracts/@openzeppelin/contracts/utils/math/SafeMath.sol deleted file mode 100644 index 550f0e7..0000000 --- a/lib/RedStoneBaseContracts/@openzeppelin/contracts/utils/math/SafeMath.sol +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol) - -pragma solidity ^0.8.0; - -// CAUTION -// This version of SafeMath should only be used with Solidity 0.8 or later, -// because it relies on the compiler's built in overflow checks. - -/** - * @dev Wrappers over Solidity's arithmetic operations. - * - * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler - * now has built in overflow checking. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - uint256 c = a + b; - if (c < a) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the subtraction of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b > a) return (false, 0); - return (true, a - b); - } - } - - /** - * @dev Returns the multiplication of two unsigned integers, with an overflow flag. - * - * _Available since v3.4._ - */ - function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) return (true, 0); - uint256 c = a * b; - if (c / a != b) return (false, 0); - return (true, c); - } - } - - /** - * @dev Returns the division of two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a / b); - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. - * - * _Available since v3.4._ - */ - function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { - unchecked { - if (b == 0) return (false, 0); - return (true, a % b); - } - } - - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - return a + b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return a - b; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - return a * b; - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return a / b; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return a % b; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {trySub}. - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - * - Subtraction cannot overflow. - */ - function sub( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b <= a, errorMessage); - return a - b; - } - } - - /** - * @dev Returns the integer division of two unsigned integers, reverting with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function div( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a / b; - } - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * reverting with custom message when dividing by zero. - * - * CAUTION: This function is deprecated because it requires allocating memory for the error - * message unnecessarily. For custom revert reasons use {tryMod}. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - * - The divisor cannot be zero. - */ - function mod( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { - unchecked { - require(b > 0, errorMessage); - return a % b; - } - } -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol deleted file mode 100644 index d1c5f7a..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/CalldataExtractor.sol +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.4; - -import "@openzeppelin/contracts/utils/math/SafeMath.sol"; - -import "./RedstoneConstants.sol"; - -/** - * @title The base contract with the main logic of data extraction from calldata - * @author The Redstone Oracles team - * @dev This contract was created to reuse the same logic in the RedstoneConsumerBase - * and the ProxyConnector contracts - */ -contract CalldataExtractor is RedstoneConstants { - using SafeMath for uint256; - - error DataPackageTimestampMustNotBeZero(); - error DataPackageTimestampsMustBeEqual(); - error RedstonePayloadMustHaveAtLeastOneDataPackage(); - - function extractTimestampsAndAssertAllAreEqual() public pure returns (uint256 extractedTimestamp) { - uint256 calldataNegativeOffset = _extractByteSizeOfUnsignedMetadata(); - uint256 dataPackagesCount = _extractDataPackagesCountFromCalldata(calldataNegativeOffset); - - if (dataPackagesCount == 0) { - revert RedstonePayloadMustHaveAtLeastOneDataPackage(); - } - - calldataNegativeOffset += DATA_PACKAGES_COUNT_BS; - for (uint256 dataPackageIndex = 0; dataPackageIndex < dataPackagesCount; dataPackageIndex++) { - uint256 dataPackageByteSize = _getDataPackageByteSize(calldataNegativeOffset); - - // Extracting timestamp for the current data package - uint48 dataPackageTimestamp; // uint48, because timestamp uses 6 bytes - uint256 timestampNegativeOffset = (calldataNegativeOffset + TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS); - uint256 timestampOffset = msg.data.length - timestampNegativeOffset; - assembly { - dataPackageTimestamp := calldataload(timestampOffset) - } - - if (dataPackageTimestamp == 0) { - revert DataPackageTimestampMustNotBeZero(); - } - - if (extractedTimestamp == 0) { - extractedTimestamp = dataPackageTimestamp; - } else if (dataPackageTimestamp != extractedTimestamp) { - revert DataPackageTimestampsMustBeEqual(); - } - - calldataNegativeOffset += dataPackageByteSize; - } - } - - function _getDataPackageByteSize(uint256 calldataNegativeOffset) internal pure returns (uint256) { - ( - uint256 dataPointsCount, - uint256 eachDataPointValueByteSize - ) = _extractDataPointsDetailsForDataPackage(calldataNegativeOffset); - - return - dataPointsCount * - (DATA_POINT_SYMBOL_BS + eachDataPointValueByteSize) + - DATA_PACKAGE_WITHOUT_DATA_POINTS_BS; - } - - function _extractByteSizeOfUnsignedMetadata() internal pure returns (uint256) { - // Checking if the calldata ends with the RedStone marker - bool hasValidRedstoneMarker; - assembly { - let calldataLast32Bytes := calldataload(sub(calldatasize(), STANDARD_SLOT_BS)) - hasValidRedstoneMarker := eq( - REDSTONE_MARKER_MASK, - and(calldataLast32Bytes, REDSTONE_MARKER_MASK) - ) - } - if (!hasValidRedstoneMarker) { - revert CalldataMustHaveValidPayload(); - } - - // Using uint24, because unsigned metadata byte size number has 3 bytes - uint24 unsignedMetadataByteSize; - if (REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS > msg.data.length) { - revert CalldataOverOrUnderFlow(); - } - assembly { - unsignedMetadataByteSize := calldataload( - sub(calldatasize(), REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS) - ) - } - uint256 calldataNegativeOffset = unsignedMetadataByteSize - + UNSIGNED_METADATA_BYTE_SIZE_BS - + REDSTONE_MARKER_BS; - if (calldataNegativeOffset + DATA_PACKAGES_COUNT_BS > msg.data.length) { - revert IncorrectUnsignedMetadataSize(); - } - return calldataNegativeOffset; - } - - // We return uint16, because unsigned metadata byte size number has 2 bytes - function _extractDataPackagesCountFromCalldata(uint256 calldataNegativeOffset) - internal - pure - returns (uint16 dataPackagesCount) - { - uint256 calldataNegativeOffsetWithStandardSlot = calldataNegativeOffset + STANDARD_SLOT_BS; - if (calldataNegativeOffsetWithStandardSlot > msg.data.length) { - revert CalldataOverOrUnderFlow(); - } - assembly { - dataPackagesCount := calldataload( - sub(calldatasize(), calldataNegativeOffsetWithStandardSlot) - ) - } - return dataPackagesCount; - } - - function _extractDataPointValueAndDataFeedId( - uint256 calldataNegativeOffsetForDataPackage, - uint256 defaultDataPointValueByteSize, - uint256 dataPointIndex - ) internal pure virtual returns (bytes32 dataPointDataFeedId, uint256 dataPointValue) { - uint256 negativeOffsetToDataPoints = calldataNegativeOffsetForDataPackage + DATA_PACKAGE_WITHOUT_DATA_POINTS_BS; - uint256 dataPointNegativeOffset = negativeOffsetToDataPoints.add( - (1 + dataPointIndex).mul((defaultDataPointValueByteSize + DATA_POINT_SYMBOL_BS)) - ); - uint256 dataPointCalldataOffset = msg.data.length.sub(dataPointNegativeOffset); - assembly { - dataPointDataFeedId := calldataload(dataPointCalldataOffset) - dataPointValue := calldataload(add(dataPointCalldataOffset, DATA_POINT_SYMBOL_BS)) - } - } - - function _extractDataPointsDetailsForDataPackage(uint256 calldataNegativeOffsetForDataPackage) - internal - pure - returns (uint256 dataPointsCount, uint256 eachDataPointValueByteSize) - { - // Using uint24, because data points count byte size number has 3 bytes - uint24 dataPointsCount_; - - // Using uint32, because data point value byte size has 4 bytes - uint32 eachDataPointValueByteSize_; - - // Extract data points count - uint256 negativeCalldataOffset = calldataNegativeOffsetForDataPackage + SIG_BS; - uint256 calldataOffset = msg.data.length.sub(negativeCalldataOffset + STANDARD_SLOT_BS); - assembly { - dataPointsCount_ := calldataload(calldataOffset) - } - - // Extract each data point value size - calldataOffset = calldataOffset.sub(DATA_POINTS_COUNT_BS); - assembly { - eachDataPointValueByteSize_ := calldataload(calldataOffset) - } - - // Prepare returned values - dataPointsCount = dataPointsCount_; - eachDataPointValueByteSize = eachDataPointValueByteSize_; - } -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/ProxyConnector.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/ProxyConnector.sol deleted file mode 100644 index 8a3ec87..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/ProxyConnector.sol +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.4; - -import "./RedstoneConstants.sol"; -import "./CalldataExtractor.sol"; - -/** - * @title The base contract for forwarding redstone payload to other contracts - * @author The Redstone Oracles team - */ -contract ProxyConnector is RedstoneConstants, CalldataExtractor { - error ProxyCalldataFailedWithoutErrMsg(); - error ProxyCalldataFailedWithStringMessage(string message); - error ProxyCalldataFailedWithCustomError(bytes result); - - function proxyCalldata( - address contractAddress, - bytes memory encodedFunction, - bool forwardValue - ) internal returns (bytes memory) { - bytes memory message = _prepareMessage(encodedFunction); - - (bool success, bytes memory result) = - contractAddress.call{value: forwardValue ? msg.value : 0}(message); - - return _prepareReturnValue(success, result); - } - - function proxyDelegateCalldata(address contractAddress, bytes memory encodedFunction) - internal - returns (bytes memory) - { - bytes memory message = _prepareMessage(encodedFunction); - (bool success, bytes memory result) = contractAddress.delegatecall(message); - return _prepareReturnValue(success, result); - } - - function proxyCalldataView(address contractAddress, bytes memory encodedFunction) - internal - view - returns (bytes memory) - { - bytes memory message = _prepareMessage(encodedFunction); - (bool success, bytes memory result) = contractAddress.staticcall(message); - return _prepareReturnValue(success, result); - } - - function _prepareMessage(bytes memory encodedFunction) private pure returns (bytes memory) { - uint256 encodedFunctionBytesCount = encodedFunction.length; - uint256 redstonePayloadByteSize = _getRedstonePayloadByteSize(); - uint256 resultMessageByteSize = encodedFunctionBytesCount + redstonePayloadByteSize; - - if (redstonePayloadByteSize > msg.data.length) { - revert CalldataOverOrUnderFlow(); - } - - bytes memory message; - - assembly { - message := mload(FREE_MEMORY_PTR) // sets message pointer to first free place in memory - - // Saving the byte size of the result message (it's a standard in EVM) - mstore(message, resultMessageByteSize) - - // Copying function and its arguments - for { - let from := add(BYTES_ARR_LEN_VAR_BS, encodedFunction) - let fromEnd := add(from, encodedFunctionBytesCount) - let to := add(BYTES_ARR_LEN_VAR_BS, message) - } lt (from, fromEnd) { - from := add(from, STANDARD_SLOT_BS) - to := add(to, STANDARD_SLOT_BS) - } { - // Copying data from encodedFunction to message (32 bytes at a time) - mstore(to, mload(from)) - } - - // Copying redstone payload to the message bytes - calldatacopy( - add(message, add(BYTES_ARR_LEN_VAR_BS, encodedFunctionBytesCount)), // address - sub(calldatasize(), redstonePayloadByteSize), // offset - redstonePayloadByteSize // bytes length to copy - ) - - // Updating free memory pointer - mstore( - FREE_MEMORY_PTR, - add( - add(message, add(redstonePayloadByteSize, encodedFunctionBytesCount)), - BYTES_ARR_LEN_VAR_BS - ) - ) - } - - return message; - } - - function _getRedstonePayloadByteSize() private pure returns (uint256) { - uint256 calldataNegativeOffset = _extractByteSizeOfUnsignedMetadata(); - uint256 dataPackagesCount = _extractDataPackagesCountFromCalldata(calldataNegativeOffset); - calldataNegativeOffset += DATA_PACKAGES_COUNT_BS; - for (uint256 dataPackageIndex = 0; dataPackageIndex < dataPackagesCount; dataPackageIndex++) { - uint256 dataPackageByteSize = _getDataPackageByteSize(calldataNegativeOffset); - calldataNegativeOffset += dataPackageByteSize; - } - - return calldataNegativeOffset; - } - - function _prepareReturnValue(bool success, bytes memory result) - internal - pure - returns (bytes memory) - { - if (!success) { - - if (result.length == 0) { - revert ProxyCalldataFailedWithoutErrMsg(); - } else { - bool isStringErrorMessage; - assembly { - let first32BytesOfResult := mload(add(result, BYTES_ARR_LEN_VAR_BS)) - isStringErrorMessage := eq(first32BytesOfResult, STRING_ERR_MESSAGE_MASK) - } - - if (isStringErrorMessage) { - string memory receivedErrMsg; - assembly { - receivedErrMsg := add(result, REVERT_MSG_OFFSET) - } - revert ProxyCalldataFailedWithStringMessage(receivedErrMsg); - } else { - revert ProxyCalldataFailedWithCustomError(result); - } - } - } - - return result; - } -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConstants.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConstants.sol deleted file mode 100644 index ad4bf2f..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConstants.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.4; - -/** - * @title The base contract with helpful constants - * @author The Redstone Oracles team - * @dev It mainly contains redstone-related values, which improve readability - * of other contracts (e.g. CalldataExtractor and RedstoneConsumerBase) - */ -contract RedstoneConstants { - // === Abbreviations === - // BS - Bytes size - // PTR - Pointer (memory location) - // SIG - Signature - - // Solidity and YUL constants - uint256 internal constant STANDARD_SLOT_BS = 32; - uint256 internal constant FREE_MEMORY_PTR = 0x40; - uint256 internal constant BYTES_ARR_LEN_VAR_BS = 32; - uint256 internal constant FUNCTION_SIGNATURE_BS = 4; - uint256 internal constant REVERT_MSG_OFFSET = 68; // Revert message structure described here: https://ethereum.stackexchange.com/a/66173/106364 - uint256 internal constant STRING_ERR_MESSAGE_MASK = 0x08c379a000000000000000000000000000000000000000000000000000000000; - - // RedStone protocol consts - uint256 internal constant SIG_BS = 65; - uint256 internal constant TIMESTAMP_BS = 6; - uint256 internal constant DATA_PACKAGES_COUNT_BS = 2; - uint256 internal constant DATA_POINTS_COUNT_BS = 3; - uint256 internal constant DATA_POINT_VALUE_BYTE_SIZE_BS = 4; - uint256 internal constant DATA_POINT_SYMBOL_BS = 32; - uint256 internal constant DEFAULT_DATA_POINT_VALUE_BS = 32; - uint256 internal constant UNSIGNED_METADATA_BYTE_SIZE_BS = 3; - uint256 internal constant REDSTONE_MARKER_BS = 9; // byte size of 0x000002ed57011e0000 - uint256 internal constant REDSTONE_MARKER_MASK = 0x0000000000000000000000000000000000000000000000000002ed57011e0000; - - // Derived values (based on consts) - uint256 internal constant TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS = 104; // SIG_BS + DATA_POINTS_COUNT_BS + DATA_POINT_VALUE_BYTE_SIZE_BS + STANDARD_SLOT_BS - uint256 internal constant DATA_PACKAGE_WITHOUT_DATA_POINTS_BS = 78; // DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS + SIG_BS - uint256 internal constant DATA_PACKAGE_WITHOUT_DATA_POINTS_AND_SIG_BS = 13; // DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS - uint256 internal constant REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS = 41; // REDSTONE_MARKER_BS + STANDARD_SLOT_BS - - // Error messages - error CalldataOverOrUnderFlow(); - error IncorrectUnsignedMetadataSize(); - error InsufficientNumberOfUniqueSigners(uint256 receivedSignersCount, uint256 requiredSignersCount); - error EachSignerMustProvideTheSameValue(); - error EmptyCalldataPointersArr(); - error InvalidCalldataPointer(); - error CalldataMustHaveValidPayload(); - error SignerNotAuthorised(address receivedSigner); -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol deleted file mode 100644 index b15630a..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBase.sol +++ /dev/null @@ -1,310 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.4; - -import "@openzeppelin/contracts/utils/math/SafeMath.sol"; - -import "./RedstoneConstants.sol"; -import "./RedstoneDefaultsLib.sol"; -import "./CalldataExtractor.sol"; -import "../libs/BitmapLib.sol"; -import "../libs/SignatureLib.sol"; - -/** - * @title The base contract with the main Redstone logic - * @author The Redstone Oracles team - * @dev Do not use this contract directly in consumer contracts, take a - * look at `RedstoneConsumerNumericBase` and `RedstoneConsumerBytesBase` instead - */ -abstract contract RedstoneConsumerBase is CalldataExtractor { - using SafeMath for uint256; - - error GetDataServiceIdNotImplemented(); - - /* ========== VIRTUAL FUNCTIONS (MAY BE OVERRIDDEN IN CHILD CONTRACTS) ========== */ - - /** - * @dev This function must be implemented by the child consumer contract. - * It should return dataServiceId which DataServiceWrapper will use if not provided explicitly . - * If not overridden, value will always have to be provided explicitly in DataServiceWrapper. - * @return dataServiceId being consumed by contract - */ - function getDataServiceId() public view virtual returns (string memory) { - revert GetDataServiceIdNotImplemented(); - } - - /** - * @dev This function must be implemented by the child consumer contract. - * It should return a unique index for a given signer address if the signer - * is authorised, otherwise it should revert - * @param receivedSigner The address of a signer, recovered from ECDSA signature - * @return Unique index for a signer in the range [0..255] - */ - function getAuthorisedSignerIndex(address receivedSigner) public view virtual returns (uint8); - - /** - * @dev This function may be overridden by the child consumer contract. - * It should validate the timestamp against the current time (block.timestamp) - * It should revert with a helpful message if the timestamp is not valid - * @param receivedTimestampMilliseconds Timestamp extracted from calldata - */ - function validateTimestamp(uint256 receivedTimestampMilliseconds) public view virtual { - RedstoneDefaultsLib.validateTimestamp(receivedTimestampMilliseconds); - } - - /** - * @dev This function should be overridden by the child consumer contract. - * @return The minimum required value of unique authorised signers - */ - function getUniqueSignersThreshold() public view virtual returns (uint8) { - return 1; - } - - /** - * @dev This function may be overridden by the child consumer contract. - * It should aggregate values from different signers to a single uint value. - * By default, it calculates the median value - * @param values An array of uint256 values from different signers - * @return Result of the aggregation in the form of a single number - */ - function aggregateValues(uint256[] memory values) public view virtual returns (uint256) { - return RedstoneDefaultsLib.aggregateValues(values); - } - - /* ========== FUNCTIONS WITH IMPLEMENTATION (CAN NOT BE OVERRIDDEN) ========== */ - - /** - * @dev This is an internal helpful function for secure extraction oracle values - * from the tx calldata. Security is achieved by signatures verification, timestamp - * validation, and aggregating values from different authorised signers into a - * single numeric value. If any of the required conditions (e.g. too old timestamp or - * insufficient number of authorised signers) do not match, the function will revert. - * - * Note! You should not call this function in a consumer contract. You can use - * `getOracleNumericValuesFromTxMsg` or `getOracleNumericValueFromTxMsg` instead. - * - * @param dataFeedIds An array of unique data feed identifiers - * @return An array of the extracted and verified oracle values in the same order - * as they are requested in dataFeedIds array - */ - function _securelyExtractOracleValuesFromTxMsg(bytes32[] memory dataFeedIds) - internal - view - returns (uint256[] memory) - { - // Initializing helpful variables and allocating memory - uint256[] memory uniqueSignerCountForDataFeedIds = new uint256[](dataFeedIds.length); - uint256[] memory signersBitmapForDataFeedIds = new uint256[](dataFeedIds.length); - uint256[][] memory valuesForDataFeeds = new uint256[][](dataFeedIds.length); - for (uint256 i = 0; i < dataFeedIds.length; i++) { - // The line below is commented because newly allocated arrays are filled with zeros - // But we left it for better readability - // signersBitmapForDataFeedIds[i] = 0; // <- setting to an empty bitmap - valuesForDataFeeds[i] = new uint256[](getUniqueSignersThreshold()); - } - - // Extracting the number of data packages from calldata - uint256 calldataNegativeOffset = _extractByteSizeOfUnsignedMetadata(); - uint256 dataPackagesCount = _extractDataPackagesCountFromCalldata(calldataNegativeOffset); - calldataNegativeOffset += DATA_PACKAGES_COUNT_BS; - - // Saving current free memory pointer - uint256 freeMemPtr; - assembly { - freeMemPtr := mload(FREE_MEMORY_PTR) - } - - // Data packages extraction in a loop - for (uint256 dataPackageIndex = 0; dataPackageIndex < dataPackagesCount; dataPackageIndex++) { - // Extract data package details and update calldata offset - uint256 dataPackageByteSize = _extractDataPackage( - dataFeedIds, - uniqueSignerCountForDataFeedIds, - signersBitmapForDataFeedIds, - valuesForDataFeeds, - calldataNegativeOffset - ); - calldataNegativeOffset += dataPackageByteSize; - - // Shifting memory pointer back to the "safe" value - assembly { - mstore(FREE_MEMORY_PTR, freeMemPtr) - } - } - - // Validating numbers of unique signers and calculating aggregated values for each dataFeedId - return _getAggregatedValues(valuesForDataFeeds, uniqueSignerCountForDataFeedIds); - } - - /** - * @dev This is a private helpful function, which extracts data for a data package based - * on the given negative calldata offset, verifies them, and in the case of successful - * verification updates the corresponding data package values in memory - * - * @param dataFeedIds an array of unique data feed identifiers - * @param uniqueSignerCountForDataFeedIds an array with the numbers of unique signers - * for each data feed - * @param signersBitmapForDataFeedIds an array of signer bitmaps for data feeds - * @param valuesForDataFeeds 2-dimensional array, valuesForDataFeeds[i][j] contains - * j-th value for the i-th data feed - * @param calldataNegativeOffset negative calldata offset for the given data package - * - * @return An array of the aggregated values - */ - function _extractDataPackage( - bytes32[] memory dataFeedIds, - uint256[] memory uniqueSignerCountForDataFeedIds, - uint256[] memory signersBitmapForDataFeedIds, - uint256[][] memory valuesForDataFeeds, - uint256 calldataNegativeOffset - ) private view returns (uint256) { - uint256 signerIndex; - - ( - uint256 dataPointsCount, - uint256 eachDataPointValueByteSize - ) = _extractDataPointsDetailsForDataPackage(calldataNegativeOffset); - - // We use scopes to resolve problem with too deep stack - { - uint48 extractedTimestamp; - address signerAddress; - bytes32 signedHash; - bytes memory signedMessage; - uint256 signedMessageBytesCount; - - signedMessageBytesCount = dataPointsCount.mul(eachDataPointValueByteSize + DATA_POINT_SYMBOL_BS) - + DATA_PACKAGE_WITHOUT_DATA_POINTS_AND_SIG_BS; //DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS - - uint256 timestampCalldataOffset = msg.data.length.sub( - calldataNegativeOffset + TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS); - - uint256 signedMessageCalldataOffset = msg.data.length.sub( - calldataNegativeOffset + SIG_BS + signedMessageBytesCount); - - assembly { - // Extracting the signed message - signedMessage := extractBytesFromCalldata( - signedMessageCalldataOffset, - signedMessageBytesCount - ) - - // Hashing the signed message - signedHash := keccak256(add(signedMessage, BYTES_ARR_LEN_VAR_BS), signedMessageBytesCount) - - // Extracting timestamp - extractedTimestamp := calldataload(timestampCalldataOffset) - - function initByteArray(bytesCount) -> ptr { - ptr := mload(FREE_MEMORY_PTR) - mstore(ptr, bytesCount) - ptr := add(ptr, BYTES_ARR_LEN_VAR_BS) - mstore(FREE_MEMORY_PTR, add(ptr, bytesCount)) - } - - function extractBytesFromCalldata(offset, bytesCount) -> extractedBytes { - let extractedBytesStartPtr := initByteArray(bytesCount) - calldatacopy( - extractedBytesStartPtr, - offset, - bytesCount - ) - extractedBytes := sub(extractedBytesStartPtr, BYTES_ARR_LEN_VAR_BS) - } - } - - // Validating timestamp - validateTimestamp(extractedTimestamp); - - // Verifying the off-chain signature against on-chain hashed data - signerAddress = SignatureLib.recoverSignerAddress( - signedHash, - calldataNegativeOffset + SIG_BS - ); - signerIndex = getAuthorisedSignerIndex(signerAddress); - } - - // Updating helpful arrays - { - bytes32 dataPointDataFeedId; - uint256 dataPointValue; - for (uint256 dataPointIndex = 0; dataPointIndex < dataPointsCount; dataPointIndex++) { - // Extracting data feed id and value for the current data point - (dataPointDataFeedId, dataPointValue) = _extractDataPointValueAndDataFeedId( - calldataNegativeOffset, - eachDataPointValueByteSize, - dataPointIndex - ); - - for ( - uint256 dataFeedIdIndex = 0; - dataFeedIdIndex < dataFeedIds.length; - dataFeedIdIndex++ - ) { - if (dataPointDataFeedId == dataFeedIds[dataFeedIdIndex]) { - uint256 bitmapSignersForDataFeedId = signersBitmapForDataFeedIds[dataFeedIdIndex]; - - if ( - !BitmapLib.getBitFromBitmap(bitmapSignersForDataFeedId, signerIndex) && /* current signer was not counted for current dataFeedId */ - uniqueSignerCountForDataFeedIds[dataFeedIdIndex] < getUniqueSignersThreshold() - ) { - // Increase unique signer counter - uniqueSignerCountForDataFeedIds[dataFeedIdIndex]++; - - // Add new value - valuesForDataFeeds[dataFeedIdIndex][ - uniqueSignerCountForDataFeedIds[dataFeedIdIndex] - 1 - ] = dataPointValue; - - // Update signers bitmap - signersBitmapForDataFeedIds[dataFeedIdIndex] = BitmapLib.setBitInBitmap( - bitmapSignersForDataFeedId, - signerIndex - ); - } - - // Breaking, as there couldn't be several indexes for the same feed ID - break; - } - } - } - } - - // Return total data package byte size - return - DATA_PACKAGE_WITHOUT_DATA_POINTS_BS + - (eachDataPointValueByteSize + DATA_POINT_SYMBOL_BS) * - dataPointsCount; - } - - /** - * @dev This is a private helpful function, which aggregates values from different - * authorised signers for the given arrays of values for each data feed - * - * @param valuesForDataFeeds 2-dimensional array, valuesForDataFeeds[i][j] contains - * j-th value for the i-th data feed - * @param uniqueSignerCountForDataFeedIds an array with the numbers of unique signers - * for each data feed - * - * @return An array of the aggregated values - */ - function _getAggregatedValues( - uint256[][] memory valuesForDataFeeds, - uint256[] memory uniqueSignerCountForDataFeedIds - ) private view returns (uint256[] memory) { - uint256[] memory aggregatedValues = new uint256[](valuesForDataFeeds.length); - uint256 uniqueSignersThreshold = getUniqueSignersThreshold(); - - for (uint256 dataFeedIndex = 0; dataFeedIndex < valuesForDataFeeds.length; dataFeedIndex++) { - if (uniqueSignerCountForDataFeedIds[dataFeedIndex] < uniqueSignersThreshold) { - revert InsufficientNumberOfUniqueSigners( - uniqueSignerCountForDataFeedIds[dataFeedIndex], - uniqueSignersThreshold); - } - uint256 aggregatedValueForDataFeedId = aggregateValues(valuesForDataFeeds[dataFeedIndex]); - aggregatedValues[dataFeedIndex] = aggregatedValueForDataFeedId; - } - - return aggregatedValues; - } -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol deleted file mode 100644 index f096021..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerBytesBase.sol +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.4; - -import "@openzeppelin/contracts/utils/math/SafeMath.sol"; - -import "./RedstoneConsumerBase.sol"; - -/** - * @title The base contract for Redstone consumers' contracts that allows to - * securely calculate dynamic (array of bytes) redstone oracle values - * @author The Redstone Oracles team - * - * @dev This contract can extend other contracts to allow them - * securely fetch Redstone oracle data from tx calldata in a form of byte arrays. - * - * Note! If you want to use numeric values, use RedstoneConsumerNumericBase contract - * - * We wanted to reuse the core logic from the RedstoneConsumerBase contract, but it - * required few tricks, which are described below: - * - * 1. "Tricky" calldata pointers - we decided to use single uint256 values and store - * the calldata offset in the first 128 bits of those numbers, and the value byte size - * in the last 128 bits of the value. It allowed us to reuse a big part of core logic - * and even slightly optimised memory usage. To optimise gas costs, we left the burden - * of converting tricky calldata pointers to calldata bytes arrays on the consumer - * contracts developers. They can use a helpful `getCalldataBytesFromCalldataPointer` - * function for it - * - * 2. Returning memory pointers instead of actual values - we need to work with - * dynamic bytes arrays in this contract, but the core logic of RedstoneConsumerBase - * contract expects a uint256 number as a result of values aggregation. That's - * why we swtiched to returning a memory pointers instead of actual values. But this is - * more an implementation detail and should not affect end developers during the - * integration with the Redstone protocol - */ -abstract contract RedstoneConsumerBytesBase is RedstoneConsumerBase { - using SafeMath for uint256; - - uint256 constant BITS_COUNT_IN_16_BYTES = 128; - - /** - * @dev This function may be overridden by the child consumer contract. - * It should aggregate values from different signers into a bytes array - * By default, it checks if all the values are identical and returns the first one - * - * @param calldataPointersForValues An array of "tricky" calldata pointers to - * the values provided by different authorised signers. Each tricky calldata pointer - * is a uint256 number, first 128 bits of which represent calldata offset, and the - * last 128 bits - the byte length of the value - * - * @return Result of the aggregation in the form of a bytes array - */ - function aggregateByteValues(uint256[] memory calldataPointersForValues) - public - view - virtual - returns (bytes memory) - { - // Check if all byte arrays are identical - if (calldataPointersForValues.length <= 0) { - revert EmptyCalldataPointersArr(); - } - bytes calldata firstValue = getCalldataBytesFromCalldataPointer(calldataPointersForValues[0]); - bytes32 expectedHash = keccak256(firstValue); - - for (uint256 i = 1; i < calldataPointersForValues.length; i++) { - bytes calldata currentValue = getCalldataBytesFromCalldataPointer( - calldataPointersForValues[i] - ); - if (keccak256(currentValue) != expectedHash) { - revert EachSignerMustProvideTheSameValue(); - } - } - - return firstValue; - } - - /** - * @dev This function may be used to convert a "tricky" calldata pointer into a - * calldata bytes array. You may find it useful while overriding the - * `aggregateByteValues` function - * - * @param trickyCalldataPtr A "tricky" calldata pointer, 128 first bits of which - * represent the offset, and the last 128 bits - the byte length of the value - * - * @return bytesValueInCalldata The corresponding calldata bytes array - */ - function getCalldataBytesFromCalldataPointer(uint256 trickyCalldataPtr) - internal - pure - returns (bytes calldata bytesValueInCalldata) - { - uint256 calldataOffset = _getNumberFromFirst128Bits(trickyCalldataPtr); - uint256 valueByteSize = _getNumberFromLast128Bits(trickyCalldataPtr); - if (calldataOffset + valueByteSize > msg.data.length) { - revert InvalidCalldataPointer(); - } - - assembly { - bytesValueInCalldata.offset := calldataOffset - bytesValueInCalldata.length := valueByteSize - } - } - - /** - * @dev This function can be used in a consumer contract to securely extract an - * oracle value for a given data feed id. Security is achieved by - * signatures verification, timestamp validation, and aggregating bytes values - * from different authorised signers into a single bytes array. If any of the - * required conditions do not match, the function will revert. - * Note! This function expects that tx calldata contains redstone payload in the end - * Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme - * @param dataFeedId bytes32 value that uniquely identifies the data feed - * @return Bytes array with the aggregated oracle value for the given data feed id - */ - function getOracleBytesValueFromTxMsg(bytes32 dataFeedId) internal view returns (bytes memory) { - bytes32[] memory dataFeedIds = new bytes32[](1); - dataFeedIds[0] = dataFeedId; - return getOracleBytesValuesFromTxMsg(dataFeedIds)[0]; - } - - /** - * @dev This function can be used in a consumer contract to securely extract several - * numeric oracle values for a given array of data feed ids. Security is achieved by - * signatures verification, timestamp validation, and aggregating values - * from different authorised signers into a single numeric value. If any of the - * required conditions do not match, the function will revert. - * Note! This function expects that tx calldata contains redstone payload in the end - * Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme - * @param dataFeedIds An array of unique data feed identifiers - * @return arrayOfMemoryPointers TODO - */ - function getOracleBytesValuesFromTxMsg(bytes32[] memory dataFeedIds) - internal - view - returns (bytes[] memory arrayOfMemoryPointers) - { - // The `_securelyExtractOracleValuesFromTxMsg` function contains the main logic - // for the data extraction and validation - uint256[] memory arrayOfExtractedValues = _securelyExtractOracleValuesFromTxMsg(dataFeedIds); - assembly { - arrayOfMemoryPointers := arrayOfExtractedValues - } - } - - /** - * @dev This is a helpful function for the values aggregation - * Unlike in the RedstoneConsumerBase contract, you should not override - * this function. If you want to have a custom aggregation logic, you can - * override the `aggregateByteValues` instead - * - * Note! Unlike in the `RedstoneConsumerBase` this function returns a memory pointer - * to the aggregated bytes array value (instead the value itself) - * - * @param calldataPointersToValues An array of "tricky" calldata pointers to - * the values provided by different authorised signers. Each tricky calldata pointer - * is a uint256 number, first 128 bits of which represent calldata offset, and the - * last 128 bits - the byte length of the value - * - * @return pointerToResultBytesInMemory A memory pointer to the aggregated bytes array - */ - function aggregateValues(uint256[] memory calldataPointersToValues) - public - view - override - returns (uint256 pointerToResultBytesInMemory) - { - bytes memory aggregatedBytes = aggregateByteValues(calldataPointersToValues); - assembly { - pointerToResultBytesInMemory := aggregatedBytes - } - } - - /** - * @dev This function extracts details for a given data point and returns its dataFeedId, - * and a "tricky" calldata pointer for its value - * - * @param calldataNegativeOffsetForDataPackage Calldata offset for the requested data package - * @param dataPointValueByteSize Expected number of bytes for the requested data point value - * @param dataPointIndex Index of the requested data point - * - * @return dataPointDataFeedId a data feed identifier for the extracted data point - * @return dataPointValue a "tricky" calldata pointer for the extracted value - */ - function _extractDataPointValueAndDataFeedId( - uint256 calldataNegativeOffsetForDataPackage, - uint256 dataPointValueByteSize, - uint256 dataPointIndex - ) internal pure override returns (bytes32 dataPointDataFeedId, uint256 dataPointValue) { - uint256 negativeOffsetToDataPoints = calldataNegativeOffsetForDataPackage + DATA_PACKAGE_WITHOUT_DATA_POINTS_BS; - uint256 dataPointNegativeOffset = negativeOffsetToDataPoints - + (1 + dataPointIndex).mul(dataPointValueByteSize + DATA_POINT_SYMBOL_BS); - uint256 dataPointCalldataOffset = msg.data.length.sub(dataPointNegativeOffset); - assembly { - dataPointDataFeedId := calldataload(dataPointCalldataOffset) - dataPointValue := prepareTrickyCalldataPointer( - add(dataPointCalldataOffset, DATA_POINT_SYMBOL_BS), - dataPointValueByteSize - ) - - function prepareTrickyCalldataPointer(calldataOffsetArg, valueByteSize) -> calldataPtr { - calldataPtr := or(shl(BITS_COUNT_IN_16_BYTES, calldataOffsetArg), valueByteSize) - } - } - } - - /// @dev This is a helpful function for "tricky" calldata pointers - function _getNumberFromFirst128Bits(uint256 number) internal pure returns (uint256) { - return number >> 128; - } - - /// @dev This is a helpful function for "tricky" calldata pointers - function _getNumberFromLast128Bits(uint256 number) internal pure returns (uint256) { - return uint128(number); - } -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol deleted file mode 100644 index 212fae5..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.4; - -import "./RedstoneConsumerBase.sol"; - -/** - * @title The base contract for Redstone consumers' contracts that allows to - * securely calculate numeric redstone oracle values - * @author The Redstone Oracles team - * @dev This contract can extend other contracts to allow them - * securely fetch Redstone oracle data from transactions calldata - */ -abstract contract RedstoneConsumerNumericBase is RedstoneConsumerBase { - /** - * @dev This function can be used in a consumer contract to securely extract an - * oracle value for a given data feed id. Security is achieved by - * signatures verification, timestamp validation, and aggregating values - * from different authorised signers into a single numeric value. If any of the - * required conditions do not match, the function will revert. - * Note! This function expects that tx calldata contains redstone payload in the end - * Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme - * @param dataFeedId bytes32 value that uniquely identifies the data feed - * @return Extracted and verified numeric oracle value for the given data feed id - */ - function getOracleNumericValueFromTxMsg(bytes32 dataFeedId) - internal - view - virtual - returns (uint256) - { - bytes32[] memory dataFeedIds = new bytes32[](1); - dataFeedIds[0] = dataFeedId; - return getOracleNumericValuesFromTxMsg(dataFeedIds)[0]; - } - - /** - * @dev This function can be used in a consumer contract to securely extract several - * numeric oracle values for a given array of data feed ids. Security is achieved by - * signatures verification, timestamp validation, and aggregating values - * from different authorised signers into a single numeric value. If any of the - * required conditions do not match, the function will revert. - * Note! This function expects that tx calldata contains redstone payload in the end - * Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme - * @param dataFeedIds An array of unique data feed identifiers - * @return An array of the extracted and verified oracle values in the same order - * as they are requested in the dataFeedIds array - */ - function getOracleNumericValuesFromTxMsg(bytes32[] memory dataFeedIds) - internal - view - virtual - returns (uint256[] memory) - { - return _securelyExtractOracleValuesFromTxMsg(dataFeedIds); - } - - /** - * @dev This function works similarly to the `getOracleNumericValuesFromTxMsg` with the - * only difference that it allows to request oracle data for an array of data feeds - * that may contain duplicates - * - * @param dataFeedIdsWithDuplicates An array of data feed identifiers (duplicates are allowed) - * @return An array of the extracted and verified oracle values in the same order - * as they are requested in the dataFeedIdsWithDuplicates array - */ - function getOracleNumericValuesWithDuplicatesFromTxMsg(bytes32[] memory dataFeedIdsWithDuplicates) internal view returns (uint256[] memory) { - // Building an array without duplicates - bytes32[] memory dataFeedIdsWithoutDuplicates = new bytes32[](dataFeedIdsWithDuplicates.length); - bool alreadyIncluded; - uint256 uniqueDataFeedIdsCount = 0; - - for (uint256 indexWithDup = 0; indexWithDup < dataFeedIdsWithDuplicates.length; indexWithDup++) { - // Checking if current element is already included in `dataFeedIdsWithoutDuplicates` - alreadyIncluded = false; - for (uint256 indexWithoutDup = 0; indexWithoutDup < uniqueDataFeedIdsCount; indexWithoutDup++) { - if (dataFeedIdsWithoutDuplicates[indexWithoutDup] == dataFeedIdsWithDuplicates[indexWithDup]) { - alreadyIncluded = true; - break; - } - } - - // Adding if not included - if (!alreadyIncluded) { - dataFeedIdsWithoutDuplicates[uniqueDataFeedIdsCount] = dataFeedIdsWithDuplicates[indexWithDup]; - uniqueDataFeedIdsCount++; - } - } - - // Overriding dataFeedIdsWithoutDuplicates.length - // Equivalent to: dataFeedIdsWithoutDuplicates.length = uniqueDataFeedIdsCount; - assembly { - mstore(dataFeedIdsWithoutDuplicates, uniqueDataFeedIdsCount) - } - - // Requesting oracle values (without duplicates) - uint256[] memory valuesWithoutDuplicates = getOracleNumericValuesFromTxMsg(dataFeedIdsWithoutDuplicates); - - // Preparing result values array - uint256[] memory valuesWithDuplicates = new uint256[](dataFeedIdsWithDuplicates.length); - for (uint256 indexWithDup = 0; indexWithDup < dataFeedIdsWithDuplicates.length; indexWithDup++) { - for (uint256 indexWithoutDup = 0; indexWithoutDup < dataFeedIdsWithoutDuplicates.length; indexWithoutDup++) { - if (dataFeedIdsWithDuplicates[indexWithDup] == dataFeedIdsWithoutDuplicates[indexWithoutDup]) { - valuesWithDuplicates[indexWithDup] = valuesWithoutDuplicates[indexWithoutDup]; - break; - } - } - } - - return valuesWithDuplicates; - } -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneDefaultsLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneDefaultsLib.sol deleted file mode 100644 index 3618ad3..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/core/RedstoneDefaultsLib.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.4; - -import "../libs/NumericArrayLib.sol"; - -/** - * @title Default implementations of virtual redstone consumer base functions - * @author The Redstone Oracles team - */ -library RedstoneDefaultsLib { - uint256 constant DEFAULT_MAX_DATA_TIMESTAMP_DELAY_SECONDS = 3 minutes; - uint256 constant DEFAULT_MAX_DATA_TIMESTAMP_AHEAD_SECONDS = 1 minutes; - - error TimestampFromTooLongFuture(uint256 receivedTimestampSeconds, uint256 blockTimestamp); - error TimestampIsTooOld(uint256 receivedTimestampSeconds, uint256 blockTimestamp); - - function validateTimestamp(uint256 receivedTimestampMilliseconds) internal view { - // Getting data timestamp from future seems quite unlikely - // But we've already spent too much time with different cases - // Where block.timestamp was less than dataPackage.timestamp. - // Some blockchains may case this problem as well. - // That's why we add MAX_BLOCK_TIMESTAMP_DELAY - // and allow data "from future" but with a small delay - uint256 receivedTimestampSeconds = receivedTimestampMilliseconds / 1000; - - if (block.timestamp < receivedTimestampSeconds) { - if ((receivedTimestampSeconds - block.timestamp) > DEFAULT_MAX_DATA_TIMESTAMP_AHEAD_SECONDS) { - revert TimestampFromTooLongFuture(receivedTimestampSeconds, block.timestamp); - } - } else if ((block.timestamp - receivedTimestampSeconds) > DEFAULT_MAX_DATA_TIMESTAMP_DELAY_SECONDS) { - revert TimestampIsTooOld(receivedTimestampSeconds, block.timestamp); - } - } - - function aggregateValues(uint256[] memory values) internal pure returns (uint256) { - return NumericArrayLib.pickMedian(values); - } -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/BitmapLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/BitmapLib.sol deleted file mode 100644 index 27d783f..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/BitmapLib.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -library BitmapLib { - function setBitInBitmap(uint256 bitmap, uint256 bitIndex) internal pure returns (uint256) { - return bitmap | (1 << bitIndex); - } - - function getBitFromBitmap(uint256 bitmap, uint256 bitIndex) internal pure returns (bool) { - uint256 bitAtIndex = bitmap & (1 << bitIndex); - return bitAtIndex > 0; - } -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/HardhatLoggerLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/HardhatLoggerLib.sol deleted file mode 100644 index 181e31d..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/HardhatLoggerLib.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "hardhat/console.sol"; - -library HardhatLoggerLib { - function logUint256Array(uint256[] memory arr) internal view { - for (uint256 i = 0; i < arr.length; i++) { - console.log("\narr", i); - console.log(arr[i]); - } - } -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumbersLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumbersLib.sol deleted file mode 100644 index 086a2ca..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumbersLib.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -library NumbersLib { - uint256 constant BITS_COUNT_IN_16_BYTES = 128; - - function getNumberFromFirst16Bytes(uint256 number) internal pure returns (uint256) { - return uint256(number >> BITS_COUNT_IN_16_BYTES); - } - - function getNumberFromLast16Bytes(uint256 number) internal pure returns (uint256) { - return uint256((number << BITS_COUNT_IN_16_BYTES) >> BITS_COUNT_IN_16_BYTES); - } -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol deleted file mode 100644 index 25ccdf0..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/NumericArrayLib.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -import "@openzeppelin/contracts/utils/math/SafeMath.sol"; - -library NumericArrayLib { - // This function sort array in memory using bubble sort algorithm, - // which performs even better than quick sort for small arrays - - uint256 constant BYTES_ARR_LEN_VAR_BS = 32; - uint256 constant UINT256_VALUE_BS = 32; - - error CanNotPickMedianOfEmptyArray(); - - // This function modifies the array - function pickMedian(uint256[] memory arr) internal pure returns (uint256) { - if (arr.length == 0) { - revert CanNotPickMedianOfEmptyArray(); - } - sort(arr); - uint256 middleIndex = arr.length / 2; - if (arr.length % 2 == 0) { - uint256 sum = SafeMath.add(arr[middleIndex - 1], arr[middleIndex]); - return sum / 2; - } else { - return arr[middleIndex]; - } - } - - function sort(uint256[] memory arr) internal pure { - assembly { - let arrLength := mload(arr) - let valuesPtr := add(arr, BYTES_ARR_LEN_VAR_BS) - let endPtr := add(valuesPtr, mul(arrLength, UINT256_VALUE_BS)) - for { - let arrIPtr := valuesPtr - } lt(arrIPtr, endPtr) { - arrIPtr := add(arrIPtr, UINT256_VALUE_BS) // arrIPtr += 32 - } { - for { - let arrJPtr := valuesPtr - } lt(arrJPtr, arrIPtr) { - arrJPtr := add(arrJPtr, UINT256_VALUE_BS) // arrJPtr += 32 - } { - let arrI := mload(arrIPtr) - let arrJ := mload(arrJPtr) - if lt(arrI, arrJ) { - mstore(arrIPtr, arrJ) - mstore(arrJPtr, arrI) - } - } - } - } - } -} diff --git a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/SignatureLib.sol b/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/SignatureLib.sol deleted file mode 100644 index b2c6a35..0000000 --- a/lib/RedStoneBaseContracts/@redstone-finance/evm-connector/contracts/libs/SignatureLib.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.4; - -library SignatureLib { - uint256 constant ECDSA_SIG_R_BS = 32; - uint256 constant ECDSA_SIG_S_BS = 32; - - function recoverSignerAddress(bytes32 signedHash, uint256 signatureCalldataNegativeOffset) - internal - pure - returns (address) - { - bytes32 r; - bytes32 s; - uint8 v; - assembly { - let signatureCalldataStartPos := sub(calldatasize(), signatureCalldataNegativeOffset) - r := calldataload(signatureCalldataStartPos) - signatureCalldataStartPos := add(signatureCalldataStartPos, ECDSA_SIG_R_BS) - s := calldataload(signatureCalldataStartPos) - signatureCalldataStartPos := add(signatureCalldataStartPos, ECDSA_SIG_S_BS) - v := byte(0, calldataload(signatureCalldataStartPos)) // last byte of the signature memory array - } - return ecrecover(signedHash, v, r, s); - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/IRedstoneAdapter.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/IRedstoneAdapter.sol deleted file mode 100644 index 36f3575..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/IRedstoneAdapter.sol +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -/** - * @title Interface of RedStone adapter - * @author The Redstone Oracles team - */ -interface IRedstoneAdapter { - - /** - * @notice Updates values of all data feeds supported by the Adapter contract - * @dev This function requires an attached redstone payload to the transaction calldata. - * It also requires each data package to have exactly the same timestamp - * @param dataPackagesTimestamp Timestamp of each signed data package in the redstone payload - */ - function updateDataFeedsValues(uint256 dataPackagesTimestamp) external; - - - /** - * @notice Returns the latest properly reported value of the data feed - * @param dataFeedId The identifier of the requested data feed - * @return value The latest value of the given data feed - */ - function getValueForDataFeed(bytes32 dataFeedId) external view returns (uint256); - - /** - * @notice Returns the latest properly reported values for several data feeds - * @param requestedDataFeedIds The array of identifiers for the requested feeds - * @return values Values of the requested data feeds in the corresponding order - */ - function getValuesForDataFeeds(bytes32[] memory requestedDataFeedIds) external view returns (uint256[] memory); - - /** - * @notice Returns data timestamp from the latest update - * @dev It's virtual, because its implementation can sometimes be different - * (e.g. SinglePriceFeedAdapterWithClearing) - * @return lastDataTimestamp Timestamp of the latest reported data packages - */ - function getDataTimestampFromLatestUpdate() external view returns (uint256 lastDataTimestamp); - - /** - * @notice Returns block timestamp of the latest successful update - * @return blockTimestamp The block timestamp of the latest successful update - */ - function getBlockTimestampFromLatestUpdate() external view returns (uint256 blockTimestamp); - - - /** - * @notice Returns timestamps of the latest successful update - * @return dataTimestamp timestamp (usually in milliseconds) from the signed data packages - * @return blockTimestamp timestamp of the block when the update has happened - */ - function getTimestampsFromLatestUpdate() external view returns (uint128 dataTimestamp, uint128 blockTimestamp); - - /** - * @notice Returns identifiers of all data feeds supported by the Adapter contract - * @return An array of data feed identifiers - */ - function getDataFeedIds() external view returns (bytes32[] memory); - - /** - * @notice Returns the unique index of the given data feed - * @param dataFeedId The data feed identifier - * @return index The index of the data feed - */ - function getDataFeedIndex(bytes32 dataFeedId) external view returns (uint256); - - /** - * @notice Returns minimal required interval (usually in seconds) between subsequent updates - * @return interval The required interval between updates - */ - function getMinIntervalBetweenUpdates() external view returns (uint256); - - /** - * @notice Reverts if the proposed timestamp of data packages it too old or too new - * comparing to the block.timestamp. It also ensures that the proposed timestamp is newer - * Then the one from the previous update - * @param dataPackagesTimestamp The proposed timestamp (usually in milliseconds) - */ - function validateProposedDataPackagesTimestamp(uint256 dataPackagesTimestamp) external view; - - /** - * @notice Reverts if the updater is not authorised - * @dev This function should revert if msg.sender is not allowed to update data feed values - * @param updater The address of the proposed updater - */ - function requireAuthorisedUpdater(address updater) external view; -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/RedstoneAdapterBase.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/RedstoneAdapterBase.sol deleted file mode 100644 index 58e342c..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/core/RedstoneAdapterBase.sol +++ /dev/null @@ -1,377 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {RedstoneConsumerNumericBase, RedstoneDefaultsLib} from "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol"; -import {IRedstoneAdapter} from "./IRedstoneAdapter.sol"; - -/** - * @title Core logic of Redstone Adapter Contract - * @author The Redstone Oracles team - * @dev This contract is used to repeatedly push Redstone data to blockchain storage - * More details here: https://docs.redstone.finance/docs/smart-contract-devs/get-started/redstone-classic - * - * Key details about the contract: - * - Values for data feeds can be updated using the `updateDataFeedsValues` function - * - All data feeds must be updated within a single call, partial updates are not allowed - * - There is a configurable minimum interval between updates - * - Updaters can be restricted by overriding `requireAuthorisedUpdater` function - * - The contract is designed to force values validation, by default it prevents returning zero values - * - All data packages in redstone payload must have the same timestamp, - * equal to `dataPackagesTimestamp` argument of the `updateDataFeedsValues` function - * - Block timestamp abstraction - even though we call it blockTimestamp in many places, - * it's possible to have a custom logic here, e.g. use block number instead of a timestamp - */ -abstract contract RedstoneAdapterBase is RedstoneConsumerNumericBase, IRedstoneAdapter { - // We don't use storage variables to avoid potential problems with upgradable contracts - bytes32 internal constant LATEST_UPDATE_TIMESTAMPS_STORAGE_LOCATION = 0x3d01e4d77237ea0f771f1786da4d4ff757fcba6a92933aa53b1dcef2d6bd6fe2; // keccak256("RedStone.lastUpdateTimestamp"); - uint256 internal constant MIN_INTERVAL_BETWEEN_UPDATES = 0 seconds; - uint256 internal constant BITS_COUNT_IN_16_BYTES = 128; - uint256 internal constant MAX_NUMBER_FOR_128_BITS = 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff; - - error DataTimestampShouldBeNewerThanBefore( - uint256 receivedDataTimestampMilliseconds, - uint256 lastDataTimestampMilliseconds - ); - - error MinIntervalBetweenUpdatesHasNotPassedYet( - uint256 currentBlockTimestamp, - uint256 lastUpdateTimestamp, - uint256 minIntervalBetweenUpdates - ); - - error DataPackageTimestampMismatch(uint256 expectedDataTimestamp, uint256 dataPackageTimestamp); - - error DataFeedValueCannotBeZero(bytes32 dataFeedId); - - error DataFeedIdNotFound(bytes32 dataFeedId); - - error DataTimestampIsTooBig(uint256 dataTimestamp); - - error BlockTimestampIsTooBig(uint256 blockTimestamp); - - /** - * @notice Reverts if the updater is not authorised - * @dev This function should revert if msg.sender is not allowed to update data feed values - * @param updater The address of the proposed updater - */ - function requireAuthorisedUpdater(address updater) public view virtual { - // By default, anyone can update data feed values, but it can be overridden - } - - /** - * @notice Returns identifiers of all data feeds supported by the Adapter contract - * @dev this function must be implemented in derived contracts - * @return An array of data feed identifiers - */ - function getDataFeedIds() public view virtual returns (bytes32[] memory); - - /** - * @notice Returns the unique index of the given data feed - * @dev This function can (and should) be overriden to reduce gas - * costs of other functions - * @param dataFeedId The data feed identifier - * @return index The index of the data feed - */ - function getDataFeedIndex(bytes32 dataFeedId) public view virtual returns (uint256) { - bytes32[] memory dataFeedIds = getDataFeedIds(); - for (uint256 i = 0; i < dataFeedIds.length;) { - if (dataFeedIds[i] == dataFeedId) { - return i; - } - unchecked { i++; } // reduces gas costs - } - revert DataFeedIdNotFound(dataFeedId); - } - - /** - * @notice Updates values of all data feeds supported by the Adapter contract - * @dev This function requires an attached redstone payload to the transaction calldata. - * It also requires each data package to have exactly the same timestamp - * @param dataPackagesTimestamp Timestamp of each signed data package in the redstone payload - */ - function updateDataFeedsValues(uint256 dataPackagesTimestamp) public { - requireAuthorisedUpdater(msg.sender); - _assertMinIntervalBetweenUpdatesPassed(); - validateProposedDataPackagesTimestamp(dataPackagesTimestamp); - _saveTimestampsOfCurrentUpdate(dataPackagesTimestamp); - - bytes32[] memory dataFeedsIdsArray = getDataFeedIds(); - - // It will trigger timestamp validation for each data package - uint256[] memory oracleValues = getOracleNumericValuesFromTxMsg(dataFeedsIdsArray); - - _validateAndUpdateDataFeedsValues(dataFeedsIdsArray, oracleValues); - } - - /** - * @dev Note! This function is not called directly, it's called for each data package . - * in redstone payload and just verifies if each data package has the same timestamp - * as the one that was saved in the storage - * @param receivedTimestampMilliseconds Timestamp from a data package - */ - function validateTimestamp(uint256 receivedTimestampMilliseconds) public view virtual override { - // It means that we are in the special view context and we can skip validation of the - // timestamp. It can be useful for calling view functions, as they can not modify the contract - // state to pass the timestamp validation below - if (msg.sender == address(0)) { - return; - } - - uint256 expectedDataPackageTimestamp = getDataTimestampFromLatestUpdate(); - if (receivedTimestampMilliseconds != expectedDataPackageTimestamp) { - revert DataPackageTimestampMismatch( - expectedDataPackageTimestamp, - receivedTimestampMilliseconds - ); - } - } - - /** - * @dev This function should be implemented by the actual contract - * and should contain the logic of values validation and reporting. - * Usually, values reporting is based on saving them to the contract storage, - * e.g. in PriceFeedsAdapter, but some custom implementations (e.g. GMX keeper adapter - * or Mento Sorted Oracles adapter) may handle values updating in a different way - * @param dataFeedIdsArray Array of the data feeds identifiers (it will always be all data feed ids) - * @param values The reported values that should be validated and reported - */ - function _validateAndUpdateDataFeedsValues(bytes32[] memory dataFeedIdsArray, uint256[] memory values) internal virtual; - - /** - * @dev This function reverts if not enough time passed since the latest update - */ - function _assertMinIntervalBetweenUpdatesPassed() private view { - uint256 currentBlockTimestamp = getBlockTimestamp(); - uint256 blockTimestampFromLatestUpdate = getBlockTimestampFromLatestUpdate(); - uint256 minIntervalBetweenUpdates = getMinIntervalBetweenUpdates(); - if (currentBlockTimestamp < blockTimestampFromLatestUpdate + minIntervalBetweenUpdates) { - revert MinIntervalBetweenUpdatesHasNotPassedYet( - currentBlockTimestamp, - blockTimestampFromLatestUpdate, - minIntervalBetweenUpdates - ); - } - } - - /** - * @notice Returns minimal required interval (usually in seconds) between subsequent updates - * @dev You can override this function to change the required interval between udpates. - * Please do not set it to 0, as it may open many attack vectors - * @return interval The required interval between updates - */ - function getMinIntervalBetweenUpdates() public view virtual returns (uint256) { - return MIN_INTERVAL_BETWEEN_UPDATES; - } - - /** - * @notice Reverts if the proposed timestamp of data packages it too old or too new - * comparing to the block.timestamp. It also ensures that the proposed timestamp is newer - * Then the one from the previous update - * @param dataPackagesTimestamp The proposed timestamp (usually in milliseconds) - */ - function validateProposedDataPackagesTimestamp(uint256 dataPackagesTimestamp) public view { - _preventUpdateWithOlderDataPackages(dataPackagesTimestamp); - validateDataPackagesTimestampOnce(dataPackagesTimestamp); - } - - - /** - * @notice Reverts if the proposed timestamp of data packages it too old or too new - * comparing to the current block timestamp - * @param dataPackagesTimestamp The proposed timestamp (usually in milliseconds) - */ - function validateDataPackagesTimestampOnce(uint256 dataPackagesTimestamp) public view virtual { - uint256 receivedTimestampSeconds = dataPackagesTimestamp / 1000; - - (uint256 maxDataAheadSeconds, uint256 maxDataDelaySeconds) = getAllowedTimestampDiffsInSeconds(); - - uint256 blockTimestamp = getBlockTimestamp(); - - if (blockTimestamp < receivedTimestampSeconds) { - // if ((receivedTimestampSeconds - blockTimestamp) > maxDataAheadSeconds) { - // revert RedstoneDefaultsLib.TimestampFromTooLongFuture(receivedTimestampSeconds, blockTimestamp); - // } - } else if ((blockTimestamp - receivedTimestampSeconds) > maxDataDelaySeconds) { - revert RedstoneDefaultsLib.TimestampIsTooOld(receivedTimestampSeconds, blockTimestamp); - } - } - - /** - * @dev This function can be overriden, e.g. to use block.number instead of block.timestamp - * It can be useful in some L2 chains, as sometimes their different blocks can have the same timestamp - * @return timestamp Timestamp or Block number or any other number that can identify time in the context - * of the given blockchain - */ - function getBlockTimestamp() public view virtual returns (uint256) { - return block.timestamp; - } - - /** - * @dev Helpful function for getting values for timestamp validation - * @return maxDataAheadSeconds Max allowed number of seconds ahead of block.timrstamp - * @return maxDataDelaySeconds Max allowed number of seconds for data delay - */ - function getAllowedTimestampDiffsInSeconds() public view virtual returns (uint256 maxDataAheadSeconds, uint256 maxDataDelaySeconds) { - maxDataAheadSeconds = RedstoneDefaultsLib.DEFAULT_MAX_DATA_TIMESTAMP_AHEAD_SECONDS; - maxDataDelaySeconds = RedstoneDefaultsLib.DEFAULT_MAX_DATA_TIMESTAMP_DELAY_SECONDS; - } - - /** - * @dev Reverts if proposed data packages are not newer than the ones used previously - * @param dataPackagesTimestamp Timestamp od the data packages (usually in milliseconds) - */ - function _preventUpdateWithOlderDataPackages(uint256 dataPackagesTimestamp) internal view { - uint256 dataTimestampFromLatestUpdate = getDataTimestampFromLatestUpdate(); - - if (dataPackagesTimestamp <= dataTimestampFromLatestUpdate) { - revert DataTimestampShouldBeNewerThanBefore( - dataPackagesTimestamp, - dataTimestampFromLatestUpdate - ); - } - } - - /** - * @notice Returns data timestamp from the latest update - * @dev It's virtual, because its implementation can sometimes be different - * (e.g. SinglePriceFeedAdapterWithClearing) - * @return lastDataTimestamp Timestamp of the latest reported data packages - */ - function getDataTimestampFromLatestUpdate() public view virtual returns (uint256 lastDataTimestamp) { - (lastDataTimestamp, ) = getTimestampsFromLatestUpdate(); - } - - /** - * @notice Returns block timestamp of the latest successful update - * @return blockTimestamp The block timestamp of the latest successful update - */ - function getBlockTimestampFromLatestUpdate() public view returns (uint256 blockTimestamp) { - (, blockTimestamp) = getTimestampsFromLatestUpdate(); - } - - /** - * @dev Returns 2 timestamps packed into a single uint256 number - * @return packedTimestamps a single uin256 number with 2 timestamps - */ - function getPackedTimestampsFromLatestUpdate() public view returns (uint256 packedTimestamps) { - assembly { - packedTimestamps := sload(LATEST_UPDATE_TIMESTAMPS_STORAGE_LOCATION) - } - } - - /** - * @notice Returns timestamps of the latest successful update - * @return dataTimestamp timestamp (usually in milliseconds) from the signed data packages - * @return blockTimestamp timestamp of the block when the update has happened - */ - function getTimestampsFromLatestUpdate() public view virtual returns (uint128 dataTimestamp, uint128 blockTimestamp) { - return _unpackTimestamps(getPackedTimestampsFromLatestUpdate()); - } - - - /** - * @dev A helpful function to unpack 2 timestamps from one uin256 number - * @param packedTimestamps a single uin256 number - * @return dataTimestamp fetched from left 128 bits - * @return blockTimestamp fetched from right 128 bits - */ - function _unpackTimestamps(uint256 packedTimestamps) internal pure returns (uint128 dataTimestamp, uint128 blockTimestamp) { - dataTimestamp = uint128(packedTimestamps >> 128); // left 128 bits - blockTimestamp = uint128(packedTimestamps); // right 128 bits - } - - - /** - * @dev Logic of saving timestamps of the current update - * By default, it stores packed timestamps in one storage slot (32 bytes) - * to minimise gas costs - * But it can be overriden (e.g. in SinglePriceFeedAdapter) - * @param dataPackagesTimestamp . - */ - function _saveTimestampsOfCurrentUpdate(uint256 dataPackagesTimestamp) internal virtual { - uint256 blockTimestamp = getBlockTimestamp(); - - if (blockTimestamp > MAX_NUMBER_FOR_128_BITS) { - revert BlockTimestampIsTooBig(blockTimestamp); - } - - if (dataPackagesTimestamp > MAX_NUMBER_FOR_128_BITS) { - revert DataTimestampIsTooBig(dataPackagesTimestamp); - } - - assembly { - let timestamps := or(shl(BITS_COUNT_IN_16_BYTES, dataPackagesTimestamp), blockTimestamp) - sstore(LATEST_UPDATE_TIMESTAMPS_STORAGE_LOCATION, timestamps) - } - } - - /** - * @notice Returns the latest properly reported value of the data feed - * @param dataFeedId The identifier of the requested data feed - * @return value The latest value of the given data feed - */ - function getValueForDataFeed(bytes32 dataFeedId) public view returns (uint256) { - getDataFeedIndex(dataFeedId); // will revert if data feed id is not supported - - // "unsafe" here means "without validation" - uint256 valueForDataFeed = getValueForDataFeedUnsafe(dataFeedId); - - validateDataFeedValue(dataFeedId, valueForDataFeed); - return valueForDataFeed; - } - - /** - * @notice Returns the latest properly reported values for several data feeds - * @param dataFeedIds The array of identifiers for the requested feeds - * @return values Values of the requested data feeds in the corresponding order - */ - function getValuesForDataFeeds(bytes32[] memory dataFeedIds) public view returns (uint256[] memory) { - uint256[] memory values = getValuesForDataFeedsUnsafe(dataFeedIds); - for (uint256 i = 0; i < dataFeedIds.length;) { - bytes32 dataFeedId = dataFeedIds[i]; - getDataFeedIndex(dataFeedId); // will revert if data feed id is not supported - validateDataFeedValue(dataFeedId, values[i]); - unchecked { i++; } // reduces gas costs - } - return values; - } - - - /** - * @dev Reverts if proposed value for the proposed data feed id is invalid - * By default, it just checks if the value is not equal to 0, but it can be extended - * @param dataFeedId The data feed identifier - * @param valueForDataFeed Proposed value for the data feed - */ - function validateDataFeedValue(bytes32 dataFeedId, uint256 valueForDataFeed) public pure virtual { - if (valueForDataFeed == 0) { - revert DataFeedValueCannotBeZero(dataFeedId); - } - } - - /** - * @dev [HIGH RISK] Returns the latest value for a given data feed without validation - * Important! Using this function instead of `getValueForDataFeed` may cause - * significant risk for your smart contracts - * @param dataFeedId The data feed identifier - * @return dataFeedValue Unvalidated value of the latest successful update - */ - function getValueForDataFeedUnsafe(bytes32 dataFeedId) public view virtual returns (uint256); - - /** - * @notice [HIGH RISK] Returns the latest properly reported values for several data feeds without validation - * Important! Using this function instead of `getValuesForDataFeeds` may cause - * significant risk for your smart contracts - * @param requestedDataFeedIds The array of identifiers for the requested feeds - * @return values Unvalidated values of the requested data feeds in the corresponding order - */ - function getValuesForDataFeedsUnsafe(bytes32[] memory requestedDataFeedIds) public view virtual returns (uint256[] memory values) { - values = new uint256[](requestedDataFeedIds.length); - for (uint256 i = 0; i < requestedDataFeedIds.length;) { - values[i] = getValueForDataFeedUnsafe(requestedDataFeedIds[i]); - unchecked { i++; } // reduces gas costs - } - return values; - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/MergedPriceFeedAdapterCommon.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/MergedPriceFeedAdapterCommon.sol deleted file mode 100644 index e47fb7f..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/MergedPriceFeedAdapterCommon.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.14; -import {IRedstoneAdapter} from "../core/IRedstoneAdapter.sol"; -import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; - -abstract contract MergedPriceFeedAdapterCommon { - event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt); - - error CannotUpdateMoreThanOneDataFeed(); - - function getPriceFeedAdapter() public view virtual returns (IRedstoneAdapter) { - return IRedstoneAdapter(address(this)); - } - - function aggregator() public view virtual returns (address) { - return address(getPriceFeedAdapter()); - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol deleted file mode 100644 index 88e9ff4..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.14; - -import {Initializable} from "openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol"; -import {IRedstoneAdapter} from "../core/IRedstoneAdapter.sol"; -import {IPriceFeed} from "./interfaces/IPriceFeed.sol"; - -/** - * @title Main logic of the price feed contract - * @author The Redstone Oracles team - * @dev Implementation of common functions for the PriceFeed contract - * that queries data from the specified PriceFeedAdapter - * - * It can be used by projects that have already implemented with Chainlink-like - * price feeds and would like to minimise changes in their existing codebase. - * - * If you are flexible, it's much better (and cheaper in terms of gas) to query - * the PriceFeedAdapter contract directly - */ -abstract contract PriceFeedBase is IPriceFeed, Initializable { - uint256 internal constant INT256_MAX = uint256(type(int256).max); - - error UnsafeUintToIntConversion(uint256 value); - - /** - * @dev Helpful function for upgradable contracts - */ - function initialize() public virtual initializer { - // We don't have storage variables, but we keep this function - // Because it is used for contract setup in upgradable contracts - } - - /** - * @notice Returns data feed identifier for the PriceFeed contract - * @return dataFeedId The identifier of the data feed - */ - function getDataFeedId() public view virtual returns (bytes32); - - /** - * @notice Returns the address of the price feed adapter - * @return address The address of the price feed adapter - */ - function getPriceFeedAdapter() public view virtual returns (IRedstoneAdapter); - - - /** - * @notice Returns the number of decimals for the price feed - * @dev By default, RedStone uses 8 decimals for data feeds - * @return decimals The number of decimals in the price feed values - */ - function decimals() public virtual pure override returns (uint8) { - return 8; - } - - - /** - * @notice Description of the Price Feed - * @return description - */ - function description() public view virtual override returns (string memory) { - return "Redstone Price Feed"; - } - - /** - * @notice Version of the Price Feed - * @dev Currently it has no specific motivation and was added - * only to be compatible with the Chainlink interface - * @return version - */ - function version() public virtual pure override returns (uint256) { - return 1; - } - - - /** - * @notice Returns details of the latest successful update round - * @dev It uses few helpful functions to abstract logic of getting - * latest round id and value - * @return roundId The number of the latest round - * @return answer The latest reported value - * @return startedAt Block timestamp when the latest successful round started - * @return updatedAt Block timestamp of the latest successful round - * @return answeredInRound The number of the latest round - */ - function latestRoundData() - public - view - override - virtual - returns ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ) - { - roundId = latestRound(); - answer = latestAnswer(); - - uint256 blockTimestamp = getPriceFeedAdapter().getBlockTimestampFromLatestUpdate(); - - // These values are equal after chainlink’s OCR update - startedAt = blockTimestamp; - updatedAt = blockTimestamp; - - // We want to be compatible with Chainlink's interface - // And in our case the roundId is always equal to answeredInRound - answeredInRound = roundId; - } - - /** - * @notice Old Chainlink function for getting the latest successfully reported value - * @return latestAnswer The latest successfully reported value - */ - function latestAnswer() public virtual view returns (int256) { - bytes32 dataFeedId = getDataFeedId(); - - uint256 uintAnswer = getPriceFeedAdapter().getValueForDataFeed(dataFeedId); - - if (uintAnswer > INT256_MAX) { - revert UnsafeUintToIntConversion(uintAnswer); - } - - return int256(uintAnswer); - } - - /** - * @notice Old Chainlink function for getting the number of latest round - * @return latestRound The number of the latest update round - */ - function latestRound() public view virtual returns (uint80); -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol deleted file mode 100644 index 5974b7e..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/PriceFeedsAdapterBase.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {Initializable} from "openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol"; -import {RedstoneAdapterBase} from "../core/RedstoneAdapterBase.sol"; - -/** - * @title Common logic of the price feeds adapter contracts - * @author The Redstone Oracles team - */ -abstract contract PriceFeedsAdapterBase is RedstoneAdapterBase, Initializable { - - /** - * @dev Helpful function for upgradable contracts - */ - function initialize() public virtual initializer { - // We don't have storage variables, but we keep this function - // Because it is used for contract setup in upgradable contracts - } - - /** - * @dev This function is virtual and may contain additional logic in the derived contract - * E.g. it can check if the updating conditions are met (e.g. if at least one - * value is deviated enough) - * @param dataFeedIdsArray Array of all data feeds identifiers - * @param values The reported values that are validated and reported - */ - function _validateAndUpdateDataFeedsValues( - bytes32[] memory dataFeedIdsArray, - uint256[] memory values - ) internal virtual override { - for (uint256 i = 0; i < dataFeedIdsArray.length;) { - _validateAndUpdateDataFeedValue(dataFeedIdsArray[i], values[i]); - unchecked { i++; } // reduces gas costs - } - } - - /** - * @dev Helpful virtual function for handling value validation and saving in derived - * Price Feed Adapters contracts - * @param dataFeedId The data feed identifier - * @param dataFeedValue Proposed value for the data feed - */ - function _validateAndUpdateDataFeedValue(bytes32 dataFeedId, uint256 dataFeedValue) internal virtual; -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithRoundsPrimaryProd.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithRoundsPrimaryProd.sol deleted file mode 100644 index 4fb70f8..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithRoundsPrimaryProd.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {MergedPriceFeedAdapterWithRounds} from "../with-rounds/MergedPriceFeedAdapterWithRounds.sol"; - -abstract contract MergedPriceFeedAdapterWithRoundsPrimaryProd is MergedPriceFeedAdapterWithRounds { - function getUniqueSignersThreshold() public view virtual override returns (uint8) { - return 2; - } - - function getAuthorisedSignerIndex( - address signerAddress - ) public view virtual override returns (uint8) { - if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { return 0; } - else if (signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499) { return 1; } - else if (signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202) { return 2; } - else if (signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE) { return 3; } - else if (signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de) { return 4; } - else { - revert SignerNotAuthorised(signerAddress); - } - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithoutRoundsPrimaryProd.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithoutRoundsPrimaryProd.sol deleted file mode 100644 index 2e47bcd..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/MergedPriceFeedAdapterWithoutRoundsPrimaryProd.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {MergedPriceFeedAdapterWithoutRounds} from "../without-rounds/MergedPriceFeedAdapterWithoutRounds.sol"; - -abstract contract MergedPriceFeedAdapterWithoutRoundsPrimaryProd is MergedPriceFeedAdapterWithoutRounds { - function getUniqueSignersThreshold() public view virtual override returns (uint8) { - return 2; - } - - function getAuthorisedSignerIndex( - address signerAddress - ) public view virtual override returns (uint8) { - if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { return 0; } - else if (signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499) { return 1; } - else if (signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202) { return 2; } - else if (signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE) { return 3; } - else if (signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de) { return 4; } - else { - revert SignerNotAuthorised(signerAddress); - } - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithRoundsMainDemo.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithRoundsMainDemo.sol deleted file mode 100644 index d764902..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithRoundsMainDemo.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {PriceFeedsAdapterWithRounds} from "../with-rounds/PriceFeedsAdapterWithRounds.sol"; - -abstract contract PriceFeedsAdapterWithRoundsMainDemo is PriceFeedsAdapterWithRounds { - function getUniqueSignersThreshold() public view virtual override returns (uint8) { - return 1; - } - - function getAuthorisedSignerIndex( - address signerAddress - ) public view virtual override returns (uint8) { - if (signerAddress == 0x0C39486f770B26F5527BBBf942726537986Cd7eb) { - return 0; - } else { - revert SignerNotAuthorised(signerAddress); - } - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithoutRoundsMainDemo.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithoutRoundsMainDemo.sol deleted file mode 100644 index 1ca91f5..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/PriceFeedsAdapterWithoutRoundsMainDemo.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {PriceFeedsAdapterWithoutRounds} from "../without-rounds/PriceFeedsAdapterWithoutRounds.sol"; - -abstract contract PriceFeedsAdapterWithoutRoundsMainDemo is PriceFeedsAdapterWithoutRounds { - function getUniqueSignersThreshold() public view virtual override returns (uint8) { - return 1; - } - - function getAuthorisedSignerIndex(address signerAddress) - public - view - virtual - override - returns (uint8) - { - if (signerAddress == 0x0C39486f770B26F5527BBBf942726537986Cd7eb) { - return 0; - } else { - revert SignerNotAuthorised(signerAddress); - } - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapter.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapter.sol deleted file mode 100644 index 9de4eb6..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapter.sol +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {SinglePriceFeedAdapter} from "../without-rounds/SinglePriceFeedAdapter.sol"; - -contract VSTPriceFeedsAdapter is SinglePriceFeedAdapter { - uint256 internal constant BIT_MASK_TO_CHECK_CIRCUIT_BREAKER_FLAG = 0x0000000000000000000000000100000000000000000000000000000000000000; - - error InvalidSignersCount(uint256 signersCount); - error CircuitBreakerTripped(); - - function getSingleDataFeedId() public pure override returns (bytes32) { - return bytes32("VST"); - } - - function getUniqueSignersThreshold() public view virtual override returns (uint8) { - return 2; // 2 out of 3 - } - - function aggregateValues(uint256[] memory values) public pure override returns (uint256) { - if (values.length != 2) { - revert InvalidSignersCount(values.length); - } - - _checkCircuitBreaker(values[0]); - _checkCircuitBreaker(values[1]); - - return (values[0] + values[1]) / 2; - } - - function _checkCircuitBreaker(uint256 value) internal pure { - if (value & BIT_MASK_TO_CHECK_CIRCUIT_BREAKER_FLAG > 0) { - revert CircuitBreakerTripped(); - } - } - - function getAuthorisedSignerIndex( - address signerAddress - ) public view virtual override returns (uint8) { - if (signerAddress == 0xf7a873ff07E1d021ae808a28e6862f821148c789) { - return 0; - } else if (signerAddress == 0x827Cc644d3f33d55075354875A961aC8B9EB7Cc8) { - return 1; - } else if (signerAddress == 0x1C31b3eA83F48A6E550938d295893514A9e99Eca) { - return 2; - } else { - revert SignerNotAuthorised(signerAddress); - } - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapterWithoutRoundsMainDemo.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapterWithoutRoundsMainDemo.sol deleted file mode 100644 index 6bda523..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/data-services/VSTPriceFeedsAdapterWithoutRoundsMainDemo.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {SinglePriceFeedAdapter} from "../without-rounds/SinglePriceFeedAdapter.sol"; - -contract VSTPriceFeedsAdapterWithoutRoundsMainDemo is SinglePriceFeedAdapter { - function getSingleDataFeedId() public pure override returns (bytes32) { - return bytes32("VST"); - } - - function getUniqueSignersThreshold() public view virtual override returns (uint8) { - return 1; - } - - function getAuthorisedSignerIndex(address signerAddress) - public - view - virtual - override - returns (uint8) - { - if (signerAddress == 0x0C39486f770B26F5527BBBf942726537986Cd7eb) { - return 0; - } else { - revert SignerNotAuthorised(signerAddress); - } - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeed.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeed.sol deleted file mode 100644 index 5bc60af..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeed.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; -import {IPriceFeedLegacy} from "./IPriceFeedLegacy.sol"; - -/** - * @title Complete price feed interface - * @author The Redstone Oracles team - * @dev All required public functions that must be implemented - * by each Redstone PriceFeed contract - */ -interface IPriceFeed is IPriceFeedLegacy, AggregatorV3Interface { - /** - * @notice Returns data feed identifier for the PriceFeed contract - * @return dataFeedId The identifier of the data feed - */ - function getDataFeedId() external view returns (bytes32); -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeedLegacy.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeedLegacy.sol deleted file mode 100644 index e45c009..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/interfaces/IPriceFeedLegacy.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -/** - * @title Interface with the old Chainlink Price Feed functions - * @author The Redstone Oracles team - * @dev There are some projects (e.g. gmx-contracts) that still - * rely on some legacy functions - */ -interface IPriceFeedLegacy { - /** - * @notice Old Chainlink function for getting the number of latest round - * @return latestRound The number of the latest update round - */ - function latestRound() external view returns (uint80); - - - /** - * @notice Old Chainlink function for getting the latest successfully reported value - * @return latestAnswer The latest successfully reported value - */ - function latestAnswer() external view returns (int256); -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol deleted file mode 100644 index 1eeddcf..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {PriceFeedBase, PriceFeedWithRounds} from "./PriceFeedWithRounds.sol"; -import {PriceFeedsAdapterBase, PriceFeedsAdapterWithRounds} from "./PriceFeedsAdapterWithRounds.sol"; -import {IRedstoneAdapter} from "../../core/IRedstoneAdapter.sol"; -import {MergedPriceFeedAdapterCommon} from "../MergedPriceFeedAdapterCommon.sol"; -import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; - -abstract contract MergedPriceFeedAdapterWithRounds is - MergedPriceFeedAdapterCommon, - PriceFeedWithRounds, - PriceFeedsAdapterWithRounds -{ - function initialize() public override(PriceFeedBase, PriceFeedsAdapterBase) initializer { - // We don't have storage variables, but we keep this function - // Because it is used for contract setup in upgradable contracts - } - - function getPriceFeedAdapter() public view virtual override(MergedPriceFeedAdapterCommon, PriceFeedBase) returns (IRedstoneAdapter) { - return super.getPriceFeedAdapter(); - } - - function getDataFeedIds() public view virtual override returns (bytes32[] memory dataFeedIds) { - dataFeedIds = new bytes32[](1); - dataFeedIds[0] = getDataFeedId(); - } - - function getDataFeedIndex(bytes32 dataFeedId) public view virtual override returns (uint256) { - if (dataFeedId == getDataFeedId()) { - return 0; - } else { - revert DataFeedIdNotFound(dataFeedId); - } - } - - function _emitEventAfterSingleValueUpdate(uint256 newValue) internal virtual { - emit AnswerUpdated(SafeCast.toInt256(newValue), getLatestRoundId(), block.timestamp); - } - - function _validateAndUpdateDataFeedsValues( - bytes32[] memory dataFeedIdsArray, - uint256[] memory values - ) internal virtual override { - if (dataFeedIdsArray.length != 1 || values.length != 1) { - revert CannotUpdateMoreThanOneDataFeed(); - } - PriceFeedsAdapterWithRounds._validateAndUpdateDataFeedsValues(dataFeedIdsArray, values); - _emitEventAfterSingleValueUpdate(values[0]); - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedWithRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedWithRounds.sol deleted file mode 100644 index f757858..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedWithRounds.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {PriceFeedsAdapterWithRounds} from "./PriceFeedsAdapterWithRounds.sol"; -import {PriceFeedBase} from "../PriceFeedBase.sol"; - -/** - * @title Implementation of a price feed contract with rounds support - * @author The Redstone Oracles team - * @dev This contract is abstract. The actual contract instance - * must implement the following functions: - * - getDataFeedId - * - getPriceFeedAdapter - */ -abstract contract PriceFeedWithRounds is PriceFeedBase { - uint256 internal constant UINT80_MAX = uint256(type(uint80).max); - - error UnsafeUint256ToUint80Conversion(uint256 value); - - function getPriceFeedAdapterWithRounds() public view returns(PriceFeedsAdapterWithRounds) { - return PriceFeedsAdapterWithRounds(address(getPriceFeedAdapter())); - } - - /** - * @notice Old Chainlink function for getting the number of latest round - * @return latestRound The number of the latest successful round - */ - function latestRound() public view override returns (uint80) { - uint256 latestRoundUint256 = getPriceFeedAdapterWithRounds().getLatestRoundId(); - - if (latestRoundUint256 > UINT80_MAX) { - revert UnsafeUint256ToUint80Conversion(latestRoundUint256); - } - - return uint80(latestRoundUint256); - } - - /** - * @notice Returns details for the given round - * @param roundId Requested round identifier - */ - function getRoundData(uint80 requestedRoundId) public view override returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { - (uint256 dataFeedValue, uint128 roundDataTimestamp, uint128 roundBlockTimestamp) = getPriceFeedAdapterWithRounds().getRoundDataFromAdapter( - getDataFeedId(), - requestedRoundId - ); - roundId = requestedRoundId; - - if (dataFeedValue > INT256_MAX) { - revert UnsafeUintToIntConversion(dataFeedValue); - } - - answer = int256(dataFeedValue); - startedAt = roundDataTimestamp / 1000; // convert to seconds - updatedAt = roundBlockTimestamp; - - // We want to be compatible with Chainlink's interface - // And in our case the roundId is always equal to answeredInRound - answeredInRound = requestedRoundId; - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedsAdapterWithRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedsAdapterWithRounds.sol deleted file mode 100644 index d560432..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/PriceFeedsAdapterWithRounds.sol +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {PriceFeedsAdapterBase} from "../PriceFeedsAdapterBase.sol"; - -/** - * @title Price feeds adapter contract with rounds support - * @author The Redstone Oracles team - * @dev This contract is abstract. The actual contract instance - * must implement the following functions: - * - getDataFeedIds - * - getUniqueSignersThreshold - * - getAuthorisedSignerIndex - * - * We also recommend to override `getDataFeedIndex` function with hardcoded - * values, as it can significantly reduce gas usage - */ -abstract contract PriceFeedsAdapterWithRounds is PriceFeedsAdapterBase { - bytes32 constant VALUES_MAPPING_STORAGE_LOCATION = 0x4dd0c77efa6f6d590c97573d8c70b714546e7311202ff7c11c484cc841d91bfc; // keccak256("RedStone.oracleValuesMapping"); - bytes32 constant ROUND_TIMESTAMPS_MAPPING_STORAGE_LOCATION = 0x207e00944d909d1224f0c253d58489121d736649f8393199f55eecf4f0cf3eb0; // keccak256("RedStone.roundTimestampMapping"); - bytes32 constant LATEST_ROUND_ID_STORAGE_LOCATION = 0xc68d7f1ee07d8668991a8951e720010c9d44c2f11c06b5cac61fbc4083263938; // keccak256("RedStone.latestRoundId"); - - error RoundNotFound(uint256 roundId); - - /** - * @dev Saved new round data to the storage - * @param dataFeedIdsArray Array of all data feeds identifiers - * @param values The reported values that are validated and reported - */ - function _validateAndUpdateDataFeedsValues( - bytes32[] memory dataFeedIdsArray, - uint256[] memory values - ) internal virtual override { - _incrementLatestRoundId(); - _updatePackedTimestampsForLatestRound(); - - for (uint256 i = 0; i < dataFeedIdsArray.length;) { - _validateAndUpdateDataFeedValue(dataFeedIdsArray[i], values[i]); - unchecked { i++; } // reduces gas costs - } - } - - /** - * @dev Helpful virtual function for handling value validation and updating - * @param dataFeedId The data feed identifier - * @param dataFeedValue Proposed value for the data feed - */ - function _validateAndUpdateDataFeedValue(bytes32 dataFeedId, uint256 dataFeedValue) internal virtual override { - validateDataFeedValue(dataFeedId, dataFeedValue); - bytes32 locationInStorage = _getValueLocationInStorage(dataFeedId, getLatestRoundId()); - assembly { - sstore(locationInStorage, dataFeedValue) - } - } - - /** - * @dev [HIGH RISK] Returns the value for a given data feed from the latest round - * without validation. Important! Using this function instead of `getValueForDataFeed` - * may cause significant risk for your smart contracts - * @param dataFeedId The data feed identifier - * @return dataFeedValue Unvalidated value of the latest successful update - */ - function getValueForDataFeedUnsafe(bytes32 dataFeedId) public view override returns (uint256 dataFeedValue) { - return getValueForDataFeedAndRound(dataFeedId, getLatestRoundId()); - } - - /** - * @dev Returns value for the requested data feed from the given round - * @param dataFeedId The data feed identifier - * @param roundId The number of the requested round - * @return dataFeedValue value for the requested data feed from the given round - */ - function getValueForDataFeedAndRound(bytes32 dataFeedId, uint256 roundId) public view returns (uint256 dataFeedValue) { - bytes32 locationInStorage = _getValueLocationInStorage(dataFeedId, roundId); - assembly { - dataFeedValue := sload(locationInStorage) - } - } - - - /** - * @notice Returns data from the latest successful round - * @return latestRoundId - * @return latestRoundDataTimestamp - * @return latestRoundBlockTimestamp - */ - function getLatestRoundParams() public view returns ( uint256 latestRoundId, uint128 latestRoundDataTimestamp, uint128 latestRoundBlockTimestamp) { - latestRoundId = getLatestRoundId(); - uint256 packedRoundTimestamps = getPackedTimestampsForRound(latestRoundId); - (latestRoundDataTimestamp, latestRoundBlockTimestamp) = _unpackTimestamps( - packedRoundTimestamps - ); - } - - - /** - * @notice Returns details for the given round and data feed - * @param dataFeedId Requested data feed - * @param roundId Requested round identifier - * @return dataFeedValue - * @return roundDataTimestamp - * @return roundBlockTimestamp - */ - function getRoundDataFromAdapter(bytes32 dataFeedId, uint256 roundId) public view returns (uint256 dataFeedValue, uint128 roundDataTimestamp, uint128 roundBlockTimestamp) { - if (roundId > getLatestRoundId() || roundId == 0) { - revert RoundNotFound(roundId); - } - - dataFeedValue = getValueForDataFeedAndRound(dataFeedId, roundId); - validateDataFeedValue(dataFeedId, dataFeedValue); - uint256 packedRoundTimestamps = getPackedTimestampsForRound(roundId); - (roundDataTimestamp, roundBlockTimestamp) = _unpackTimestamps(packedRoundTimestamps); - } - - - /** - * @dev Helpful function for getting storage location for requested value - * @param dataFeedId Requested data feed identifier - * @param roundId Requested round number - * @return locationInStorage - */ - function _getValueLocationInStorage(bytes32 dataFeedId, uint256 roundId) private pure returns (bytes32) { - return keccak256(abi.encode(dataFeedId, roundId, VALUES_MAPPING_STORAGE_LOCATION)); - } - - - /** - * @dev Helpful function for getting storage location for round timestamps - * @param roundId Requested round number - * @return locationInStorage - */ - function _getRoundTimestampsLocationInStorage(uint256 roundId) private pure returns (bytes32) { - return keccak256(abi.encode(roundId, ROUND_TIMESTAMPS_MAPPING_STORAGE_LOCATION)); - } - - - /** - * @notice Returns latest successful round number - * @return latestRoundId - */ - function getLatestRoundId() public view returns (uint256 latestRoundId) { - assembly { - latestRoundId := sload(LATEST_ROUND_ID_STORAGE_LOCATION) - } - } - - /** - * @dev Helpful function for incrementing the latest round number by 1 in - * the contract storage - */ - function _incrementLatestRoundId() private { - uint256 latestRoundId = getLatestRoundId(); - assembly { - sstore(LATEST_ROUND_ID_STORAGE_LOCATION, add(latestRoundId, 1)) - } - } - - /** - * @notice Returns timestamps related to the given round packed into one number - * @param roundId Requested round number - * @return roundTimestamps - */ - function getPackedTimestampsForRound(uint256 roundId) public view returns (uint256 roundTimestamps) { - bytes32 locationInStorage = _getRoundTimestampsLocationInStorage(roundId); - assembly { - roundTimestamps := sload(locationInStorage) - } - } - - - /** - * @dev Saves packed timestamps (data and block.timestamp) in the contract storage - */ - function _updatePackedTimestampsForLatestRound() private { - uint256 packedTimestamps = getPackedTimestampsFromLatestUpdate(); - uint256 latestRoundId = getLatestRoundId(); - bytes32 locationInStorage = _getRoundTimestampsLocationInStorage(latestRoundId); - assembly { - sstore(locationInStorage, packedTimestamps) - } - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/MergedPriceFeedAdapterWithoutRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/MergedPriceFeedAdapterWithoutRounds.sol deleted file mode 100644 index 1ee201a..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/MergedPriceFeedAdapterWithoutRounds.sol +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {PriceFeedsAdapterBase, PriceFeedsAdapterWithoutRounds} from "./PriceFeedsAdapterWithoutRounds.sol"; -import {PriceFeedBase, PriceFeedWithoutRounds} from "./PriceFeedWithoutRounds.sol"; -import {IRedstoneAdapter} from "../../core/IRedstoneAdapter.sol"; -import {MergedPriceFeedAdapterCommon} from "../MergedPriceFeedAdapterCommon.sol"; -import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; - -abstract contract MergedPriceFeedAdapterWithoutRounds is - MergedPriceFeedAdapterCommon, - PriceFeedsAdapterWithoutRounds, - PriceFeedWithoutRounds -{ - - function initialize() public override(PriceFeedBase, PriceFeedsAdapterBase) initializer { - // We don't have storage variables, but we keep this function - // Because it is used for contract setup in upgradable contracts - } - - function getPriceFeedAdapter() public view virtual override(MergedPriceFeedAdapterCommon, PriceFeedBase) returns (IRedstoneAdapter) { - return super.getPriceFeedAdapter(); - } - - function getDataFeedIds() public view virtual override returns (bytes32[] memory dataFeedIds) { - dataFeedIds = new bytes32[](1); - dataFeedIds[0] = getDataFeedId(); - } - - function getDataFeedIndex(bytes32 dataFeedId) public view virtual override returns (uint256) { - if (dataFeedId == getDataFeedId()) { - return 0; - } else { - revert DataFeedIdNotFound(dataFeedId); - } - } - - function _emitEventAfterSingleValueUpdate(uint256 newValue) internal virtual { - emit AnswerUpdated(SafeCast.toInt256(newValue), latestRound(), block.timestamp); - } - - function _validateAndUpdateDataFeedsValues( - bytes32[] memory dataFeedIdsArray, - uint256[] memory values - ) internal virtual override { - if (dataFeedIdsArray.length != 1 || values.length != 1) { - revert CannotUpdateMoreThanOneDataFeed(); - } - _validateAndUpdateDataFeedValue(dataFeedIdsArray[0], values[0]); - _emitEventAfterSingleValueUpdate(values[0]); - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedWithoutRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedWithoutRounds.sol deleted file mode 100644 index 3291f8b..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedWithoutRounds.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.14; - -import {PriceFeedBase} from "../PriceFeedBase.sol"; - -/** - * @title Implementation of a price feed contract without rounds support - * @author The Redstone Oracles team - * @dev This contract is abstract. The actual contract instance - * must implement the following functions: - * - getDataFeedId - * - getPriceFeedAdapter - */ -abstract contract PriceFeedWithoutRounds is PriceFeedBase { - uint80 constant DEFAULT_ROUND = 1; - - error GetRoundDataCanBeOnlyCalledWithLatestRound(uint80 requestedRoundId); - - /** - * @dev We always return 1, since we do not support rounds in this contract - */ - function latestRound() public pure override returns (uint80) { - return DEFAULT_ROUND; - } - - /** - * @dev There are possible use cases that some contracts don't need values from old rounds - * but still rely on `getRoundData` or `latestRounud` functions - */ - function getRoundData(uint80 requestedRoundId) public view override returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { - if (requestedRoundId != latestRound()) { - revert GetRoundDataCanBeOnlyCalledWithLatestRound(requestedRoundId); - } - return latestRoundData(); - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedsAdapterWithoutRounds.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedsAdapterWithoutRounds.sol deleted file mode 100644 index 335a8e8..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/PriceFeedsAdapterWithoutRounds.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {PriceFeedsAdapterBase} from "../PriceFeedsAdapterBase.sol"; - -/** - * @title Implementation of a price feeds adapter without rounds support - * @author The Redstone Oracles team - * @dev This contract is abstract, the following functions should be - * implemented in the actual contract before deployment: - * - getDataFeedIds - * - getUniqueSignersThreshold - * - getAuthorisedSignerIndex - * - * We also recommend to override `getDataFeedIndex` function with hardcoded - * values, as it can significantly reduce gas usage - */ -abstract contract PriceFeedsAdapterWithoutRounds is PriceFeedsAdapterBase { - bytes32 constant VALUES_MAPPING_STORAGE_LOCATION = 0x4dd0c77efa6f6d590c97573d8c70b714546e7311202ff7c11c484cc841d91bfc; // keccak256("RedStone.oracleValuesMapping"); - - /** - * @dev Helpful virtual function for handling value validation and saving - * @param dataFeedId The data feed identifier - * @param dataFeedValue Proposed value for the data feed - */ - function _validateAndUpdateDataFeedValue(bytes32 dataFeedId, uint256 dataFeedValue) internal override virtual { - validateDataFeedValue(dataFeedId, dataFeedValue); - bytes32 locationInStorage = _getValueLocationInStorage(dataFeedId); - assembly { - sstore(locationInStorage, dataFeedValue) - } - } - - /** - * @dev [HIGH RISK] Returns the latest value for a given data feed without validation - * Important! Using this function instead of `getValueForDataFeed` may cause - * significant risk for your smart contracts - * @param dataFeedId The data feed identifier - * @return dataFeedValue Unvalidated value of the latest successful update - */ - function getValueForDataFeedUnsafe(bytes32 dataFeedId) public view virtual override returns (uint256 dataFeedValue) { - bytes32 locationInStorage = _getValueLocationInStorage(dataFeedId); - assembly { - dataFeedValue := sload(locationInStorage) - } - } - - /** - * @dev Helpful function for getting storage location for the requested data feed - * @param dataFeedId Requested data feed identifier - * @return locationInStorage - */ - function _getValueLocationInStorage(bytes32 dataFeedId) private pure returns (bytes32) { - return keccak256(abi.encode(dataFeedId, VALUES_MAPPING_STORAGE_LOCATION)); - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapter.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapter.sol deleted file mode 100644 index 52dd184..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapter.sol +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {PriceFeedsAdapterBase} from "../PriceFeedsAdapterBase.sol"; - -/** - * @title Price feed adapter for one specific data feed without rounds support - * @author The Redstone Oracles team - * @dev This version works only with a single data feed. It's abstract and - * the following functions should be implemented in the actual contract - * before deployment: - * - getSingleDataFeedId - * - getUniqueSignersThreshold - * - getAuthorisedSignerIndex - * - * This contract stores the value along with timestamps in a single storage slot - * 32 bytes = 6 bytes (Data timestamp ) + 6 bytes (Block timestamp) + 20 bytes (Value) - */ -abstract contract SinglePriceFeedAdapter is PriceFeedsAdapterBase { - - bytes32 internal constant DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION = 0x632f4a585e47073d66129e9ebce395c9b39d8a1fc5b15d4d7df2e462fb1fccfa; // keccak256("RedStone.singlePriceFeedAdapter"); - uint256 internal constant MAX_VALUE_WITH_20_BYTES = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff; - uint256 internal constant BIT_MASK_TO_CLEAR_LAST_20_BYTES = 0xffffffffffffffffffffffff0000000000000000000000000000000000000000; - uint256 internal constant MAX_NUMBER_FOR_48_BITS = 0x0000000000000000000000000000000000000000000000000000ffffffffffff; - - error DataFeedValueTooBig(uint256 valueForDataFeed); - - /** - * @notice Returns the only data feed identifer supported by the adapter - * @dev This function should be overriden in the derived contracts, - * but `getDataFeedIds` and `getDataFeedIndex` should not (and can not) - * @return dataFeedId The only data feed identifer supported by the adapter - */ - function getSingleDataFeedId() public view virtual returns (bytes32); - - /** - * @notice Returns identifiers of all data feeds supported by the Adapter contract - * In this case - an array with only one element - * @return dataFeedIds - */ - function getDataFeedIds() public view virtual override returns (bytes32[] memory dataFeedIds) { - dataFeedIds = new bytes32[](1); - dataFeedIds[0] = getSingleDataFeedId(); - } - - /** - * @dev Returns 0 if dataFeedId is the one, otherwise reverts - * @param dataFeedId The identifier of the requested data feed - */ - function getDataFeedIndex(bytes32 dataFeedId) public virtual view override returns(uint256) { - if (dataFeedId == getSingleDataFeedId()) { - return 0; - } - revert DataFeedIdNotFound(dataFeedId); - } - - /** - * @dev Reverts if proposed value for the proposed data feed id is invalid - * By default, it checks if the value is not equal to 0 and if it fits to 20 bytes - * Because other 12 bytes are used for storing the packed timestamps - * @param dataFeedId The data feed identifier - * @param valueForDataFeed Proposed value for the data feed - */ - function validateDataFeedValue(bytes32 dataFeedId, uint256 valueForDataFeed) public pure virtual override { - if (valueForDataFeed == 0) { - revert DataFeedValueCannotBeZero(dataFeedId); - } - if (valueForDataFeed > MAX_VALUE_WITH_20_BYTES) { - revert DataFeedValueTooBig(valueForDataFeed); - } - } - - /** - * @dev [HIGH RISK] Returns the latest value for a given data feed without validation - * Important! Using this function instead of `getValueForDataFeed` may cause - * significant risk for your smart contracts - * @return dataFeedValue Unvalidated value of the latest successful update - */ - function getValueForDataFeedUnsafe(bytes32) public view virtual override returns (uint256 dataFeedValue) { - uint160 dataFeedValueCompressed; - assembly { - dataFeedValueCompressed := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) - } - return uint256(dataFeedValueCompressed); - } - - /** - * @notice Returns timestamps of the latest successful update - * @dev Timestamps here use only 6 bytes each and are packed together with the value - * @return dataTimestamp timestamp (usually in milliseconds) from the signed data packages - * @return blockTimestamp timestamp of the block when the update has happened - */ - function getTimestampsFromLatestUpdate() public view virtual override returns (uint128 dataTimestamp, uint128 blockTimestamp) { - uint256 latestUpdateDetails; - assembly { - latestUpdateDetails := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) - } - dataTimestamp = uint128(latestUpdateDetails >> 208); // first 48 bits - blockTimestamp = uint128((latestUpdateDetails << 48) >> 208); // next 48 bits - } - - /** - * @dev Validates and saves the value in the contract storage - * It uses only 20 right bytes of the corresponding storage slot - * @param dataFeedId The data feed identifier - * @param dataFeedValue Proposed value for the data feed - */ - function _validateAndUpdateDataFeedValue(bytes32 dataFeedId, uint256 dataFeedValue) internal virtual override { - validateDataFeedValue(dataFeedId, dataFeedValue); - assembly { - let curValueFromStorage := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) - curValueFromStorage := and(curValueFromStorage, BIT_MASK_TO_CLEAR_LAST_20_BYTES) // clear dataFeedValue bits - curValueFromStorage := or(curValueFromStorage, dataFeedValue) - sstore(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION, curValueFromStorage) - } - } - - /** - * @dev Helpful function that packs and saves timestamps in the 12 left bytes of the - * storage slot reserved for storing details about the latest update - * @param dataPackagesTimestamp Timestamp from the signed data packages, - * extracted from the RedStone payload in calldata - */ - function _saveTimestampsOfCurrentUpdate(uint256 dataPackagesTimestamp) internal virtual override { - uint256 blockTimestamp = getBlockTimestamp(); - - if (dataPackagesTimestamp > MAX_NUMBER_FOR_48_BITS) { - revert DataTimestampIsTooBig(dataPackagesTimestamp); - } - - if (blockTimestamp > MAX_NUMBER_FOR_48_BITS) { - revert BlockTimestampIsTooBig(blockTimestamp); - } - - uint256 timestampsPacked = dataPackagesTimestamp << 208; // 48 first bits for dataPackagesTimestamp - timestampsPacked |= (blockTimestamp << 160); // 48 next bits for blockTimestamp - assembly { - let latestUpdateDetails := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) - latestUpdateDetails := and(latestUpdateDetails, MAX_VALUE_WITH_20_BYTES) // clear timestamp bits - sstore(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION, or(latestUpdateDetails, timestampsPacked)) - } - } -} diff --git a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapterWithClearing.sol b/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapterWithClearing.sol deleted file mode 100644 index 2c88441..0000000 --- a/lib/RedStoneBaseContracts/redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/without-rounds/SinglePriceFeedAdapterWithClearing.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.14; - -import {SinglePriceFeedAdapter} from "./SinglePriceFeedAdapter.sol"; - -/** - * @title [HIGH RISK] Price feed adapter for one specific data feed without - * rounds support, with storage clearing feature - * @author The Redstone Oracles team - * @dev This contract has a significant security risk, as it allows to - * update oracle data with older timestamps then the previous one. It can - * open many opportunities for attackers to manipulate the values and use it - * for arbitrage. Use it only if you know what you are doing very well - */ -abstract contract SinglePriceFeedAdapterWithClearing is SinglePriceFeedAdapter { - - bytes32 internal constant TEMP_DATA_TIMESTAMP_STORAGE_LOCATION = 0x9ba2e81f7980c774323961547312ae2319fc1970bb8ec60c86c869e9a1c1c0d2; // keccak256("RedStone.tempDataTimestampStorageLocation"); - uint256 internal constant MAX_VALUE_WITH_26_BYTES = 0x000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff; - uint256 internal constant BIT_MASK_TO_CLEAR_LAST_26_BYTES = 0xffffffffffff0000000000000000000000000000000000000000000000000000; - - function validateDataFeedValue(bytes32 dataFeedId, uint256 valueForDataFeed) public pure virtual override { - if (valueForDataFeed == 0) { - revert DataFeedValueCannotBeZero(dataFeedId); - } - if (valueForDataFeed > MAX_VALUE_WITH_26_BYTES) { - revert DataFeedValueTooBig(valueForDataFeed); - } - } - - function getValueForDataFeedUnsafe(bytes32) public view override virtual returns (uint256 dataFeedValue) { - uint208 dataFeedValueCompressed; - assembly { - dataFeedValueCompressed := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) - } - return uint256(dataFeedValueCompressed); - } - - function getTimestampsFromLatestUpdate() public view override virtual returns (uint128 dataTimestamp, uint128 blockTimestamp) { - uint256 latestUpdateDetails; - assembly { - latestUpdateDetails := sload(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION) - } - blockTimestamp = uint128(latestUpdateDetails >> 208); // first 48 bits - dataTimestamp = blockTimestamp * 1000; // It's a hack, because we don't store dataTimestamp in storage in this version of adapter - } - - function getDataTimestampFromLatestUpdate() public view virtual override returns (uint256 lastDataTimestamp) { - assembly { - lastDataTimestamp := sload(TEMP_DATA_TIMESTAMP_STORAGE_LOCATION) - } - } - - function _validateAndUpdateDataFeedValue(bytes32 dataFeedId, uint256 dataFeedValue) virtual internal override { - validateDataFeedValue(dataFeedId, dataFeedValue); - uint256 blockTimestampCompressedAndShifted = getBlockTimestamp() << 208; // Move value to the first 48 bits - assembly { - // Save timestamp and data feed value - let timestampAndValue := or(blockTimestampCompressedAndShifted, dataFeedValue) - sstore(DATA_FROM_LATEST_UPDATE_STORAGE_LOCATION, timestampAndValue) - - // Clear temp data timestamp, it refunds 19.9k gas - sstore(TEMP_DATA_TIMESTAMP_STORAGE_LOCATION, 0) - } - } - - function _saveTimestampsOfCurrentUpdate(uint256 dataPackagesTimestamp) virtual internal override { - assembly { - sstore(TEMP_DATA_TIMESTAMP_STORAGE_LOCATION, dataPackagesTimestamp) - } - } -} diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable deleted file mode 160000 index 5ffedf4..0000000 --- a/lib/openzeppelin-contracts-upgradeable +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5ffedf46f77bed03e714913df1beff2d71611f0a From 9a5b832fc087c5a59f8a287b9febd5823df71e74 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 11:44:05 +0100 Subject: [PATCH 19/31] forge install: redstone-contracts --- .gitmodules | 3 +++ lib/redstone-contracts | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/redstone-contracts diff --git a/.gitmodules b/.gitmodules index e80ffd8..124ef1f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/openzeppelin/openzeppelin-contracts +[submodule "lib/redstone-contracts"] + path = lib/redstone-contracts + url = https://github.com/UMAprotocol/redstone-contracts diff --git a/lib/redstone-contracts b/lib/redstone-contracts new file mode 160000 index 0000000..755450a --- /dev/null +++ b/lib/redstone-contracts @@ -0,0 +1 @@ +Subproject commit 755450ae078288bad7338f0329405f9e531688a6 From 282fbba2e4568161937c4354d99b13a14ae0c129 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 11:46:49 +0100 Subject: [PATCH 20/31] feat: remappings Signed-off-by: Pablo Maldonado --- remappings.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/remappings.txt b/remappings.txt index 374f0be..8d4be09 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,6 +1,3 @@ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ -redstone-oracles-monorepo/=lib/RedStoneBaseContracts/redstone-oracles-monorepo -@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ -openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ -@redstone-finance/=lib/RedStoneBaseContracts/@redstone-finance/ +redstone-oracles-monorepo/=lib/redstone-contracts/src/RedStoneBaseContracts/redstone-oracles-monorepo From da0ab20409e95293e01eb8a35af4cd7d17489a0e Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 11:56:57 +0100 Subject: [PATCH 21/31] fix: redstone tests Signed-off-by: Pablo Maldonado --- src/oracles/RedstonePriceFeedWithRounds.sol | 24 +++++++------------ .../RedStoneAsChainlinkSourceAdapter.sol | 20 +++++++++------- test/unit/CoinbaseSourceAdapter.sol | 4 ++-- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/oracles/RedstonePriceFeedWithRounds.sol b/src/oracles/RedstonePriceFeedWithRounds.sol index 5927f9b..e40fcad 100644 --- a/src/oracles/RedstonePriceFeedWithRounds.sol +++ b/src/oracles/RedstonePriceFeedWithRounds.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; -import "redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol"; +import + "redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol"; + contract RedstonePriceFeedWithRounds is MergedPriceFeedAdapterWithRounds { bytes32 private immutable dataFeedId; @@ -21,26 +23,16 @@ contract RedstonePriceFeedWithRounds is MergedPriceFeedAdapterWithRounds { return 3; } - function getAuthorisedSignerIndex( - address signerAddress - ) public view virtual override returns (uint8) { + function getAuthorisedSignerIndex(address signerAddress) public view virtual override returns (uint8) { if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { return 0; - } else if ( - signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499 - ) { + } else if (signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499) { return 1; - } else if ( - signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202 - ) { + } else if (signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202) { return 2; - } else if ( - signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE - ) { + } else if (signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE) { return 3; - } else if ( - signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de - ) { + } else if (signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de) { return 4; } else { revert SignerNotAuthorised(signerAddress); diff --git a/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol b/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol index e6b51b5..419e285 100644 --- a/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol +++ b/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol @@ -16,7 +16,9 @@ import {RedstonePriceFeedWithRounds} from "../../../src/oracles/RedstonePriceFee contract TestedSourceAdapter is ChainlinkSourceAdapter { constructor(IAggregatorV3Source source) ChainlinkSourceAdapter(source) {} - function internalLatestData() public view override returns (int256, uint256) {} + function internalLatestData() public view override returns (int256, uint256, uint256) {} + + function internalDataAtRound(uint256 roundId) public view override returns (int256, uint256) {} function canUnlock(address caller, uint256 cachedLatestTimestamp) public view virtual override returns (bool) {} @@ -66,7 +68,7 @@ contract RedstoneAsChainlinkSourceAdapterTest is CommonTest { uint256 targetTime = block.timestamp - 1 hours; (uint80 latestRound,,,,) = redstone.latestRoundData(); - (int256 lookBackPrice, uint256 lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 10); + (int256 lookBackPrice, uint256 lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 10); (, int256 answer,, uint256 updatedAt,) = redstone.getRoundData(latestRound); assertTrue(updatedAt <= targetTime); assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); @@ -75,7 +77,7 @@ contract RedstoneAsChainlinkSourceAdapterTest is CommonTest { // Next, try looking back 2 hours. by looking on-chain we can see only one update was applied. Therefore we // should get the values from latestRound -1 (one update applied relative to the "latest" round). targetTime = block.timestamp - 2 hours; - (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 10); + (lookBackPrice, lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 10); (, answer,, updatedAt,) = redstone.getRoundData(latestRound - 1); assertTrue(updatedAt <= targetTime); assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); @@ -84,7 +86,7 @@ contract RedstoneAsChainlinkSourceAdapterTest is CommonTest { // Next, try land at 2 rounds ago. Again, by looking on-chain, we can see this is ~2 23 mins before the current // fork timestamp. We should be able to show the value is the oldest value within this interval. targetTime = block.timestamp - 2 hours - 23 minutes; - (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 10); + (lookBackPrice, lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 10); (, answer,, updatedAt,) = redstone.getRoundData(latestRound - 2); assertTrue(updatedAt <= targetTime); assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); @@ -93,7 +95,7 @@ contract RedstoneAsChainlinkSourceAdapterTest is CommonTest { // Now, try 3 hours old. On-chain there were 5 updates in this interval. we should be able to show the value is // the oldest value within this interval. targetTime = block.timestamp - 3 hours; - (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 10); + (lookBackPrice, lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 10); (, answer,, updatedAt,) = redstone.getRoundData(latestRound - 5); assertTrue(updatedAt <= targetTime); assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); @@ -106,7 +108,7 @@ contract RedstoneAsChainlinkSourceAdapterTest is CommonTest { // 2. If we try look back longer than this we should get the price from round 2, no matter how far we look back, // if we bound the maximum lookback to 2 rounds. uint256 targetTime = block.timestamp - 2 hours - 23 minutes; - (int256 lookBackPrice, uint256 lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 2); + (int256 lookBackPrice, uint256 lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 2); (uint80 latestRound,,,,) = redstone.latestRoundData(); (, int256 answer,, uint256 updatedAt,) = redstone.getRoundData(latestRound - 2); assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); @@ -114,11 +116,11 @@ contract RedstoneAsChainlinkSourceAdapterTest is CommonTest { // Now, lookback longer than 2 hours. should get the same value as before. targetTime = block.timestamp - 3 hours; - (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 2); + (lookBackPrice, lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 2); assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); assertTrue(updatedAt == lookBackTimestamp); targetTime = block.timestamp - 10 hours; - (lookBackPrice, lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 2); + (lookBackPrice, lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 2); assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); assertTrue(updatedAt == lookBackTimestamp); } @@ -128,7 +130,7 @@ contract RedstoneAsChainlinkSourceAdapterTest is CommonTest { (, int256 answer,, uint256 updatedAt,) = redstone.latestRoundData(); - (int256 lookBackPrice, uint256 lookBackTimestamp) = sourceAdapter.tryLatestDataAt(targetTime, 0); + (int256 lookBackPrice, uint256 lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 0); assertEq(lookBackPrice / 10 ** 10, answer); assertEq(lookBackTimestamp, updatedAt); } diff --git a/test/unit/CoinbaseSourceAdapter.sol b/test/unit/CoinbaseSourceAdapter.sol index df7cf27..120570e 100644 --- a/test/unit/CoinbaseSourceAdapter.sol +++ b/test/unit/CoinbaseSourceAdapter.sol @@ -33,10 +33,10 @@ contract CoinbaseSourceAdapterTest is CommonTest { string public ticker = "ETH"; uint256 public price = 3000e6; - function pushPrice(string memory ticker, uint256 price, uint256 timestamp) public { + function pushPrice(string memory ticker, uint256 priceToPush, uint256 timestamp) public { string memory kind = "price"; - bytes memory encodedData = abi.encode(kind, timestamp, ticker, price); + bytes memory encodedData = abi.encode(kind, timestamp, ticker, priceToPush); bytes32 hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", keccak256(encodedData))); From 08875caee17d8920669a8ddb0351be6ce3103907 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 12:07:22 +0100 Subject: [PATCH 22/31] fix: redstone test round id Signed-off-by: Pablo Maldonado --- test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol b/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol index 419e285..c3a78b3 100644 --- a/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol +++ b/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol @@ -68,8 +68,9 @@ contract RedstoneAsChainlinkSourceAdapterTest is CommonTest { uint256 targetTime = block.timestamp - 1 hours; (uint80 latestRound,,,,) = redstone.latestRoundData(); - (int256 lookBackPrice, uint256 lookBackTimestamp,) = sourceAdapter.tryLatestDataAt(targetTime, 10); + (int256 lookBackPrice, uint256 lookBackTimestamp, uint256 roundId) = sourceAdapter.tryLatestDataAt(targetTime, 10); (, int256 answer,, uint256 updatedAt,) = redstone.getRoundData(latestRound); + assertTrue(roundId == latestRound); assertTrue(updatedAt <= targetTime); assertTrue(scaleRedstoneTo18(answer) == lookBackPrice); assertTrue(updatedAt == lookBackTimestamp); From 08cd2d5ffbb430c20f401d8419ce86db4488c73a Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 14:27:14 +0100 Subject: [PATCH 23/31] feat: update redstone lib Signed-off-by: Pablo Maldonado --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitmodules b/.gitmodules index 124ef1f..3f3212d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "lib/redstone-contracts"] path = lib/redstone-contracts url = https://github.com/UMAprotocol/redstone-contracts +[submodule "lib/redstone-contracts.git"] + path = lib/redstone-contracts.git + url = https://github.com/UMAprotocol/redstone-contracts.git From 5cf167be6e5f6760af593b409312cd8e2b4d1761 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 14:28:49 +0100 Subject: [PATCH 24/31] feat: update redstone lib Signed-off-by: Pablo Maldonado --- lib/redstone-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/redstone-contracts b/lib/redstone-contracts index 755450a..0ccb286 160000 --- a/lib/redstone-contracts +++ b/lib/redstone-contracts @@ -1 +1 @@ -Subproject commit 755450ae078288bad7338f0329405f9e531688a6 +Subproject commit 0ccb286fc5e3e2ccc989d756eb63c73fe2416a32 From 580879510874f9bd001b4c6997c1bfb1035518a6 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 14:37:46 +0100 Subject: [PATCH 25/31] feat: update redstone lib Signed-off-by: Pablo Maldonado --- lib/redstone-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/redstone-contracts b/lib/redstone-contracts index 0ccb286..628c301 160000 --- a/lib/redstone-contracts +++ b/lib/redstone-contracts @@ -1 +1 @@ -Subproject commit 0ccb286fc5e3e2ccc989d756eb63c73fe2416a32 +Subproject commit 628c30197232e37eabd3adc389032db6b9dd88e5 From 9931d9be9de0c759b5b1616d9edcb9a9fa17887d Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 14:41:20 +0100 Subject: [PATCH 26/31] feat: update remappings Signed-off-by: Pablo Maldonado --- remappings.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remappings.txt b/remappings.txt index 8d4be09..5174468 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,3 +1,3 @@ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ -redstone-oracles-monorepo/=lib/redstone-contracts/src/RedStoneBaseContracts/redstone-oracles-monorepo +redstone-oracles-monorepo/packages/=lib/redstone-contracts/src/ From fdafdc4e3e5c2ee7240d21ad35480ce8432223f4 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 15:24:09 +0100 Subject: [PATCH 27/31] feat: updates Signed-off-by: Pablo Maldonado --- .gitignore | 1 + scripts/getRedstonePayload.log.txt | 2 - .../ChainlinkSourceAdapter.sol | 1 - src/oracles/RedstonePriceFeedWithRounds.sol | 29 +++++++- .../RedStoneAsChainlinkSourceAdapter.sol | 10 +-- test/unit/RedStoneOracle.sol | 72 +++++++++++++------ 6 files changed, 87 insertions(+), 28 deletions(-) delete mode 100644 scripts/getRedstonePayload.log.txt diff --git a/.gitignore b/.gitignore index f4e6715..02942b3 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ scripts/contract-types node_modules log.txt +getRedstonePayload.log.txt diff --git a/scripts/getRedstonePayload.log.txt b/scripts/getRedstonePayload.log.txt deleted file mode 100644 index 090e102..0000000 --- a/scripts/getRedstonePayload.log.txt +++ /dev/null @@ -1,2 +0,0 @@ -You have to provide a data FeedAn error occurred: Request failed {"reqParams":{"dataServiceId":"redstone-primary-prod","uniqueSignersCount":3,"dataFeeds":["--no-warnings"],"urls":["https://oracle-gateway-1.a.redstone.finance"]}}, Original error: AggregateError: , errors: Error: Requested data feed id is not included in response: --no-warnings - at parseAndValidateDataPackagesResponse (/Users/chris/uma/oval-contracts/scripts/node_modules/@redstone-finance/sdk/dist... \ No newline at end of file diff --git a/src/adapters/source-adapters/ChainlinkSourceAdapter.sol b/src/adapters/source-adapters/ChainlinkSourceAdapter.sol index b4ec1e8..7fb3fae 100644 --- a/src/adapters/source-adapters/ChainlinkSourceAdapter.sol +++ b/src/adapters/source-adapters/ChainlinkSourceAdapter.sol @@ -6,7 +6,6 @@ import {SafeCast} from "openzeppelin-contracts/contracts/utils/math/SafeCast.sol import {DecimalLib} from "../lib/DecimalLib.sol"; import {IAggregatorV3Source} from "../../interfaces/chainlink/IAggregatorV3Source.sol"; import {DiamondRootOval} from "../../DiamondRootOval.sol"; -import "forge-std/console.sol"; /** * @title ChainlinkSourceAdapter contract to read data from Chainlink aggregator and standardize it for Oval. diff --git a/src/oracles/RedstonePriceFeedWithRounds.sol b/src/oracles/RedstonePriceFeedWithRounds.sol index e40fcad..1d827f3 100644 --- a/src/oracles/RedstonePriceFeedWithRounds.sol +++ b/src/oracles/RedstonePriceFeedWithRounds.sol @@ -1,28 +1,55 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; -import +import {MergedPriceFeedAdapterWithRounds} from "redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol"; +/** + * @title RedstonePriceFeedWithRounds + * @dev A contract that extends MergedPriceFeedAdapterWithRounds to provide price feed functionality with rounds. + */ contract RedstonePriceFeedWithRounds is MergedPriceFeedAdapterWithRounds { + // The ID of the data feed bytes32 private immutable dataFeedId; + /** + * @notice Constructor to set the data feed ID. + * @param _dataFeedId The ID of the data feed. + */ constructor(bytes32 _dataFeedId) { dataFeedId = _dataFeedId; } + /** + * @notice Gets the data feed ID. + * @return The data feed ID. + */ function getDataFeedId() public view override returns (bytes32) { return dataFeedId; } + /** + * @notice Gets the data service ID. + * @return A string representing the data service ID. + */ function getDataServiceId() public pure override returns (string memory) { return "redstone-primary-prod"; } + /** + * @notice Gets the threshold for the unique signers. + * @return The number of unique signers required. + */ function getUniqueSignersThreshold() public pure override returns (uint8) { return 3; } + /** + * @notice Gets the index of an authorized signer based on their address. + * @param signerAddress The address of the signer. + * @return The index of the authorized signer. + * @dev Reverts if the signer is not authorized. + */ function getAuthorisedSignerIndex(address signerAddress) public view virtual override returns (uint8) { if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { return 0; diff --git a/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol b/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol index c3a78b3..5216b91 100644 --- a/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol +++ b/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol @@ -11,7 +11,8 @@ import {DecimalLib} from "../../../src/adapters/lib/DecimalLib.sol"; import {ChainlinkSourceAdapter} from "../../../src/adapters/source-adapters/ChainlinkSourceAdapter.sol"; import {IAggregatorV3Source} from "../../../src/interfaces/chainlink/IAggregatorV3Source.sol"; -import {RedstonePriceFeedWithRounds} from "../../../src/oracles/RedstonePriceFeedWithRounds.sol"; +import {MergedPriceFeedAdapterWithRounds} from + "redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol"; contract TestedSourceAdapter is ChainlinkSourceAdapter { constructor(IAggregatorV3Source source) ChainlinkSourceAdapter(source) {} @@ -30,12 +31,12 @@ contract TestedSourceAdapter is ChainlinkSourceAdapter { contract RedstoneAsChainlinkSourceAdapterTest is CommonTest { uint256 targetBlock = 19889008; - RedstonePriceFeedWithRounds redstone; + MergedPriceFeedAdapterWithRounds redstone; TestedSourceAdapter sourceAdapter; function setUp() public { vm.createSelectFork("mainnet", targetBlock); - redstone = RedstonePriceFeedWithRounds(0xdDb6F90fFb4d3257dd666b69178e5B3c5Bf41136); // Redstone weETH + redstone = MergedPriceFeedAdapterWithRounds(0xdDb6F90fFb4d3257dd666b69178e5B3c5Bf41136); // Redstone weETH sourceAdapter = new TestedSourceAdapter(IAggregatorV3Source(address(redstone))); } @@ -68,7 +69,8 @@ contract RedstoneAsChainlinkSourceAdapterTest is CommonTest { uint256 targetTime = block.timestamp - 1 hours; (uint80 latestRound,,,,) = redstone.latestRoundData(); - (int256 lookBackPrice, uint256 lookBackTimestamp, uint256 roundId) = sourceAdapter.tryLatestDataAt(targetTime, 10); + (int256 lookBackPrice, uint256 lookBackTimestamp, uint256 roundId) = + sourceAdapter.tryLatestDataAt(targetTime, 10); (, int256 answer,, uint256 updatedAt,) = redstone.getRoundData(latestRound); assertTrue(roundId == latestRound); assertTrue(updatedAt <= targetTime); diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol index f5563c7..272aff7 100644 --- a/test/unit/RedStoneOracle.sol +++ b/test/unit/RedStoneOracle.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; -import {RedstoneConsumerNumericBase} from - "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol"; +import {RedstoneConsumerNumericBase} from "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol"; import {CommonTest} from "../Common.sol"; @@ -16,8 +15,12 @@ import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3S import {TestedSourceAdapter} from "../fork/adapters/ChainlinkSourceAdapter.sol"; +import "forge-std/console.sol"; + contract MockRedstonePayload is CommonTest { - function getRedstonePayload(string memory priceFeed) public returns (bytes memory) { + function getRedstonePayload( + string memory priceFeed + ) public returns (bytes memory) { string[] memory args = new string[](4); args[0] = "node"; args[1] = "--no-warnings"; @@ -34,41 +37,70 @@ contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { function setUp() public { redstoneOracle = new RedstonePriceFeedWithRounds(bytes32("BTC")); - sourceAdapter = new TestedSourceAdapter(IAggregatorV3Source(address(redstoneOracle))); + sourceAdapter = new TestedSourceAdapter( + IAggregatorV3Source(address(redstoneOracle)) + ); } - function pushPrice() internal returns (uint256) { + function pushPrice() internal returns (uint256, uint256) { bytes memory data = getRedstonePayload("BTC"); - (bytes memory redstonePayload, uint256 timestampMilliseconds, uint256 updatePrice) = - abi.decode(data, (bytes, uint256, uint256)); + ( + bytes memory redstonePayload, + uint256 timestampMilliseconds, + uint256 updatePrice + ) = abi.decode(data, (bytes, uint256, uint256)); + + uint256 timestampSeconds = timestampMilliseconds / 1000; - bytes memory encodedFunction = abi.encodeWithSignature("updateDataFeedsValues(uint256)", timestampMilliseconds); - bytes memory encodedFunctionWithRedstonePayload = abi.encodePacked(encodedFunction, redstonePayload); + vm.warp(timestampSeconds); + bytes memory encodedFunction = abi.encodeWithSignature( + "updateDataFeedsValues(uint256)", + timestampMilliseconds + ); + bytes memory encodedFunctionWithRedstonePayload = abi.encodePacked( + encodedFunction, + redstonePayload + ); address(redstoneOracle).call(encodedFunctionWithRedstonePayload); - return updatePrice; + return (updatePrice, timestampSeconds); } function testPushPrice() public { - vm.warp(1234); - uint256 updatePrice = pushPrice(); - (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) = - redstoneOracle.latestRoundData(); + (uint256 updatePrice, uint256 updateTimestamp) = pushPrice(); + ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) = redstoneOracle.latestRoundData(); assertEq(roundId, 1); assertEq(uint256(answer), updatePrice); - assertEq(startedAt, 1234); - assertEq(updatedAt, 1234); + assertEq(startedAt, updateTimestamp); + assertEq(updatedAt, updateTimestamp); assertEq(answeredInRound, 1); } function testCorrectlyStandardizesOutputs() public { - uint256 pushedPrice = pushPrice(); - (, int256 latestChainlinkAnswer,, uint256 latestChainlinkTimestamp,) = redstoneOracle.latestRoundData(); - (int256 latestSourceAnswer, uint256 latestSourceTimestamp) = sourceAdapter.getLatestSourceData(); - assertTrue(scaleChainlinkTo18(latestChainlinkAnswer) == latestSourceAnswer); + (uint256 pushedPrice, ) = pushPrice(); + ( + , + int256 latestChainlinkAnswer, + , + uint256 latestChainlinkTimestamp, + + ) = redstoneOracle.latestRoundData(); + ( + int256 latestSourceAnswer, + uint256 latestSourceTimestamp + ) = sourceAdapter.getLatestSourceData(); + assertTrue( + scaleChainlinkTo18(latestChainlinkAnswer) == latestSourceAnswer + ); assertTrue(pushedPrice == uint256(latestChainlinkAnswer)); assertTrue(latestSourceTimestamp == latestChainlinkTimestamp); } From e4073cf5ae7c754951d9321cbcf1690456446c6d Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 15:29:54 +0100 Subject: [PATCH 28/31] refactor: del test redstone oracle Signed-off-by: Pablo Maldonado --- lib/redstone-contracts | 2 +- src/oracles/RedstonePriceFeedWithRounds.sol | 68 --------------------- test/unit/RedStoneOracle.sol | 58 +++++------------- 3 files changed, 16 insertions(+), 112 deletions(-) delete mode 100644 src/oracles/RedstonePriceFeedWithRounds.sol diff --git a/lib/redstone-contracts b/lib/redstone-contracts index 628c301..8bcda0a 160000 --- a/lib/redstone-contracts +++ b/lib/redstone-contracts @@ -1 +1 @@ -Subproject commit 628c30197232e37eabd3adc389032db6b9dd88e5 +Subproject commit 8bcda0a5ba9cc3fd1ad9b6869f498e18ce29f7b2 diff --git a/src/oracles/RedstonePriceFeedWithRounds.sol b/src/oracles/RedstonePriceFeedWithRounds.sol deleted file mode 100644 index 1d827f3..0000000 --- a/src/oracles/RedstonePriceFeedWithRounds.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity 0.8.17; - -import {MergedPriceFeedAdapterWithRounds} from - "redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol"; - -/** - * @title RedstonePriceFeedWithRounds - * @dev A contract that extends MergedPriceFeedAdapterWithRounds to provide price feed functionality with rounds. - */ -contract RedstonePriceFeedWithRounds is MergedPriceFeedAdapterWithRounds { - // The ID of the data feed - bytes32 private immutable dataFeedId; - - /** - * @notice Constructor to set the data feed ID. - * @param _dataFeedId The ID of the data feed. - */ - constructor(bytes32 _dataFeedId) { - dataFeedId = _dataFeedId; - } - - /** - * @notice Gets the data feed ID. - * @return The data feed ID. - */ - function getDataFeedId() public view override returns (bytes32) { - return dataFeedId; - } - - /** - * @notice Gets the data service ID. - * @return A string representing the data service ID. - */ - function getDataServiceId() public pure override returns (string memory) { - return "redstone-primary-prod"; - } - - /** - * @notice Gets the threshold for the unique signers. - * @return The number of unique signers required. - */ - function getUniqueSignersThreshold() public pure override returns (uint8) { - return 3; - } - - /** - * @notice Gets the index of an authorized signer based on their address. - * @param signerAddress The address of the signer. - * @return The index of the authorized signer. - * @dev Reverts if the signer is not authorized. - */ - function getAuthorisedSignerIndex(address signerAddress) public view virtual override returns (uint8) { - if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { - return 0; - } else if (signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499) { - return 1; - } else if (signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202) { - return 2; - } else if (signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE) { - return 3; - } else if (signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de) { - return 4; - } else { - revert SignerNotAuthorised(signerAddress); - } - } -} diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol index 272aff7..fe546ad 100644 --- a/test/unit/RedStoneOracle.sol +++ b/test/unit/RedStoneOracle.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.17; -import {RedstoneConsumerNumericBase} from "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol"; +import {RedstoneConsumerNumericBase} from + "@redstone-finance/evm-connector/contracts/core/RedstoneConsumerNumericBase.sol"; import {CommonTest} from "../Common.sol"; @@ -9,7 +10,7 @@ import {BaseController} from "../../src/controllers/BaseController.sol"; import {ChainlinkSourceAdapter} from "../../src/adapters/source-adapters/ChainlinkSourceAdapter.sol"; import {DecimalLib} from "../../src/adapters/lib/DecimalLib.sol"; import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3Source.sol"; -import {RedstonePriceFeedWithRounds} from "../../src/oracles/RedstonePriceFeedWithRounds.sol"; +import {RedstonePriceFeedWithRounds} from "redstone-oracles-monorepo/packages/examples/RedstonePriceFeedWithRounds.sol"; import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3Source.sol"; @@ -18,9 +19,7 @@ import {TestedSourceAdapter} from "../fork/adapters/ChainlinkSourceAdapter.sol"; import "forge-std/console.sol"; contract MockRedstonePayload is CommonTest { - function getRedstonePayload( - string memory priceFeed - ) public returns (bytes memory) { + function getRedstonePayload(string memory priceFeed) public returns (bytes memory) { string[] memory args = new string[](4); args[0] = "node"; args[1] = "--no-warnings"; @@ -37,31 +36,20 @@ contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { function setUp() public { redstoneOracle = new RedstonePriceFeedWithRounds(bytes32("BTC")); - sourceAdapter = new TestedSourceAdapter( - IAggregatorV3Source(address(redstoneOracle)) - ); + sourceAdapter = new TestedSourceAdapter(IAggregatorV3Source(address(redstoneOracle))); } function pushPrice() internal returns (uint256, uint256) { bytes memory data = getRedstonePayload("BTC"); - ( - bytes memory redstonePayload, - uint256 timestampMilliseconds, - uint256 updatePrice - ) = abi.decode(data, (bytes, uint256, uint256)); + (bytes memory redstonePayload, uint256 timestampMilliseconds, uint256 updatePrice) = + abi.decode(data, (bytes, uint256, uint256)); uint256 timestampSeconds = timestampMilliseconds / 1000; vm.warp(timestampSeconds); - bytes memory encodedFunction = abi.encodeWithSignature( - "updateDataFeedsValues(uint256)", - timestampMilliseconds - ); - bytes memory encodedFunctionWithRedstonePayload = abi.encodePacked( - encodedFunction, - redstonePayload - ); + bytes memory encodedFunction = abi.encodeWithSignature("updateDataFeedsValues(uint256)", timestampMilliseconds); + bytes memory encodedFunctionWithRedstonePayload = abi.encodePacked(encodedFunction, redstonePayload); address(redstoneOracle).call(encodedFunctionWithRedstonePayload); @@ -70,13 +58,8 @@ contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { function testPushPrice() public { (uint256 updatePrice, uint256 updateTimestamp) = pushPrice(); - ( - uint80 roundId, - int256 answer, - uint256 startedAt, - uint256 updatedAt, - uint80 answeredInRound - ) = redstoneOracle.latestRoundData(); + (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) = + redstoneOracle.latestRoundData(); assertEq(roundId, 1); assertEq(uint256(answer), updatePrice); @@ -86,21 +69,10 @@ contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { } function testCorrectlyStandardizesOutputs() public { - (uint256 pushedPrice, ) = pushPrice(); - ( - , - int256 latestChainlinkAnswer, - , - uint256 latestChainlinkTimestamp, - - ) = redstoneOracle.latestRoundData(); - ( - int256 latestSourceAnswer, - uint256 latestSourceTimestamp - ) = sourceAdapter.getLatestSourceData(); - assertTrue( - scaleChainlinkTo18(latestChainlinkAnswer) == latestSourceAnswer - ); + (uint256 pushedPrice,) = pushPrice(); + (, int256 latestChainlinkAnswer,, uint256 latestChainlinkTimestamp,) = redstoneOracle.latestRoundData(); + (int256 latestSourceAnswer, uint256 latestSourceTimestamp) = sourceAdapter.getLatestSourceData(); + assertTrue(scaleChainlinkTo18(latestChainlinkAnswer) == latestSourceAnswer); assertTrue(pushedPrice == uint256(latestChainlinkAnswer)); assertTrue(latestSourceTimestamp == latestChainlinkTimestamp); } From 4c625ea144b3da94018feba5c0a05df185dc16f4 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 15:38:01 +0100 Subject: [PATCH 29/31] feat: rename redstone remapping Signed-off-by: Pablo Maldonado --- remappings.txt | 2 +- test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol | 2 +- test/unit/RedStoneOracle.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/remappings.txt b/remappings.txt index 5174468..1f54288 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,3 +1,3 @@ ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ -redstone-oracles-monorepo/packages/=lib/redstone-contracts/src/ +redstone-oracle/=lib/redstone-contracts/src/ diff --git a/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol b/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol index 5216b91..d1430eb 100644 --- a/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol +++ b/test/fork/adapters/RedStoneAsChainlinkSourceAdapter.sol @@ -12,7 +12,7 @@ import {ChainlinkSourceAdapter} from "../../../src/adapters/source-adapters/Chai import {IAggregatorV3Source} from "../../../src/interfaces/chainlink/IAggregatorV3Source.sol"; import {MergedPriceFeedAdapterWithRounds} from - "redstone-oracles-monorepo/packages/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol"; + "redstone-oracle/on-chain-relayer/contracts/price-feeds/with-rounds/MergedPriceFeedAdapterWithRounds.sol"; contract TestedSourceAdapter is ChainlinkSourceAdapter { constructor(IAggregatorV3Source source) ChainlinkSourceAdapter(source) {} diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol index fe546ad..9cef982 100644 --- a/test/unit/RedStoneOracle.sol +++ b/test/unit/RedStoneOracle.sol @@ -10,7 +10,7 @@ import {BaseController} from "../../src/controllers/BaseController.sol"; import {ChainlinkSourceAdapter} from "../../src/adapters/source-adapters/ChainlinkSourceAdapter.sol"; import {DecimalLib} from "../../src/adapters/lib/DecimalLib.sol"; import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3Source.sol"; -import {RedstonePriceFeedWithRounds} from "redstone-oracles-monorepo/packages/examples/RedstonePriceFeedWithRounds.sol"; +import {RedstonePriceFeedWithRounds} from "redstone-oracle/examples/RedstonePriceFeedWithRounds.sol"; import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3Source.sol"; From 31ac93f40011322ef215407155705c4336d37a0d Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 15:43:29 +0100 Subject: [PATCH 30/31] feat: clean submodules Signed-off-by: Pablo Maldonado --- .gitmodules | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 3f3212d..124ef1f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,3 @@ [submodule "lib/redstone-contracts"] path = lib/redstone-contracts url = https://github.com/UMAprotocol/redstone-contracts -[submodule "lib/redstone-contracts.git"] - path = lib/redstone-contracts.git - url = https://github.com/UMAprotocol/redstone-contracts.git From fcf261378b32184a99ecc26fc094f86dddec2138 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Mon, 20 May 2024 15:46:09 +0100 Subject: [PATCH 31/31] refactor: console log Signed-off-by: Pablo Maldonado --- test/unit/RedStoneOracle.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol index 9cef982..c3c4d15 100644 --- a/test/unit/RedStoneOracle.sol +++ b/test/unit/RedStoneOracle.sol @@ -16,7 +16,6 @@ import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3S import {TestedSourceAdapter} from "../fork/adapters/ChainlinkSourceAdapter.sol"; -import "forge-std/console.sol"; contract MockRedstonePayload is CommonTest { function getRedstonePayload(string memory priceFeed) public returns (bytes memory) {