Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update redstone-core.mdx #48

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
338 changes: 126 additions & 212 deletions docs/get-started/models/redstone-core.mdx
Original file line number Diff line number Diff line change
@@ -1,235 +1,111 @@
---
sidebar_position: 1
sidebar_label: "⚙️ Core (on-demand feeds)"
sidebar_label: "Pull Model"
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# ⚙️ Core Model

## Fetching prices on-demand

This is our basic operating model when the data is automatically appended to user transaction.

:::tip In Prod

Core model is the most mature way to use RedStone, battle tested in production, protecting >$100M TVL of [DeFi Protocols](https://defillama.com/oracles/RedStone) (not all listed yet) across multiple mainnets. The price feeds have been injected to more than [~50K transactions](https://dune.com/hatskier/redstone).

:::

## Installation

Install [@redstone-finance/evm-connector](https://www.npmjs.com/package/@redstone-finance/evm-connector) from NPM registry

### Hardhat

<Tabs>
<TabItem value="Yarn">

```bash
yarn add @redstone-finance/evm-connector
```

</TabItem>
<TabItem value="NPM">

```bash
npm install @redstone-finance/evm-connector
```

</TabItem>
</Tabs>


### Foundry

Foundry installs dependencies using git submodules. Thus additional steps are needed to [install dependencies](https://book.getfoundry.sh/projects/dependencies).

In foundry project:

1. Install `@redstone-finance/evm-connector` - it will install current code from main branch

```bash
forge install redstone-finance/redstone-oracles-monorepo
```

2. Install `@OpenZeppelin` contracts (dependency of `@redstone-finance/evm-connector`) - it will install current code from main branch

```bash
forge install OpenZeppelin/openzeppelin-contracts@v4.9.5
```

3. Add libraries to `remappings.txt`

```bash
# Pull Model
This is our recommended model which provides data feeds to dApps only upon request, reducing the costs of putting data onto the blockchain. To learn more about how RedStone brings data on-chain, check out the [Data Formatting and Processing](https://docs.redstone.finance/docs/get-started/data-formatting-processing) section.
### Prerequisites Before You Begin:

- **Knowledge of Smart Contracts:** Understanding how to implement and interact with smart contracts.
- **Familiarity with Hardhat or Foundry:** Knowing how to use these development environments for building and testing dApps.
- **OpenZeppelin Contracts:** Understanding and using OpenZeppelin's library.

### Important Notes:
- **Solidity Version:** Ensure your smart contract uses Solidity version 0.8.4 or higher. If using an older version, refer to the manual payload method.
- **Testing Environment:** Remix is not supported for testing RedStone Oracles.
- **Upgradability:** Implement an upgradability mechanism (e.g., multisig or DAO) for your contracts to quickly replace data providers if needed.
- **Examples:** You can see examples of the `@redstone-finance/evm-connector` usage in our [dedicated repo with examples](https://github.com/redstone-finance/redstone-evm-examples).


# ****Step-by-Step Guide****

## 1. Install Prerequisites

#### For Hardhat
1. To add the RedStone EVM connector package, type one of these commands:
- If you're using Yarn:
```bash
yarn add @redstone-finance/evm-connector
```
- If you're using npm:
```bash
npm install @redstone-finance/evm-connector
```

#### For Foundry

1. Open your terminal
2. Navigate to your Foundry project directory
```bash
cd path/to/your/foundry/project
```

3. Install the RedStone EVM connector

```
forge install redstone-finance/redstone-oracles-monorepo
```

4. Install the OpenZeppelin contracts, which the RedStone connector relies on.

```
forge install OpenZeppelin/openzeppelin-contracts@v4.9.5
```
5. Link these new libraries by adding their paths to a file called remappings.txt.
```bash
echo "@redstone-finance/evm-connector/dist/contracts/=lib/redstone-oracles-monorepo/packages/evm-connector/contracts/
@openzeppelin/contracts=lib/openzeppelin-contracts/contracts/" >> remappings.txt
```

## Usage

:::tip TLDR;
You need to do 2 things:

1. [Adjust your smart contracts](#1-adjust-your-smart-contracts) to include the libraries responsible for data extraction and verification
2. [Adjust Javascript code of your dApp](#2-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
:::caution Heads up
1. Our contracts require `solidity > 0.8.4`. If your code is written in an older version please use the [Manual Payload](#manual-payload).
2. If you work with 3rd party aggregators, make sure that they also support passing the additional payload.
3. Please don't use Remix to test RedStone oracles, as Remix does not support modifying transactions in the way that the evm-connector does
4. 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](https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector/contracts/data-services), depending on which data service are you going to use.

<details>
<summary> List of base contracts with data services </summary>

| Base Contract | Data service with the list of feeds | Status |
|----------------------------------------|------------------------------|-----------------|
| [MainDemoConsumerBase.sol](https://github.com/redstone-finance/redstone-oracles-monorepo/blob/main/packages/evm-connector/contracts/data-services/MainDemoConsumerBase.sol) | [redstone-main-demo](https://app.redstone.finance/#/app/data-services/redstone-main-demo) | Demo |
| [RapidDemoConsumerBase.sol](https://github.com/redstone-finance/redstone-oracles-monorepo/blob/main/packages/evm-connector/contracts/data-services/RapidDemoConsumerBase.sol) | [redstone-rapid-demo](https://app.redstone.finance/#/app/data-services/redstone-rapid-demo) | Demo |
| [StocksDemoConsumerBase.sol](https://github.com/redstone-finance/redstone-oracles-monorepo/blob/main/packages/evm-connector/contracts/data-services/StocksDemoConsumerBase.sol) | [redstone-stocks-demo](https://app.redstone.finance/#/app/data-services/redstone-stocks-demo) | Demo |
| [AvalancheDataServiceConsumerBase.sol](https://github.com/redstone-finance/redstone-oracles-monorepo/blob/main/packages/evm-connector/contracts/data-services/AvalancheDataServiceConsumerBase.sol) | [redstone-avalanche-prod](https://app.redstone.finance/#/app/data-services/redstone-avalanche-prod) | Production |
| [PrimaryProdDataServiceConsumerBase.sol](https://github.com/redstone-finance/redstone-oracles-monorepo/blob/main/packages/evm-connector/contracts/data-services/PrimaryProdDataServiceConsumerBase.sol) | [redstone-primary-prod](https://app.redstone.finance/#/app/data-services/redstone-primary-prod) | Production |


💡 Note: Service with `Production` status have got multiple nodes deployed and are professionally monitored.
</details>
## 2. Adjust Your Smart Contracts

#### 1. Import RedStone Base Contract
Add this line at the top of your smart contract code.

```js
import "@redstone-finance/evm-connector/contracts/data-services/MainDemoConsumerBase.sol";

contract YourContractName is MainDemoConsumerBase {
...
}
```
You should pass the data feed id converted to `bytes32`.

<Tabs>
<TabItem value="Getting a single value">

```js
uint256 ethPrice = getOracleNumericValueFromTxMsg(bytes32("ETH"));
```

</TabItem>
<TabItem value="Getting several values">

```js
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];
```
</TabItem>
</Tabs>



For all the supported feeds we provide [UI with charts and historical data](https://app.redstone.finance)

💡 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](https://github.com/ethers-io/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

<Tabs>
<TabItem value="Javascript">
#### 2. Extend Your Contract
Make your contract use the new features by extending from MainDemoConsumerBase.

```js
const { WrapperBuilder } = require("@redstone-finance/evm-connector");
```

</TabItem>
<TabItem value="Typescript">

```js
import { WrapperBuilder } from "@redstone-finance/evm-connector";
```
</TabItem>
</Tabs>

Then you can wrap your ethers contract pointing to the selected [RedStone data service id.](https://app.redstone.finance/#/app/data-services) You can (optionally) specify a number of unique signers, data feed identifiers, and URLs for the redstone cache nodes.

```js
const yourEthersContract = new ethers.Contract(address, abi, provider);

const wrappedContract = WrapperBuilder.wrap(contract).usingDataService(
{
dataFeeds: ["ETH", "BTC"],
},
);
contract YourContractName is MainDemoConsumerBase {
// Your contract code goes here
}
```
#### 3. Use Data Feeds
Inside your contract, you can now access data provided by RedStone. This code fetches the latest price of ETH and BTC.

Now you can access any of the contract's methods in exactly the same way as interacting with the ethers-js code:

For a single price:
```js
wrappedContract.executeYourMethod();
uint256 ethPrice = getOracleNumericValueFromTxMsg(bytes32("ETH"));
```

#### 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.

For multiple prices:
```js
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();
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.

#### 4. About overriding the following functions (only if necessary - at your own risk)

You can see more examples of mocking data [here.](https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector/test/mock-wrapper)
```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.

##### Foundry
```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.

To use RedStone Oracles with Foundry in test context, we recommend using foundry `vm.ffi` function to generate mocked dataPackages.
We have prepared [repository](https://github.com/redstone-finance/minimal-foundry-repo) showing how we can integrate foundry with redstone.
- [consuming redstone payload in foundry contract](https://github.com/redstone-finance/minimal-foundry-repo/blob/main/test/Counter.t.sol)
- [generating mock redstone payload](https://github.com/redstone-finance/minimal-foundry-repo/blob/main/getRedstonePayload.js)
```getAuthorisedSignerIndex(address _signerAddress)``` returns (uint256) - to whitelist additional signers or remove corrupted ones.

## Manual payload
This approach is helpful if you need to pass the pricing data from one contract to another in your protocol.
```getUniqueSignersThreshold()``` returns (uint256) - to modify number of required signers. The higher number means greater reliability but also higher gas costs.

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:
#### 5. About a manual payload (if needed)

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 cases where your contracts are written in Solidity in a version lower than 0.8.4, making it problematic to extend from the RedstoneConsumerBase contract. In such cases, we recommend deploying a separate Extractor contract that will contain the verification logic.
```js
pragma solidity 0.8.4;

Expand All @@ -242,17 +118,11 @@ contract RedstoneExtractor is RedstoneConsumerNumericMock {
}
}
```

and proxy the payload from your originating contract

```js
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:

```js
const redstonePayload = await (new DataServiceWrapper({
dataServiceId: "redstone-main-demo",
Expand All @@ -262,7 +132,51 @@ const redstonePayload = await (new DataServiceWrapper({
// Interact with the contract (getting oracle value securely)
const price = await yourContract.getPriceFromRedstoneOracle(redstonePayload);
```
Working demo examples of the @redstone-finance/evm-connector usage can be found in our [dedicated repository with examples](https://github.com/redstone-finance/redstone-evm-examples).



## 3. Adjust JavaScript Code of Your dApp

#### 1. Import the Wrapper Code

```js
const { WrapperBuilder } = require("@redstone-finance/evm-connector");
// or using ES6 syntax
import { WrapperBuilder } from "@redstone-finance/evm-connector";
```

#### 2. Wrap Ethers contract

```js
const yourEthersContract = new ethers.Contract(address, abi, provider);

const wrappedContract = WrapperBuilder.wrap(contract).usingDataService({
dataFeeds ["ETH", "BTC"],
});
```

#### 3. Use the Wrapped Contract

## Working demo
```js
wrappedContract.executeYourMethod();
```
## 4. Testing
For Hardhat
Mock Wrapper for Testing.
Use a mock wrapper to simulate different scenarios without using real data:
```js
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();

```
For Foundry
Generate Mock Data:
Use Foundry's functions to create mock data packages for testing. Refer to the [foundry integration repository](https://github.com/redstone-finance/minimal-foundry-repo) for detailed examples.

You can see examples of the `@redstone-finance/evm-connector` usage in our [dedicated repo with examples](https://github.com/redstone-finance/redstone-evm-examples).