Skip to content

Commit

Permalink
feat: import redstone contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
md0x committed May 20, 2024
1 parent 065b3a3 commit 628c301
Show file tree
Hide file tree
Showing 70 changed files with 5,159 additions and 115 deletions.
9 changes: 9 additions & 0 deletions .gitmodules
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
67 changes: 2 additions & 65 deletions README.md
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).
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
);
}
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at 978ac6
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at 045704
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts-upgradeable
4 changes: 4 additions & 0 deletions remappings.txt
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
12 changes: 0 additions & 12 deletions script/Counter.s.sol

This file was deleted.

14 changes: 0 additions & 14 deletions src/Counter.sol

This file was deleted.

163 changes: 163 additions & 0 deletions src/evm-connector/core/CalldataExtractor.sol
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_;
}
}
Loading

0 comments on commit 628c301

Please sign in to comment.