From 26c33a1019c15a2c156d99d61c0cf3a05a7de328 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Thu, 16 May 2024 17:14:28 +0100 Subject: [PATCH] refactor: cleanup Signed-off-by: Pablo Maldonado --- .github/workflows/test.yml | 5 ++ getRedstonePayload.log.txt | 3 +- redstone/getRedstonePayload.js | 76 ++++++++-------------- test/unit/RedStoneOracle.sol | 113 ++++++++++++++------------------- 4 files changed, 78 insertions(+), 119 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 29ca00b..5ccec5b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,6 +71,11 @@ jobs: with: version: "nightly-de33b6af53005037b463318d2628b5cfcaf39916" + - name: "Navigate to redstone and install dependencies" + run: | + cd redstone + yarn install + - name: "Show the Foundry config" run: "forge config" diff --git a/getRedstonePayload.log.txt b/getRedstonePayload.log.txt index edea00b..6f38c81 100644 --- a/getRedstonePayload.log.txt +++ b/getRedstonePayload.log.txt @@ -1 +1,2 @@ -An error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableYou have to provide at least one dataFeedAn error occurred: provided input is not valid bytes value \ No newline at end of file +An error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableAn error occurred: this.signedDataPackages.map is not a function or its return value is not iterableYou have to provide at least one dataFeedAn error occurred: provided input is not valid bytes valueAn error occurred: Request failed {"reqParams":{"dataServiceId":"redstone-primary-prod","uniqueSignersCount":3,"dataFeeds":["BTC:120:8"],"urls":["https://oracle-gateway-1.a.redstone.finance"]}}, Original error: AggregateError: , errors: Error: Requested data feed id is not included in response: BTC:120:8 + at parseAndValidateDataPackagesResponse (/Users/salvadorpablomaldonadoturci/ghq/github.com/UMAprotocol/oval-contracts/redsto... \ No newline at end of file diff --git a/redstone/getRedstonePayload.js b/redstone/getRedstonePayload.js index 75db876..3ce90bc 100644 --- a/redstone/getRedstonePayload.js +++ b/redstone/getRedstonePayload.js @@ -1,9 +1,5 @@ const { appendFileSync } = require("fs"); -const { - DataPackage, - NumericDataPoint, - RedstonePayload, -} = require("@redstone-finance/protocol"); +const { RedstonePayload } = require("@redstone-finance/protocol"); const web3 = require("web3"); const sdk = require("@redstone-finance/sdk"); const args = process.argv.slice(2); @@ -20,69 +16,47 @@ const parsePrice = (value) => { return Number(bigNumberPrice); }; -const main = async () => { - if (args.length === 0) { - exit(1, "You have to provide at least on dataFeed"); +const pickMedian = (arr) => { + if (arr.length === 0) { + throw new Error("Cannot pick median of empty array"); } - - const dataFeeds = args[0].split(","); - - if (dataFeeds.length === 0) { - exit(2, "You have to provide at least on dataFeed"); + arr.sort((a, b) => a - b); + const middleIndex = Math.floor(arr.length / 2); + if (arr.length % 2 === 0) { + return (arr[middleIndex - 1] + arr[middleIndex]) / 2; + } else { + return arr[middleIndex]; } +}; - const timestampMilliseconds = Date.now(); - - const PRIVATE_KEY_1 = - "0x548e7c2fae09cc353ffe54ed40609d88a99fab24acfc81bfbf5cd9c11741643d"; - - const dataPoints = dataFeeds.map((arg) => { - const [dataFeedId, value, decimals] = arg.split(":"); - - if (!dataFeedId || !value || !decimals) { - exit( - 3, - "Input should have format: dataFeedId:value:decimals (example: BTC:120:8)" - ); - } - - return new NumericDataPoint({ - dataFeedId, - value: parseInt(value), - decimals: parseInt(decimals), - }); - }); - - // Prepare unsigned data package - const dataPackage = new DataPackage(dataPoints, timestampMilliseconds); +const main = async () => { + if (args.length === 0) { + exit(1, "You have to provide a data Feed"); + } - // Prepare signed data packages - const signedDataPackages = [dataPackage.sign(PRIVATE_KEY_1)]; + const dataFeed = args[0]; const getLatestSignedPrice = await sdk.requestDataPackages({ dataServiceId: "redstone-primary-prod", uniqueSignersCount: 3, - dataFeeds: ["BTC"], + dataFeeds: [dataFeed], urls: ["https://oracle-gateway-1.a.redstone.finance"], }); - // const payload = RedstonePayload.prepare(signedDataPackages, ""); - const payload = RedstonePayload.prepare(getLatestSignedPrice["BTC"], ""); + const prices = getLatestSignedPrice[dataFeed].map((dataPackage) => + parsePrice(dataPackage.dataPackage.dataPoints[0].value) + ); + + const medianPrice = pickMedian(prices); - // process.stdout.write("0x" + payload) + const payload = RedstonePayload.prepare(getLatestSignedPrice[dataFeed], ""); const timestampMS = - getLatestSignedPrice["BTC"][0].dataPackage.timestampMilliseconds; + getLatestSignedPrice[dataFeed][0].dataPackage.timestampMilliseconds; const encodedData = web3.eth.abi.encodeParameters( ["bytes", "uint256", "uint256"], - [ - "0x" + payload, - timestampMS, - parsePrice( - getLatestSignedPrice["BTC"][0].dataPackage.dataPoints[0].value - ), - ] + ["0x" + payload, timestampMS, medianPrice] ); process.stdout.write(encodedData); diff --git a/test/unit/RedStoneOracle.sol b/test/unit/RedStoneOracle.sol index 2e53460..74b096b 100644 --- a/test/unit/RedStoneOracle.sol +++ b/test/unit/RedStoneOracle.sol @@ -10,7 +10,9 @@ import {DecimalLib} from "../../src/adapters/lib/DecimalLib.sol"; import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3Source.sol"; import {RedstonePriceFeedWithRounds} from "../../src/oracles/RedstonePriceFeedWithRounds.sol"; -import "forge-std/console.sol"; +import {IAggregatorV3Source} from "../../src/interfaces/chainlink/IAggregatorV3Source.sol"; + +import {TestedSourceAdapter} from "../fork/adapters/ChainlinkSourceAdapter.sol"; contract MockRedstonePayload is CommonTest { function getRedstonePayload( @@ -27,39 +29,25 @@ contract MockRedstonePayload is CommonTest { } } -contract RedstoneOracleAdapterTest is - CommonTest, - MockRedstonePayload, - RedstoneConsumerNumericBase -{ +contract RedstoneOracleAdapterTest is CommonTest, MockRedstonePayload { RedstonePriceFeedWithRounds redstoneOracle; + TestedSourceAdapter sourceAdapter; function setUp() public { redstoneOracle = new RedstonePriceFeedWithRounds(bytes32("BTC")); - } - - function testPushPrice() public { - bytes memory data = getRedstonePayload("BTC:120:8"); - - (bytes memory redstonePayload, uint256 timestampMilliseconds, uint256 updatePrice) = abi - .decode(data, (bytes, uint256,uint256)); - - bytes memory encodedFunctionNumericValues = abi.encodeWithSignature( - "getOracleNumericValueFromTxMsg(bytes32)", - bytes32("BTC") + sourceAdapter = new TestedSourceAdapter( + IAggregatorV3Source(address(redstoneOracle)) ); + } - bytes memory encodedFunctionNumericWithRedstonePayload = abi - .encodePacked(encodedFunctionNumericValues, redstonePayload); + function pushPrice() internal returns (uint256) { + bytes memory data = getRedstonePayload("BTC"); - (bool success2, bytes memory dataa) = address(redstoneOracle).call( - encodedFunctionNumericWithRedstonePayload - ); - - uint256 oracleValue; - if (success2) { - oracleValue = abi.decode(dataa, (uint256)); - } + ( + bytes memory redstonePayload, + uint256 timestampMilliseconds, + uint256 updatePrice + ) = abi.decode(data, (bytes, uint256, uint256)); bytes memory encodedFunction = abi.encodeWithSignature( "updateDataFeedsValues(uint256)", @@ -70,16 +58,13 @@ contract RedstoneOracleAdapterTest is redstonePayload ); - (bool success, ) = address(redstoneOracle).call( - encodedFunctionWithRedstonePayload - ); - // (bool success3, ) = address(redstoneOracle).call( - // encodedFunctionWithRedstonePayload - // ); + address(redstoneOracle).call(encodedFunctionWithRedstonePayload); - assert(success); - // assert(success3); + return updatePrice; + } + function testPushPrice() public { + uint256 updatePrice = pushPrice(); ( uint80 roundId, int256 answer, @@ -88,40 +73,34 @@ contract RedstoneOracleAdapterTest is uint80 answeredInRound ) = redstoneOracle.latestRoundData(); - console.logInt(answer); - console.logUint(oracleValue); - console.logBytes(dataa); - - // assertEq(roundId, 1); + assertEq(roundId, 1); assertEq(uint256(answer), updatePrice); - // // assertEq(startedAt, timestampMilliseconds); - // // assertEq(updatedAt, timestampMilliseconds); - // assertEq(answeredInRound, 1); + // assertEq(startedAt, timestampMilliseconds); + // assertEq(updatedAt, timestampMilliseconds); + assertEq(answeredInRound, 1); + } + + function testCorrectlyStandardizesOutputs() public { + uint256 pushedPrice = pushPrice(); + ( + , + int256 latestChainlinkAnswer, + , + uint256 latestChainlinkTimestamp, + + ) = redstoneOracle.latestRoundData(); + ( + int256 latestSourceAnswer, + uint256 latestSourceTimestamp + ) = sourceAdapter.getLatestSourceData(); + assertTrue( + scaleChainlinkTo18(latestChainlinkAnswer) == latestSourceAnswer + ); + assertTrue(pushedPrice == uint256(latestChainlinkAnswer)); + assertTrue(latestSourceTimestamp == latestChainlinkTimestamp); } - function getAuthorisedSignerIndex( - address signerAddress - ) public view virtual override returns (uint8) { - if (signerAddress == 0x8BB8F32Df04c8b654987DAaeD53D6B6091e3B774) { - return 0; - } else if ( - signerAddress == 0xdEB22f54738d54976C4c0fe5ce6d408E40d88499 - ) { - return 1; - } else if ( - signerAddress == 0x51Ce04Be4b3E32572C4Ec9135221d0691Ba7d202 - ) { - return 2; - } else if ( - signerAddress == 0xDD682daEC5A90dD295d14DA4b0bec9281017b5bE - ) { - return 3; - } else if ( - signerAddress == 0x9c5AE89C4Af6aA32cE58588DBaF90d18a855B6de - ) { - return 4; - } else { - revert SignerNotAuthorised(signerAddress); - } + function scaleChainlinkTo18(int256 input) public pure returns (int256) { + return (input * 10 ** 18) / 10 ** 8; } }