Skip to content
Open
Show file tree
Hide file tree
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
28 changes: 28 additions & 0 deletions .github/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Publish to NPM
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: 'Check out the repo'
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- name: 'Install Node.js'
uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0
with:
node-version-file: '.nvmrc'
registry-url: 'https://registry.npmjs.org'
# Defaults to the user or organization that owns the workflow file
scope: '@waveshq'
- name: Install dependencies and compile smart contracts
run: npm ci && npm run compile
- name: Build the package
run: npm run build
- run: npm config set "//registry.npmjs.org/:_authToken" "\${NODE_AUTH_TOKEN}" --location=project
- name: Publish package on NPM 📦
run: npm publish --access public --tag latest
env:
NODE_AUTH_TOKEN: ${{ secrets.WAVESHQ_NPM_TOKEN }}
- run: npm config delete "//registry.npmjs.org/:_authToken" --location=project
if: always()
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ typechain-types
cache
artifacts

# Foundry files
#foundry files
out
cache_forge

dist
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20.6.1
3 changes: 3 additions & 0 deletions .solhint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "solhint:default"
}
50 changes: 46 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,54 @@
# Redstone integration
# Redstone MetaChain integration

This repo is about how to integrate Redstone.
This follows the example demonstrated here: https://docs.redstone.finance/docs/smart-contract-devs/get-started/redstone-core
This repo aims to integrate Redstone oracle with DefiMetaChain
It is inspired from the example here: https://docs.redstone.finance/docs/smart-contract-devs/get-started/redstone-core

How to test:
## How to test

```
npx hardhat node
npx hardhat test --network localhost
```

## How to use the package

On the contracts side:
```
import "@waveshq/redstone-metachain-integration/contracts/PriceOracleConsumer.sol";

contract OracleConsumer is PriceOracleConsumer {
address public oracleContractAddress;

constructor(address _oracleContractAddress) {
oracleContractAddress = _oracleContractAddress;
}

function mockSwap(bytes32[] calldata dataFeedIds) public view returns (uint256[] memory) {
uint256[] memory prices = getPricesForDataFeedsFromOracle(dataFeedIds, oracleContractAddress)
return prices;
}
}
```

On the backend/ frontend side:
```javascript
import {CustomDataServiceWrapper} from "@waveshq/redstone-metachain-integration";
import {abi as RedstoneOracleAbi} from "@waveshq/redstone-metachain-integration/artifacts/contracts/RedstoneOracle.sol/RedstoneOracle.json";
import {abi as OracleConsumerAbi} from ""; // import abi for your smart contract
import {ethers} from "ethers"; // should be ethersv5

const redstoneOracle = new ethers.Contract(REDSTONE_ORACLE_ADDRESS, RedstoneOracleAbi, ethereum_provider);
const oracleConsumer = new ethers.Contract(ORACLE_CONSUMER_ADDRESS, OracleConsumerAbi, ethereum_provider);

const oracleConsumerWrapped = new CustomDataServiceWrapper({
dataFeedId: "redstone-primary-prod",
dataFeeds: ["ETH", "BTC"]
},
redstoneOracle
).overwriteEthersContract(
oracleConsumer
)

await oracleConsumerWrapped.mockSwap([ethers.encodeBytes32String("ETH"), ethers.encodeBytes32String("BTC")]);
// this should fetch the correct prices for eth and btc
```
48 changes: 48 additions & 0 deletions contracts/OracleConsumer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import "./RedstoneOracle.sol";
import "./PriceOracleConsumer.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract OracleConsumer is PriceOracleConsumer, Ownable {
mapping(address => bytes32) public tokenToDataFeedId;
RedstoneOracle oracleContract;
event SWAP_WITH_PRICE(uint256[] price);

constructor(address[] memory tokenAddresses, bytes32[] memory dataFeedIds, address _oracleContract) Ownable() {
for (uint i = 0; i < tokenAddresses.length; i++) {
tokenToDataFeedId[tokenAddresses[i]] = dataFeedIds[i];
}
oracleContract = RedstoneOracle(_oracleContract);
}

function mockSwap(address firstToken, address secondToken) public returns(bool) {
address[] memory dummyAddreses = new address[](2);
dummyAddreses[0] = address(1);
dummyAddreses[1] = address(2);
address[] memory tokenAddresses = new address[](1);
tokenAddresses[0] = firstToken;
uint256[] memory prices = getPricesForTokenAddresses(tokenAddresses);
emit SWAP_WITH_PRICE(prices);
return true;
}

function getPricesForTokenAddresses(address[] memory _tokenAddresses) public view returns (uint256[] memory)
{
bytes32[] memory dataFeedIds = new bytes32[](_tokenAddresses.length);
for (uint i = 0; i < _tokenAddresses.length; i++) {
dataFeedIds[i] = tokenToDataFeedId[_tokenAddresses[i]];
}
address oracleContractAddress = address(oracleContract);
return getPricesForDataFeedsFromOracle(dataFeedIds, oracleContractAddress);

}

function getPriceForTokenAddress(address _tokenAddress) public view returns (uint256)
{
bytes32 dataFeedId = tokenToDataFeedId[_tokenAddress];
address oracleContractAddress = address(oracleContract);
return getPriceForDataFeedFromOracle(dataFeedId, oracleContractAddress);
}
}
77 changes: 77 additions & 0 deletions contracts/PriceOracleConsumer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

