From e171965e8204813da370fad337e158f094249331 Mon Sep 17 00:00:00 2001 From: Harsh Date: Mon, 29 Apr 2024 12:13:50 +0530 Subject: [PATCH 1/4] added oracle price info --- README.md | 11 +- bot/StateRelayerBot.ts | 24 +- bot/test-i9n/StateRelayerBot.unit.ts | 19 +- bot/test-i9n/mockData/oceanMockedData.ts | 428 +++++++++++++++++++++++ bot/utils/transformData.ts | 23 +- bot/utils/types.ts | 1 + contracts/IStateRelayer.sol | 17 +- contracts/StateRelayer.sol | 33 +- scripts/gasEstimationRealData.ts | 16 +- tests/StateRelayer.data.spec.ts | 107 +++++- tests/StateRelayer.deployment.spec.ts | 2 +- 11 files changed, 647 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 14fcdfa..472c21b 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,12 @@ handler({ testGasCost: false, urlNetwork: "https://ocean.defichain.com/", envNetwork: EnvironmentNetwork.MainNet, - contractAddress: /* YOUR STATE RELAYER PROXY ADDRESS */, - signer: /* signer object */, - gasUpdateDEX: /* gas limit for dex update transaction */, - gasUpdateMaster: /* gas limit for master node update transaction */, - gasUpdateVault: /* gas limit for vault update transaction */ + contractAddress: "" /* YOUR STATE RELAYER PROXY ADDRESS */, + signer: "" /* signer object */, + gasUpdateDEX: "" /* gas limit for dex update transaction */, + gasUpdateMaster: "" /* gas limit for master node update transaction */, + gasUpdateVault: "" /* gas limit for vault update transaction */, + gasUpdateOracle: "" /* gas limit for oracle update transaction */, }) ``` diff --git a/bot/StateRelayerBot.ts b/bot/StateRelayerBot.ts index f20b71e..0e210ff 100644 --- a/bot/StateRelayerBot.ts +++ b/bot/StateRelayerBot.ts @@ -1,10 +1,11 @@ import { ApiPagedResponse } from '@defichain/whale-api-client'; import { PoolPairData } from '@defichain/whale-api-client/dist/api/poolpairs'; +import { PriceTicker } from '@defichain/whale-api-client/dist/api/prices'; import { getWhaleClient } from '@waveshq/walletkit-bot'; import { ethers } from 'ethers'; import { StateRelayer__factory } from '../generated'; -import { tranformPairData, transformDataMasternode, transformDataVault } from './utils/transformData'; +import { tranformPairData, transformDataMasternode, transformDataVault, transformOracleData } from './utils/transformData'; import { DataStore, MasterNodeData, StateRelayerHandlerProps, VaultData } from './utils/types'; const DENOMINATION = 'USDT'; @@ -71,6 +72,16 @@ export async function handler(props: StateRelayerHandlerProps): Promise = []; + let pagedPriceData: ApiPagedResponse = await client.prices.list(PAGESIZE); + rawPriceData = rawPriceData.concat(pagedPriceData); + while (pagedPriceData.hasNext) { + pagedPriceData = await client.paginate(pagedPriceData); + rawPriceData = rawPriceData.concat(pagedPriceData); + } + + const inputForOracleUpdate = transformOracleData(rawPriceData) // Data from vaults const dataVault = transformDataVault(statsData); @@ -99,9 +110,18 @@ export async function handler(props: StateRelayerHandlerProps): Promise ({ list: () => mockedPoolPairData, listDexPrices: () => mockedDexPricesData, }, + prices: { + list: () => mockedPriceData + } })), })); -describe('State Relayer Bot Tests', () => { +describe.only('State Relayer Bot Tests', () => { let startedHardhatContainer: StartedHardhatNetworkContainer; let hardhatNetwork: HardhatNetwork; let admin: ethers.Signer; @@ -59,6 +63,7 @@ describe('State Relayer Bot Tests', () => { ]), ], }); + proxy = StateRelayer__factory.connect((await stateRelayerProxy?.getAddress()) || '', bot); }); @@ -103,6 +108,16 @@ describe('State Relayer Bot Tests', () => { expect(dex[2]).toStrictEqual(expectedDexInfo.totalValueLocked); expect(dex[1]).toStrictEqual(expectedDexInfo.total24HVolume); + // Checking the oracle info + const testOceanData = await client.prices.list(200); + const dTSLA = await proxy.getOraclePairInfo('TSLA-USD'); + const expectedOracleInfo = transformOracleData(testOceanData); + const lastTSLAUSDInfo = expectedOracleInfo.oracleInfo[expectedOracleInfo.oracle.indexOf('TSLA-USD')]; + // for sure both two sides have the same type as bigint + expect(dTSLA[1].price).toStrictEqual(lastTSLAUSDInfo.price); + expect(dTSLA[1].oraclesActive).toStrictEqual(lastTSLAUSDInfo.oraclesActive); + expect(dTSLA[1].oraclesTotal).toStrictEqual(lastTSLAUSDInfo.oraclesTotal); + // Checking MasterNode information const receivedMasterNodeData = await proxy.getMasterNodeInfo(); expect(receivedMasterNodeData[1].totalValueLockedInMasterNodes).toStrictEqual( diff --git a/bot/test-i9n/mockData/oceanMockedData.ts b/bot/test-i9n/mockData/oceanMockedData.ts index 2c85112..a60d678 100644 --- a/bot/test-i9n/mockData/oceanMockedData.ts +++ b/bot/test-i9n/mockData/oceanMockedData.ts @@ -1180,3 +1180,431 @@ export const mockedDexPricesData = { export const expectedVaultData = transformDataVault(mockedStatsData); export const expectedMasterNodeData = transformDataMasternode(mockedStatsData); + +export const mockedPriceData = [ + { + "id": "MARA-USD", + "sort": "0000000f003bd7d9MARA-USD", + "price": { + "block": { + "hash": "4531df332a790a0e1de05e7b5fde103972e7c06a8efc87837bdfa0af173dbad8", + "height": 3921881, + "medianTime": 1713935549, + "time": 1713935780 + }, + "aggregated": { + "amount": "19.44008797", + "weightage": 100, + "oracles": { + "active": 10, + "total": 15 + } + }, + "currency": "USD", + "token": "MARA", + "id": "MARA-USD-3921881", + "key": "MARA-USD", + "sort": "662894bd003bd7d9" + } + }, + { + "id": "TSLA-USD", + "sort": "0000000d003bd7ddTSLA-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "144.67764212", + "weightage": 50, + "oracles": { + "active": 5, + "total": 13 + } + }, + "currency": "USD", + "token": "TSLA", + "id": "TSLA-USD-3921885", + "key": "TSLA-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "GOOGL-USD", + "sort": "0000000d003bd7ddGOOGL-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "158.25786341", + "weightage": 50, + "oracles": { + "active": 5, + "total": 13 + } + }, + "currency": "USD", + "token": "GOOGL", + "id": "GOOGL-USD-3921885", + "key": "GOOGL-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "GME-USD", + "sort": "0000000d003bd7ddGME-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "10.16005593", + "weightage": 50, + "oracles": { + "active": 5, + "total": 13 + } + }, + "currency": "USD", + "token": "GME", + "id": "GME-USD-3921885", + "key": "GME-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "FB-USD", + "sort": "0000000d003bd7ddFB-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "496.11049209", + "weightage": 50, + "oracles": { + "active": 5, + "total": 13 + } + }, + "currency": "USD", + "token": "FB", + "id": "FB-USD-3921885", + "key": "FB-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "COIN-USD", + "sort": "0000000d003bd7ddCOIN-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "236.43035335", + "weightage": 50, + "oracles": { + "active": 5, + "total": 13 + } + }, + "currency": "USD", + "token": "COIN", + "id": "COIN-USD-3921885", + "key": "COIN-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "AMZN-USD", + "sort": "0000000d003bd7ddAMZN-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "179.53823352", + "weightage": 50, + "oracles": { + "active": 5, + "total": 13 + } + }, + "currency": "USD", + "token": "AMZN", + "id": "AMZN-USD-3921885", + "key": "AMZN-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "AMD-USD", + "sort": "0000000d003bd7ddAMD-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "152.27074225", + "weightage": 50, + "oracles": { + "active": 5, + "total": 13 + } + }, + "currency": "USD", + "token": "AMD", + "id": "AMD-USD-3921885", + "key": "AMD-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "AAPL-USD", + "sort": "0000000d003bd7ddAAPL-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "166.90040006", + "weightage": 50, + "oracles": { + "active": 5, + "total": 13 + } + }, + "currency": "USD", + "token": "AAPL", + "id": "AAPL-USD-3921885", + "key": "AAPL-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "TWTR-USD", + "sort": "0000000d0024efe3TWTR-USD", + "price": { + "block": { + "hash": "1ad0ecbea0ab5a9e51b52f5f4218ff590e28dbc4f329bcddff8ebfda9e3b5e42", + "height": 2420707, + "medianTime": 1668729011, + "time": 1668729103 + }, + "aggregated": { + "amount": "53.85964879", + "weightage": 20, + "oracles": { + "active": 2, + "total": 13 + } + }, + "currency": "USD", + "token": "TWTR", + "id": "TWTR-USD-2420707", + "key": "TWTR-USD", + "sort": "6376c8b30024efe3" + } + }, + { + "id": "UBER-USD", + "sort": "0000000c003bd7ddUBER-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "70.79989869", + "weightage": 50, + "oracles": { + "active": 5, + "total": 12 + } + }, + "currency": "USD", + "token": "UBER", + "id": "UBER-USD-3921885", + "key": "UBER-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "OTGLY-USD", + "sort": "0000000c003bd7ddOTGLY-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "7.20996073", + "weightage": 50, + "oracles": { + "active": 5, + "total": 12 + } + }, + "currency": "USD", + "token": "OTGLY", + "id": "OTGLY-USD-3921885", + "key": "OTGLY-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "NVDA-USD", + "sort": "0000000c003bd7ddNVDA-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "824.24601933", + "weightage": 50, + "oracles": { + "active": 5, + "total": 12 + } + }, + "currency": "USD", + "token": "NVDA", + "id": "NVDA-USD-3921885", + "key": "NVDA-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "MSTR-USD", + "sort": "0000000c003bd7ddMSTR-USD", + "price": { + "block": { + "hash": "72af059d13c6539c00f3ab0dc8b4a1251120a8fbb121e30615ae1c350f43d132", + "height": 3921885, + "medianTime": 1713935772, + "time": 1713935853 + }, + "aggregated": { + "amount": "1338.62935899", + "weightage": 50, + "oracles": { + "active": 5, + "total": 12 + } + }, + "currency": "USD", + "token": "MSTR", + "id": "MSTR-USD-3921885", + "key": "MSTR-USD", + "sort": "6628959c003bd7dd" + } + }, + { + "id": "SQNXF-USD", + "sort": "0000000c0022f28eSQNXF-USD", + "price": { + "block": { + "hash": "5e12fcca9942743bcac1e6a52feda67b8e58dc8d9263738da757a8b324b6bfe9", + "height": 2290318, + "medianTime": 1664810328, + "time": 1664810451 + }, + "aggregated": { + "amount": "43.90986690", + "weightage": 50, + "oracles": { + "active": 5, + "total": 12 + } + }, + "currency": "USD", + "token": "SQNXF", + "id": "SQNXF-USD-2290318", + "key": "SQNXF-USD", + "sort": "633afd580022f28e" + } + }, + { + "id": "UUUU-USD", + "sort": "0000000a003bd7dfUUUU-USD", + "price": { + "block": { + "hash": "a30a194360b7c9b52e12ac6080f80d111db22e460a641e3e48537406ff0937f3", + "height": 3921887, + "medianTime": 1713935846, + "time": 1713935864 + }, + "aggregated": { + "amount": "5.24008517", + "weightage": 50, + "oracles": { + "active": 5, + "total": 10 + } + }, + "currency": "USD", + "token": "UUUU", + "id": "UUUU-USD-3921887", + "key": "UUUU-USD", + "sort": "662895e6003bd7df" + } + }, + { + "id": "UL-USD", + "sort": "0000000a003bd7dfUL-USD", + "price": { + "block": { + "hash": "a30a194360b7c9b52e12ac6080f80d111db22e460a641e3e48537406ff0937f3", + "height": 3921887, + "medianTime": 1713935846, + "time": 1713935864 + }, + "aggregated": { + "amount": "47.97904154", + "weightage": 50, + "oracles": { + "active": 5, + "total": 10 + } + }, + "currency": "USD", + "token": "UL", + "id": "UL-USD-3921887", + "key": "UL-USD", + "sort": "662895e6003bd7df" + } + } +] diff --git a/bot/utils/transformData.ts b/bot/utils/transformData.ts index fd82404..eafb4b5 100644 --- a/bot/utils/transformData.ts +++ b/bot/utils/transformData.ts @@ -1,8 +1,10 @@ +import { DexPricesResult, PoolPairData } from '@defichain/whale-api-client/dist/api/poolpairs'; +import { PriceTicker } from '@defichain/whale-api-client/dist/api/prices'; +import { StatsData } from '@defichain/whale-api-client/dist/api/stats'; import { BigNumber } from 'bignumber.js'; + import { IStateRelayer } from '../../generated'; import { MasterNodeData, PairData, VaultData } from './types'; -import { DexPricesResult, PoolPairData } from '@defichain/whale-api-client/dist/api/poolpairs'; -import { StatsData } from '@defichain/whale-api-client/dist/api/stats'; const DECIMALS = 18; const DENOMINATION = 'USDT'; @@ -105,3 +107,20 @@ export function tranformPairData( total24HVolume, }; } +export function transformOracleData(prices: PriceTicker[] ):{ + oracle: string[]; + oracleInfo: IStateRelayer.OracleInfoStruct[]; +} { + const data = prices.reduce((all, { id, price }) => ({ + ...all, + [id]: { + price: transformToBigInt(price.aggregated.amount, DECIMALS), + oraclesActive: transformToBigInt(price.aggregated.oracles.active, DECIMALS), + oraclesTotal:transformToBigInt(price.aggregated.oracles.total, DECIMALS) + } + }), {}) + return { + oracle: Object.keys(data), + oracleInfo: Object.values(data) + } +} diff --git a/bot/utils/types.ts b/bot/utils/types.ts index 9fc22ba..22a4ee1 100644 --- a/bot/utils/types.ts +++ b/bot/utils/types.ts @@ -22,4 +22,5 @@ export type StateRelayerHandlerProps = { gasUpdateDEX?: bigint; gasUpdateMaster?: bigint; gasUpdateVault?: bigint; + gasUpdateOracle?: bigint; }; diff --git a/contracts/IStateRelayer.sol b/contracts/IStateRelayer.sol index 9c9487e..dc1750e 100644 --- a/contracts/IStateRelayer.sol +++ b/contracts/IStateRelayer.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.18; interface IStateRelayer { - struct DEXInfo { // the price of the primary token in USDT/ USD uint256 primaryTokenPrice; @@ -51,6 +50,15 @@ interface IStateRelayer { uint256 tenYearLockedNoDecimals; } + struct OracleInfo { + // the price of the primary token in USDT/ USD + uint256 price; + // number of active oracles + uint256 oraclesActive; + // number of total oracles + uint256 oraclesTotal; + } + /** * @notice Getter function to get the information about dexes * @return Last time that information about dexes are updated @@ -81,4 +89,11 @@ interface IStateRelayer { */ function getMasterNodeInfo() external view returns (uint256, MasterNodeInformation memory); + /** + * @notice Getter function to get information about a certain oracle + * @param _pair The pair to get information about + * @return Last time that information about all dexes are updated + * @return Information about that pair + */ + function getOraclePairInfo(string calldata _pair) external view returns (uint256, OracleInfo memory); } diff --git a/contracts/StateRelayer.sol b/contracts/StateRelayer.sol index d275137..ffce701 100644 --- a/contracts/StateRelayer.sol +++ b/contracts/StateRelayer.sol @@ -8,8 +8,10 @@ import './IStateRelayer.sol'; error ERROR_IN_LOW_LEVEL_CALLS(); error NOT_BOT_ROLE_OR_NOT_IN_BATCH_CALL_IN_BOT(); error DEX_AND_DEXINFO_NOT_HAVE_THE_SAME_LENGTH(); +error ORACLE_AND_ORACLEINFO_NOT_HAVE_THE_SAME_LENGTH(); // @NOTE: if a uint256 is equal to 2**256 - 1, the value is not reliable and therefore should not be used +/// @custom:oz-upgrades-unsafe-allow constructor contract StateRelayer is UUPSUpgradeable, AccessControlUpgradeable, IStateRelayer { bytes32 public constant BOT_ROLE = keccak256('BOT_ROLE'); uint256 public constant DECIMALS = 18; @@ -20,12 +22,16 @@ contract StateRelayer is UUPSUpgradeable, AccessControlUpgradeable, IStateRelaye uint256 private total24HVolume; // integer value, no decimals uint256 private lastUpdatedVaultInfoTimestampNoDecimals; + // integer value, no decimals + uint256 private lastUpdatedOracleInfoTimestampNoDecimals; + // integer value, no decimals uint256 private lastUpdatedMasterNodeInfoTimestampNoDecimals; // integer value, no decimals uint256 private lastUpdatedDexInfoTimestampNoDecimals; bool private inBatchCallByBot; + mapping(string => OracleInfo) private OracleInfoMapping; mapping(string => DEXInfo) private DEXInfoMapping; VaultGeneralInformation private vaultInfo; @@ -33,8 +39,9 @@ contract StateRelayer is UUPSUpgradeable, AccessControlUpgradeable, IStateRelaye MasterNodeInformation private masterNodeInformation; event UpdateDEXInfo(); + event UpdateOracleInfo(); event UpdateVaultGeneralInformation(); - event UpdateMasterNodeInformation(); + event UpdateMasterNodeInformation(); function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {} @@ -100,7 +107,7 @@ contract StateRelayer is UUPSUpgradeable, AccessControlUpgradeable, IStateRelaye masterNodeInformation = _masterNodeInformation; uint256 _lastUpdatedMasterNodeInfoTimestamp = block.timestamp; lastUpdatedMasterNodeInfoTimestampNoDecimals = _lastUpdatedMasterNodeInfoTimestamp; - emit UpdateMasterNodeInformation(); + emit UpdateMasterNodeInformation(); } /** @@ -126,6 +133,21 @@ contract StateRelayer is UUPSUpgradeable, AccessControlUpgradeable, IStateRelaye inBatchCallByBot = false; } + /** + @notice Function to update the oracle info + @param _oracle The names of the pool pairs + @param _oracleInfo Information about the oracles + */ + function updateOracleInfo(string[] calldata _oracle, OracleInfo[] calldata _oracleInfo) external allowUpdate { + if (_oracle.length != _oracleInfo.length) revert ORACLE_AND_ORACLEINFO_NOT_HAVE_THE_SAME_LENGTH(); + for (uint256 i = 0; i < _oracle.length; ++i) { + OracleInfoMapping[_oracle[i]] = _oracleInfo[i]; + } + uint256 _lastUpdatedOracleInfo = block.timestamp; + lastUpdatedOracleInfoTimestampNoDecimals = _lastUpdatedOracleInfo; + emit UpdateOracleInfo(); + } + /** * @inheritdoc IStateRelayer */ @@ -153,4 +175,11 @@ contract StateRelayer is UUPSUpgradeable, AccessControlUpgradeable, IStateRelaye function getMasterNodeInfo() external view returns (uint256, MasterNodeInformation memory) { return (lastUpdatedMasterNodeInfoTimestampNoDecimals, masterNodeInformation); } + + /** + * @inheritdoc IStateRelayer + */ + function getOraclePairInfo(string calldata _pair) external view returns (uint256, OracleInfo memory) { + return (lastUpdatedOracleInfoTimestampNoDecimals, OracleInfoMapping[_pair]); + } } diff --git a/scripts/gasEstimationRealData.ts b/scripts/gasEstimationRealData.ts index b769a21..33d3811 100644 --- a/scripts/gasEstimationRealData.ts +++ b/scripts/gasEstimationRealData.ts @@ -5,12 +5,13 @@ import { BigNumber as BigFloatingNumber } from 'bignumber.js'; import { handler } from '../bot/StateRelayerBot'; import { deployContract } from '../tests/utils/deployment'; -// to run this file, run npx hardhat clean && npm i && npx hardhat run scripts/gasEstimation.ts -// estimation is 22.46 USD per month +// to run this file, run npx hardhat clean && npm i && npx hardhat run scripts/gasEstimationRealData.ts +// Average cost of updating all data in USD at one time is $0.015 async function estimateGasCost() { const dexesData: BigInt[] = []; const masterData: BigInt[] = []; const vaultData: BigInt[] = []; + const oracleData: BigInt[] = []; const { bot, stateRelayerProxy } = await deployContract(); for (let i = 0; i < 10; i += 1) { const data = await handler({ @@ -21,22 +22,25 @@ async function estimateGasCost() { signer: bot, }); if (data === undefined) break; - const { dexInfoTxReceipt, masterDataTxReceipt, vaultTxReceipt } = data; + const { dexInfoTxReceipt, masterDataTxReceipt, vaultTxReceipt, oracleInfoTxReceipt } = data; dexesData.push(dexInfoTxReceipt!.gasUsed); masterData.push(masterDataTxReceipt!.gasUsed); vaultData.push(vaultTxReceipt!.gasUsed); + oracleData.push(oracleInfoTxReceipt!.gasUsed) console.log('Successfully update ', i); await new Promise((r) => setTimeout(r, 10 * 1000)); } - console.log('Update DEXes'); + console.log('Update DEXes', dexesData); const averageCostUpdateDEX = await calculateAverageCost(dexesData.map((x) => x.valueOf())); + console.log('Update Oracles'); + const averageCostUpdateOracles = await calculateAverageCost(oracleData.map((x) => x.valueOf())); console.log('Update Masterdata'); const averageCostUpdateMasterData = await calculateAverageCost(masterData.map((x) => x.valueOf())); console.log('Update Vault data'); const averageCostUpdateVaultData = await calculateAverageCost(vaultData.map((x) => x.valueOf())); console.log( 'Average cost of updating all data in USD at one time is ', - averageCostUpdateDEX.plus(averageCostUpdateMasterData).plus(averageCostUpdateVaultData).toString(), + averageCostUpdateDEX.plus(averageCostUpdateOracles).plus(averageCostUpdateMasterData).plus(averageCostUpdateVaultData).toString(), ); } @@ -80,4 +84,4 @@ async function calculateAverageCost(arr: bigint[]): Promise { return laterAverageCostUpdateInUSD; } -estimateGasCost(); +estimateGasCost(); \ No newline at end of file diff --git a/tests/StateRelayer.data.spec.ts b/tests/StateRelayer.data.spec.ts index ca7d58f..5d72acd 100644 --- a/tests/StateRelayer.data.spec.ts +++ b/tests/StateRelayer.data.spec.ts @@ -2,12 +2,14 @@ import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers'; import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; import { expect } from 'chai'; -import { StateRelayer, IStateRelayer, StateRelayer__factory } from '../generated'; +import { StateRelayer, StateRelayer__factory } from '../generated'; +import { IStateRelayer } from '../generated/contracts/StateRelayer' import { deployContract } from './utils/deployment'; type MasterNodeInformationStruct = IStateRelayer.MasterNodeInformationStruct; type VaultGeneralInformation = IStateRelayer.VaultGeneralInformationStruct; type DexInfo = IStateRelayer.DEXInfoStruct; +type OracleInfo = IStateRelayer.OracleInfoStruct; describe('State relayer contract data tests', () => { let stateRelayerProxy: StateRelayer; @@ -15,9 +17,12 @@ describe('State relayer contract data tests', () => { let user: SignerWithAddress; let admin: SignerWithAddress; + before(async ()=> { + ({ stateRelayerProxy, admin, bot, user } = await loadFixture(deployContract)); + }) + describe('Test update master node data', () => { it('Should successfully set master node data', async () => { - ({ stateRelayerProxy, bot } = await loadFixture(deployContract)); const masterNodeData: MasterNodeInformationStruct = { totalValueLockedInMasterNodes: 108, zeroYearLockedNoDecimals: 101, @@ -33,7 +38,6 @@ describe('State relayer contract data tests', () => { }); it('Should successfully revert if the signer is not `bot`', async () => { - ({ stateRelayerProxy, user } = await loadFixture(deployContract)); const masterNodeData: MasterNodeInformationStruct = { totalValueLockedInMasterNodes: 108, zeroYearLockedNoDecimals: 101, @@ -48,7 +52,6 @@ describe('State relayer contract data tests', () => { describe('Test update vault data', () => { it('Should successfully set vault node data', async () => { - ({ stateRelayerProxy, bot } = await loadFixture(deployContract)); const vaultInformationData: VaultGeneralInformation = { noOfVaultsNoDecimals: 2, totalLoanValue: 1000, @@ -65,7 +68,6 @@ describe('State relayer contract data tests', () => { }); it('Should successfully revert if the signer is not `bot`', async () => { - ({ stateRelayerProxy, user } = await loadFixture(deployContract)); const vaultInformationData: VaultGeneralInformation = { noOfVaultsNoDecimals: 2, totalLoanValue: 1000, @@ -81,7 +83,6 @@ describe('State relayer contract data tests', () => { describe('Test update dexs data ', () => { it('Should successfully set dexs data', async () => { - ({ stateRelayerProxy, bot } = await loadFixture(deployContract)); const totalValueLockInPoolPair = 76354685; const total24HVolume = 65738274; const dexDataEth: DexInfo = { @@ -120,7 +121,6 @@ describe('State relayer contract data tests', () => { }); it('Should successfully revert if the signer is not `bot`', async () => { - ({ stateRelayerProxy, user } = await loadFixture(deployContract)); const dexDataEth: DexInfo = { primaryTokenPrice: 113, @@ -138,7 +138,6 @@ describe('State relayer contract data tests', () => { }); it('Should successfully revert if there is a mismatch between the length of _dexInfo and _dex', async () => { - ({ stateRelayerProxy, bot } = await loadFixture(deployContract)); const totalValueLockInPoolPair = 76354685; const total24HVolume = 65738274; const dexDataEth: DexInfo = { @@ -169,9 +168,65 @@ describe('State relayer contract data tests', () => { }); }); + describe('Test update oracle data ', () => { + it('Should successfully set oracle data', async () => { + const oracleDataEth: OracleInfo = { + price: 111, + oraclesActive: 5, + oraclesTotal: 13, + }; + const oracleDataBtc: OracleInfo = { + price: 222, + oraclesActive: 5, + oraclesTotal: 13, + }; + const oracleData: OracleInfo[] = [oracleDataEth, oracleDataBtc]; + const symbols: string[] = ['eth-usd', 'btc-usd']; + await expect( + stateRelayerProxy.connect(bot).updateOracleInfo(symbols, oracleData), + ).to.emit(stateRelayerProxy, 'UpdateOracleInfo'); + // Getting ETH Oracle Data + const receivedEThOracleData = await stateRelayerProxy.getOraclePairInfo(symbols[0]); + // Testing that the received is as expected as oracleDataEth + expect(receivedEThOracleData[1].toString()).to.equal(Object.values(oracleDataEth).toString()); + // Getting BTC Oracle Data + const receivedBtcOracleData = await stateRelayerProxy.getOraclePairInfo(symbols[1]); + // Testing that the received is as expected as oracleDataBtc + expect(receivedBtcOracleData[1].toString()).to.equal(Object.values(oracleDataBtc).toString()); + }); + + it('Should successfully revert if the signer is not `bot`', async () => { + const oracleDataEth: OracleInfo = { + price: 111, + oraclesActive: 5, + oraclesTotal: 13, + }; + await expect( + stateRelayerProxy.connect(user).updateOracleInfo(['eth-usd'], [oracleDataEth]), + ).to.be.revertedWithCustomError(stateRelayerProxy, 'NOT_BOT_ROLE_OR_NOT_IN_BATCH_CALL_IN_BOT'); + }); + + it('Should successfully revert if there is a mismatch between the length of _oracleInfo and _oracle', async () => { + const oracleDataEth: OracleInfo = { + price: 111, + oraclesActive: 5, + oraclesTotal: 13, + }; + const oracleDataBtc: OracleInfo = { + price: 222, + oraclesActive: 5, + oraclesTotal: 13, + }; + const oracleData: OracleInfo[] = [oracleDataEth, oracleDataBtc]; + const symbols: string[] = ['eth-usd', 'btc-usd', 'dfi-usd']; + await expect( + stateRelayerProxy.connect(bot).updateOracleInfo(symbols, oracleData), + ).to.revertedWithCustomError(stateRelayerProxy, 'ORACLE_AND_ORACLEINFO_NOT_HAVE_THE_SAME_LENGTH'); + }); + }); + describe('Test batch call', () => { it('Should be able to update in batch ', async () => { - ({ stateRelayerProxy, bot } = await loadFixture(deployContract)); // Master node data const masterNodeData: MasterNodeInformationStruct = { totalValueLockedInMasterNodes: 108, @@ -227,12 +282,31 @@ describe('State relayer contract data tests', () => { 2, ]); + const oracleDataEth: OracleInfo = { + price: 12, + oraclesActive: 53, + oraclesTotal: 113, + }; + const oracleDataBtc: OracleInfo = { + price: 212, + oraclesActive: 52, + oraclesTotal: 123, + }; + + const oracleData: OracleInfo[] = [oracleDataEth, oracleDataBtc]; + const oracleSymbols: string[] = ['eth-usd', 'btc-usd']; + const callDataForUpdatingOracleInfos = stateRelayerInterface.encodeFunctionData('updateOracleInfo', [ + oracleSymbols, + oracleData + ]); + await stateRelayerProxy .connect(bot) .batchCallByBot([ callDataForUpdatingMasterNodeData, callDataForUpdatingVaultInformation, callDataForUpdatingDexInfos, + callDataForUpdatingOracleInfos ]); const receivedMasterNodeData = await stateRelayerProxy.getMasterNodeInfo(); expect(receivedMasterNodeData[1].toString()).to.equal(Object.values(masterNodeData).toString()); @@ -246,10 +320,18 @@ describe('State relayer contract data tests', () => { const receivedBtcDexData = await stateRelayerProxy.getDexPairInfo(symbols[1]); // Testing that the received is as expected as dexDataBtc expect(receivedBtcDexData[1].toString()).to.equal(Object.values(dexDataBtc).toString()); + + // Getting ETH Oracle Data + const receivedEThDexOracleData = await stateRelayerProxy.getOraclePairInfo(oracleSymbols[0]); + // Testing that the received is as expected as oracleDataEth + expect(receivedEThDexOracleData[1].toString()).to.equal(Object.values(oracleDataEth).toString()); + // Getting BTC Oracle Data + const receivedBtcOracleData = await stateRelayerProxy.getOraclePairInfo(oracleSymbols[1]); + // Testing that the received is as expected as oracleDataBtc + expect(receivedBtcOracleData[1].toString()).to.equal(Object.values(oracleDataBtc).toString()); }); it('Should fail when the caller is not authorized ', async () => { - ({ stateRelayerProxy, admin } = await loadFixture(deployContract)); const masterNodeData: MasterNodeInformationStruct = { totalValueLockedInMasterNodes: 108, @@ -270,7 +352,6 @@ describe('State relayer contract data tests', () => { }); it('Should fail when the batch call tries to use authorized function', async () => { - ({ stateRelayerProxy, bot } = await loadFixture(deployContract)); const stateRelayerInterface = StateRelayer__factory.createInterface(); const encodedGrantRole = stateRelayerInterface.encodeFunctionData('grantRole', [ @@ -285,7 +366,6 @@ describe('State relayer contract data tests', () => { }); it('Should fail when not granting state relayer the bot_role and then perform recursive batch call', async () => { - ({ stateRelayerProxy, bot } = await loadFixture(deployContract)); const stateRelayerInterface = StateRelayer__factory.createInterface(); const encodedGrantRole = stateRelayerInterface.encodeFunctionData('batchCallByBot', [ @@ -298,7 +378,6 @@ describe('State relayer contract data tests', () => { }); it('Return right error when not using the right call data', async () => { - ({ stateRelayerProxy, bot } = await loadFixture(deployContract)); // check if 0xffffffff is among the signatures of the contract expect(StateRelayer__factory.createInterface().hasFunction('0xffffffff')).to.be.false; @@ -309,4 +388,4 @@ describe('State relayer contract data tests', () => { ); }); }); -}); +}); \ No newline at end of file diff --git a/tests/StateRelayer.deployment.spec.ts b/tests/StateRelayer.deployment.spec.ts index c23e5b7..001b7e6 100644 --- a/tests/StateRelayer.deployment.spec.ts +++ b/tests/StateRelayer.deployment.spec.ts @@ -1,5 +1,5 @@ -import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers'; +import { loadFixture } from '@nomicfoundation/hardhat-network-helpers'; import { expect } from 'chai'; import { StateRelayer } from '../generated'; From ecd385c0dcda617f1beba7cdd804c305b628c806 Mon Sep 17 00:00:00 2001 From: Harsh Date: Mon, 29 Apr 2024 13:01:15 +0530 Subject: [PATCH 2/4] chore --- bot/StateRelayerBot.ts | 9 ++++----- bot/test-i9n/StateRelayerBot.unit.ts | 2 +- scripts/gasEstimationRealData.ts | 3 +-- tests/StateRelayer.data.spec.ts | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/bot/StateRelayerBot.ts b/bot/StateRelayerBot.ts index 0e210ff..ec66bbe 100644 --- a/bot/StateRelayerBot.ts +++ b/bot/StateRelayerBot.ts @@ -98,17 +98,21 @@ export async function handler(props: StateRelayerHandlerProps): Promise ({ })), })); -describe.only('State Relayer Bot Tests', () => { +describe('State Relayer Bot Tests', () => { let startedHardhatContainer: StartedHardhatNetworkContainer; let hardhatNetwork: HardhatNetwork; let admin: ethers.Signer; diff --git a/scripts/gasEstimationRealData.ts b/scripts/gasEstimationRealData.ts index 33d3811..0669aff 100644 --- a/scripts/gasEstimationRealData.ts +++ b/scripts/gasEstimationRealData.ts @@ -30,7 +30,6 @@ async function estimateGasCost() { console.log('Successfully update ', i); await new Promise((r) => setTimeout(r, 10 * 1000)); } - console.log('Update DEXes', dexesData); const averageCostUpdateDEX = await calculateAverageCost(dexesData.map((x) => x.valueOf())); console.log('Update Oracles'); const averageCostUpdateOracles = await calculateAverageCost(oracleData.map((x) => x.valueOf())); @@ -84,4 +83,4 @@ async function calculateAverageCost(arr: bigint[]): Promise { return laterAverageCostUpdateInUSD; } -estimateGasCost(); \ No newline at end of file +estimateGasCost(); diff --git a/tests/StateRelayer.data.spec.ts b/tests/StateRelayer.data.spec.ts index 5d72acd..c823443 100644 --- a/tests/StateRelayer.data.spec.ts +++ b/tests/StateRelayer.data.spec.ts @@ -388,4 +388,4 @@ describe('State relayer contract data tests', () => { ); }); }); -}); \ No newline at end of file +}); From e4cdd30494abc327dd7e0a468fcdc34a64943fc2 Mon Sep 17 00:00:00 2001 From: Harsh Date: Mon, 29 Apr 2024 14:39:51 +0530 Subject: [PATCH 3/4] added flag for enable/disable oracle update --- README.md | 1 + bot/StateRelayerBot.ts | 42 +++++++++++++++------------- bot/test-i9n/StateRelayerBot.unit.ts | 1 + bot/utils/types.ts | 1 + scripts/gasEstimationRealData.ts | 1 + 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 472c21b..bda6823 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ handler({ envNetwork: EnvironmentNetwork.MainNet, contractAddress: "" /* YOUR STATE RELAYER PROXY ADDRESS */, signer: "" /* signer object */, + enableOracleUpdate: false /* enable or disable oracle info update */ gasUpdateDEX: "" /* gas limit for dex update transaction */, gasUpdateMaster: "" /* gas limit for master node update transaction */, gasUpdateVault: "" /* gas limit for vault update transaction */, diff --git a/bot/StateRelayerBot.ts b/bot/StateRelayerBot.ts index ec66bbe..574c5b1 100644 --- a/bot/StateRelayerBot.ts +++ b/bot/StateRelayerBot.ts @@ -12,7 +12,7 @@ const DENOMINATION = 'USDT'; const PAGESIZE = 50; export async function handler(props: StateRelayerHandlerProps): Promise { - const { urlNetwork, envNetwork, signer, contractAddress } = props; + const { urlNetwork, envNetwork, signer, contractAddress, enableOracleUpdate } = props; const stateRelayerContract = StateRelayer__factory.connect(contractAddress, signer); const dataStore = {} as DataStore; try { @@ -72,16 +72,6 @@ export async function handler(props: StateRelayerHandlerProps): Promise = []; - let pagedPriceData: ApiPagedResponse = await client.prices.list(PAGESIZE); - rawPriceData = rawPriceData.concat(pagedPriceData); - while (pagedPriceData.hasNext) { - pagedPriceData = await client.paginate(pagedPriceData); - rawPriceData = rawPriceData.concat(pagedPriceData); - } - - const inputForOracleUpdate = transformOracleData(rawPriceData) // Data from vaults const dataVault = transformDataVault(statsData); @@ -114,13 +104,27 @@ export async function handler(props: StateRelayerHandlerProps): Promise = []; + let pagedPriceData: ApiPagedResponse = await client.prices.list(PAGESIZE); + rawPriceData = rawPriceData.concat(pagedPriceData); + while (pagedPriceData.hasNext) { + pagedPriceData = await client.paginate(pagedPriceData); + rawPriceData = rawPriceData.concat(pagedPriceData); + } + + const inputForOracleUpdate = transformOracleData(rawPriceData) + // Update Oracle information + oracleInfoTx = await stateRelayerContract.updateOracleInfo( + inputForOracleUpdate.oracle, + inputForOracleUpdate.oracleInfo, + { nonce: nonce + 3, gasLimit: props.gasUpdateOracle }, + ); + console.log('Hash of oracle update transaction', oracleInfoTx.hash); + + } if (!props.testGasCost) { return { @@ -136,7 +140,7 @@ export async function handler(props: StateRelayerHandlerProps): Promise { test('Successfully set the dexInfo data', async () => { const output = await handler({ testGasCost: false, + enableOracleUpdate: true, envNetwork: EnvironmentNetwork.LocalPlayground, urlNetwork: '', contractAddress: await proxy.getAddress(), diff --git a/bot/utils/types.ts b/bot/utils/types.ts index 22a4ee1..10dde7d 100644 --- a/bot/utils/types.ts +++ b/bot/utils/types.ts @@ -19,6 +19,7 @@ export type StateRelayerHandlerProps = { envNetwork: EnvironmentNetwork; contractAddress: string; signer: ethers.Signer; + enableOracleUpdate?: boolean; gasUpdateDEX?: bigint; gasUpdateMaster?: bigint; gasUpdateVault?: bigint; diff --git a/scripts/gasEstimationRealData.ts b/scripts/gasEstimationRealData.ts index 0669aff..4bad09d 100644 --- a/scripts/gasEstimationRealData.ts +++ b/scripts/gasEstimationRealData.ts @@ -19,6 +19,7 @@ async function estimateGasCost() { urlNetwork: 'https://ocean.defichain.com/', envNetwork: EnvironmentNetwork.MainNet, contractAddress: await stateRelayerProxy.getAddress(), // Proxy contract address + enableOracleUpdate: true, signer: bot, }); if (data === undefined) break; From 45a905a00a382840c666e6f212e278b3e4b5bd8f Mon Sep 17 00:00:00 2001 From: Harsh Date: Mon, 29 Apr 2024 15:29:41 +0530 Subject: [PATCH 4/4] chore --- hardhat.config.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index d2b1257..d11b410 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -83,10 +83,10 @@ const config: HardhatUserConfig = { allowUnlimitedContractSize: true, }, DMCTestnet: { - url: 'https://testnet-dmc.mydefichain.com:20551/', + url: 'https://eth.testnet.ocean.jellyfishsdk.com', accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], // ledgerAccounts: ['first EVM address of your ledger'], - chainId: 1133, + chainId: 1131, }, sepolia: { url: process.env.SEPOLIA_URL || '', @@ -102,10 +102,10 @@ const config: HardhatUserConfig = { customChains: [ { network: 'DMCTestnet', - chainId: 1133, + chainId: 1131, urls: { - apiURL: 'https://testnet-dmc.mydefichain.com:8444/api', - browserURL: 'https://testnet-dmc.mydefichain.com:8444', + apiURL: 'https://blockscout.testnet.ocean.jellyfishsdk.com/api', + browserURL: 'https://blockscout.testnet.ocean.jellyfishsdk.com', }, }, ],