-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
70 changed files
with
5,159 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[submodule "lib/forge-std"] | ||
path = lib/forge-std | ||
url = https://github.com/foundry-rs/forge-std | ||
[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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,3 @@ | ||
## Foundry | ||
# Redstone Contracts | ||
|
||
**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** | ||
|
||
Foundry consists of: | ||
|
||
- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). | ||
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. | ||
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. | ||
- **Chisel**: Fast, utilitarian, and verbose solidity REPL. | ||
|
||
## Documentation | ||
|
||
https://book.getfoundry.sh/ | ||
|
||
## Usage | ||
|
||
### Build | ||
|
||
```shell | ||
$ forge build | ||
``` | ||
|
||
### Test | ||
|
||
```shell | ||
$ forge test | ||
``` | ||
|
||
### Format | ||
|
||
```shell | ||
$ forge fmt | ||
``` | ||
|
||
### Gas Snapshots | ||
|
||
```shell | ||
$ forge snapshot | ||
``` | ||
|
||
### Anvil | ||
|
||
```shell | ||
$ anvil | ||
``` | ||
|
||
### Deploy | ||
|
||
```shell | ||
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key> | ||
``` | ||
|
||
### Cast | ||
|
||
```shell | ||
$ cast <subcommand> | ||
``` | ||
|
||
### Help | ||
|
||
```shell | ||
$ forge --help | ||
$ anvil --help | ||
$ cast --help | ||
``` | ||
This repository serves as an auxiliary collection of smart contracts specifically designed to be used within a Foundry development environment. The primary purpose of this repository is to import contracts from the [Redstone Oracles Monorepo](https://github.com/redstone-finance/redstone-oracles-monorepo) for integration in Foundry as importing all the monorepo contracts would be impractical (and causes build errors). |
32 changes: 32 additions & 0 deletions
32
lib/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
); | ||
} |
Submodule openzeppelin-contracts
added at
045704
Submodule openzeppelin-contracts-upgradeable
added at
58fa0f
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ | ||
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ | ||
@redstone-finance/evm-connector/contracts/=src/evm-connector/ | ||
@chainlink=lib/@chainlink |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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_; | ||
} | ||
} |
Oops, something went wrong.