contract PriceOracleConsumer {

/**
* @notice Function to get prices for data feeds from the oracle contract
* @param dataFeedIds array of data feed ids
* @param oracle the address of the oracle contract
*/
function getPricesForDataFeedsFromOracle(
bytes32[] memory dataFeedIds,
address oracle
) public view returns (uint256[] memory) {
bytes memory redstonePayload = abi.encodeWithSignature("getLatestPrices(bytes32[])", dataFeedIds);
bytes memory bytesRes;
assembly {
let initialRedstoneLen := mload(redstonePayload)
let newRedstoneLen := add(initialRedstoneLen, calldatasize())
mstore(redstonePayload, newRedstoneLen)
let oldMemorySize := mload(0x40)
bytesRes := add(oldMemorySize, calldatasize())
calldatacopy(oldMemorySize, 0x00, calldatasize())
let success := staticcall(gas(), oracle, add(redstonePayload, 0x20), newRedstoneLen, 0, 0)
// construct the memory bytes array
mstore(bytesRes, returndatasize())
returndatacopy(add(0x20, bytesRes), 0, returndatasize())
switch success
case 0 {
// bubble up the error
revert(add(0x20, bytesRes),returndatasize())
}
default {
mstore(0x40, add(add(bytesRes, 0x20), returndatasize()))
}
}
(uint256[] memory res) = abi.decode(bytesRes, (uint256[]));
return res;
}

/**
* @notice Function to get price for a data feed from the oracle contract
* @param dataFeedId dataFeedId
* @param oracle the address of the oracle contract
*/
function getPriceForDataFeedFromOracle(
bytes32 dataFeedId,
address oracle
) public view returns (uint256) {
bytes memory redstonePayload = abi.encodeWithSignature("getLatestPrice(bytes32)", dataFeedId);
bytes memory bytesRes;

assembly {
let initialRedstoneLen := mload(redstonePayload)
let newRedstoneLen := add(initialRedstoneLen, calldatasize())
mstore(redstonePayload, newRedstoneLen)
let oldMemorySize := mload(0x40)
bytesRes := add(oldMemorySize, calldatasize())
calldatacopy(oldMemorySize, 0x00, calldatasize())
let success := staticcall(gas(), oracle, add(redstonePayload, 0x20), newRedstoneLen, 0, 0)
// construct the memory bytes array
mstore(bytesRes, returndatasize())
returndatacopy(add(0x20, bytesRes), 0, returndatasize())
switch success
case 0 {
// bubble up the error
revert(add(0x20, bytesRes),returndatasize())
}
default {
mstore(0x40, add(add(bytesRes, 0x20), returndatasize()))
}
}

return abi.decode(bytesRes, (uint256));

}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import "@redstone-finance/evm-connector/contracts/data-services/PrimaryProdDataServiceConsumerBase.sol";

contract ExampleOracleConsumer is PrimaryProdDataServiceConsumerBase {
contract RedstoneOracle is PrimaryProdDataServiceConsumerBase {

function getLatestPrice(bytes32 dataFeedId) public view returns (uint256) {
return getOracleNumericValueFromTxMsg(dataFeedId);
Expand All @@ -11,8 +12,4 @@ contract ExampleOracleConsumer is PrimaryProdDataServiceConsumerBase {
function getLatestPrices(bytes32[] memory dataFeedIds) public view returns (uint256[] memory) {
return getOracleNumericValuesFromTxMsg(dataFeedIds);
}

function getLatestEthPrice() public view returns (uint256) {
return getOracleNumericValueFromTxMsg(bytes32("ETH"));
}
}
6 changes: 6 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[profile.default]
src = 'contracts'
out = 'out'
libs = ['node_modules', 'lib']
test = 'test'
cache_path = 'cache_forge'
45 changes: 45 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,53 @@
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import "@nomicfoundation/hardhat-foundry";
import "@nomicfoundation/hardhat-ledger";

require("dotenv").config({
path: ".env"
})

const config: HardhatUserConfig = {
solidity: "0.8.19",
paths: {
sources: "contracts/"
},
networks: {
DMCTestnet: {
url: process.env.DMC_TESTNET_URL || '',
accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [],
chainId: 1131
},
DMCMainnet: {
url: process.env.DMC_MAINNET_URL || '',
// ledgerAccounts: ['first EVM address of your ledger']
// chainId:
}
},
etherscan: {
apiKey: {
DMCTestnet: 'abc',
// DMCMainnet: 'abc'
},
customChains: [
{
network: 'DMCTestnet',
chainId: 1131,
urls: {
apiURL: 'https://blockscout.testnet.ocean.jellyfishsdk.com/api',
browserURL: 'https://meta.defiscan.live/?network=TestNet'
}
},
// {
// network: 'DMCMainnet',
// chainId:
// urls : {
// apiURL: blockscout URL,
// browserURL: browserURL
// }
// }
]
}
};

export default config;
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CustomDataServiceWrapper } from "./scripts/CustomDataServiceWrapperClass";
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at f73c73
Loading