Real-time Data from Diverse Sources
RedStone's Pull Model injects data directly into user transactions, maximizing gas efficiency while simplifying dApp data access. This streamlined approach handles the entire process in a single transaction, significantly reducing costs.
Best suited for dApps that require low-latency data.
Installation
Install @redstone-finance/evm-connector from NPM registry
Hardhat
- Yarn
- NPM
yarn add @redstone-finance/evm-connector
npm install @redstone-finance/evm-connector
Foundry
Foundry installs dependencies using git submodules. Thus additional steps are needed to install dependencies.
In foundry project:
-
Install
@redstone-finance/evm-connector
- it will install current code from main branchforge install redstone-finance/redstone-oracles-monorepo
-
Install
@OpenZeppelin
contracts (dependency of@redstone-finance/evm-connector
) - it will install current code from main branchforge install OpenZeppelin/openzeppelin-contracts@v4.9.5
-
Add libraries to
remappings.txt
echo "@redstone-finance/evm-connector/dist/contracts/=lib/redstone-oracles-monorepo/packages/evm-connector/contracts/
@openzeppelin/contracts=lib/openzeppelin-contracts/contracts/" >> remappings.txt
Usage
You need to do 2 things:
- Adjust your smart contracts to include the libraries responsible for data extraction and verification
- Adjust Javascript code of your dApp to inject the additional payload with data feeds (otherwise you will get smart contract errors).
1. Adjust your smart contracts
- Our contracts require
solidity > 0.8.4
. If your code is written in an older version please use the Manual Payload. - If you work with 3rd party aggregators, make sure that they also support passing the additional payload.
- Please don't use Remix to test RedStone, as Remix does not support modifying transactions in the way that the evm-connector does
- We strongly recommend having some upgradability mechanism for your contracts (it can be based on multisig or DAO). This way, you can quickly replace data providers in case of any issues.
You need to apply a minimum change to the source code to enable smart contract to access data. Your contract needs to extend one of our base contracts, depending on which data service are you going to use.
List of base contracts with data services
Base Contract | Data service with the list of feeds | Status |
---|---|---|
MainDemoConsumerBase.sol | redstone-main-demo | Demo |
RapidDemoConsumerBase.sol | redstone-rapid-demo | Demo |
StocksDemoConsumerBase.sol | redstone-stocks-demo | Demo |
AvalancheDataServiceConsumerBase.sol | redstone-avalanche-prod | Production |
PrimaryProdDataServiceConsumerBase.sol | redstone-primary-prod | Production |
💡 Note: Service with Production
status have got multiple nodes deployed and are professionally monitored.
import "@redstone-finance/evm-connector/contracts/data-services/MainDemoConsumerBase.sol";
contract YourContractName is MainDemoConsumerBase {
...
}
You should pass the data feed id converted to bytes32
.
- Getting a single value
- Getting several values
uint256 ethPrice = getOracleNumericValueFromTxMsg(bytes32("ETH"));
bytes32[] memory dataFeedIds = new bytes32[](2);
dataFeedIds[0] = bytes32("ETH");
dataFeedIds[1] = bytes32("BTC");
uint256[] memory values = getOracleNumericValuesFromTxMsg(dataFeedIds);
uint256 ethPrice = values[0];
uint256 btcPrice = values[1];
For all the supported feeds we provide UI with charts and historical data
💡 Note: You can also override the following functions (do it at your own risk):
-
isTimestampValid(uint256 receivedTimestamp) returns (bool)
- to enable custom logic of timestamp validation. You may specify a shorter delay to accept only the most recent price fees. However, on networks with longer block times you may extend this period to avoid rejecting too many transactions. -
aggregateValues(uint256[] memory values) returns (uint256)
- to enable custom logic of aggregating values from different providers (by default this function takes the median value). For example, you may request values from providers to be strictly equal while dealing with discrete numbers. -
getAuthorisedSignerIndex(address _signerAddress) returns (uint256)
- to whitelist additional signers or remove corrupted ones. -
getUniqueSignersThreshold() returns (uint256)
- to modify number of required signers. The higher number means greater reliability but also higher gas costs.
2. Adjust Javascript code of your dApp
You should also update the code responsible for submitting transactions. If you're using ethers.js, we've prepared a dedicated library to make the transition seamless.
Contract object wrapping
First, you need to import the wrapper code to your project
- Javascript
- Typescript
const { WrapperBuilder } = require("@redstone-finance/evm-connector");
import { WrapperBuilder } from "@redstone-finance/evm-connector";
Then you can wrap your ethers contract pointing to the selected RedStone data service id. You can (optionally) specify a number of unique signers, data feed identifiers, and URLs for the redstone cache nodes.
const yourEthersContract = new ethers.Contract(address, abi, provider);
const wrappedContract = WrapperBuilder.wrap(contract).usingDataService({
dataFeeds: ["ETH", "BTC"],
});
Now you can access any of the contract's methods in exactly the same way as interacting with the ethers-js code:
wrappedContract.executeYourMethod();
Testing
Hardhat
If you'd like to use the wrapper in a test context, we recommend using a mock wrapper so that you can easily override the oracle values to test different scenarios. To use the mock wrapper just use the usingMockData(signedDataPackages)
function instead of the usingDataService
function.
const {
SimpleNumericMockWrapper,
} = require("@redstone-finance/evm-connector/dist/src/wrappers/SimpleMockNumericWrapper");
const wrappedContract = WrapperBuilder.wrap(
yourContract,
).usingSimpleNumericMock({
mockSignersCount: 10,
dataPoints: [{ dataFeedId: "ETH", value: 1000 }],
});
await wrappedContract.yourMethod();
You can see more examples of mocking data here.
Foundry
To use RedStone with Foundry in test context, we recommend using foundry vm.ffi
function to generate mocked dataPackages.
We have prepared repository showing how we can integrate foundry with redstone.
Manual payload
This approach is helpful if you need to pass the pricing data from one contract to another in your protocol.
It's also a solution for a case, where your contracts are written in solidity in a version lower than 0.8.4
it could be problematic to extend from the RedstoneConsumerBase
contract.
In that case we recomment to deploy a separate Extractor
contract that will contain the verification logic:
pragma solidity 0.8.4;
import "@redstone-finance/evm-connector/contracts/mocks/RedstoneConsumerNumericMock.sol";
contract RedstoneExtractor is RedstoneConsumerNumericMock {
function extractPrice(bytes32 feedId, bytes calldata redstonePayload) public view returns(uint256) {
return getOracleNumericValueFromTxMsg(feedId);
}
}
and proxy the payload from your originating contract
function getPriceFromRedstoneOracle(bytes32 feedId, bytes calldata redstonePayload) public view returns(uint256) {
return redstoneExtractor.extractPrice(feedId, redstonePayload);
}
The manual payload could be obtained using the following code on the client side:
const redstonePayload = await new DataServiceWrapper({
dataServiceId: "redstone-main-demo",
dataFeeds: ["ETH"],
}).getRedstonePayloadForManualUsage(yourContract);
// Interact with the contract (getting oracle value securely)
const price = await yourContract.getPriceFromRedstoneOracle(redstonePayload);
Working demo
You can see examples of the @redstone-finance/evm-connector
usage in our dedicated repo with examples.
RedStone SDK
Even though the most popular way of using RedStone data is to pass it on-chain one may want to consume it off-chain. For this scenario we created @redstone-finance/sdk.