From 80cf72f96abd675082a3865090d3308f58caa1ec Mon Sep 17 00:00:00 2001 From: Atul Agarwal <21087753+asquare08@users.noreply.github.com> Date: Thu, 4 Apr 2024 22:01:53 +0530 Subject: [PATCH 1/2] add support for custom oracle --- precompile/contracts/bibliophile/oracle.go | 29 +++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/precompile/contracts/bibliophile/oracle.go b/precompile/contracts/bibliophile/oracle.go index 633e9f0944..3cec3b4a86 100644 --- a/precompile/contracts/bibliophile/oracle.go +++ b/precompile/contracts/bibliophile/oracle.go @@ -16,6 +16,8 @@ var ( AGGREGATOR_MAP_SLOT int64 = 1 RED_STONE_ADAPTER_SLOT int64 = 2 + CUSTOM_ORACLE_ROUND_ID_SLOT int64 = 0 + CUSTOM_ORACLE_ENTRIES_SLOT int64 = 1 ) const ( @@ -30,14 +32,18 @@ func getUnderlyingPrice(stateDB contract.StateDB, market common.Address) *big.In func getUnderlyingPrice_(stateDB contract.StateDB, underlying common.Address) *big.Int { oracle := getOracleAddress(stateDB) // this comes from margin account feedId := getRedStoneFeedId(stateDB, oracle, underlying) + aggregator := getAggregatorAddress(stateDB, oracle, underlying) if feedId.Big().Sign() != 0 { // redstone oracle is configured for this market redStoneAdapter := getRedStoneAdapterAddress(stateDB, oracle) redstonePrice := getRedStonePrice(stateDB, redStoneAdapter, feedId) // log.Info("redstone-price", "amm", market, "price", redstonePrice) return redstonePrice + } else if aggregator.Big().Sign() != 0 { // double check the assumption that no other aggregator with non-zero feedId is configured before + // custom oracle is configured for this market + return getCustomOraclePrice(stateDB, aggregator) } - // red stone oracle is not enabled for this market, we use the default TestOracle + // neither red stone nor custom oracle is enabled for this market, we use the default TestOracle slot := crypto.Keccak256(append(common.LeftPadBytes(underlying.Bytes(), 32), common.BigToHash(big.NewInt(TEST_ORACLE_PRICES_MAPPING_SLOT)).Bytes()...)) return fromTwosComplement(stateDB.GetState(oracle, common.BytesToHash(slot)).Bytes()) } @@ -65,7 +71,24 @@ func getlatestRoundId(stateDB contract.StateDB, adapterAddress common.Address) * return fromTwosComplement(stateDB.GetState(adapterAddress, RED_STONE_LATEST_ROUND_ID_STORAGE_LOCATION).Bytes()) } +func aggregatorMapSlot(underlying common.Address) *big.Int { + return new(big.Int).SetBytes(crypto.Keccak256(append(common.LeftPadBytes(underlying.Bytes(), 32), common.BigToHash(big.NewInt(AGGREGATOR_MAP_SLOT)).Bytes()...))) +} + func getRedStoneFeedId(stateDB contract.StateDB, oracle, underlying common.Address) common.Hash { - aggregatorMapSlot := crypto.Keccak256(append(common.LeftPadBytes(underlying.Bytes(), 32), common.BigToHash(big.NewInt(AGGREGATOR_MAP_SLOT)).Bytes()...)) - return stateDB.GetState(oracle, common.BytesToHash(aggregatorMapSlot)) + aggregatorMapSlot := aggregatorMapSlot(underlying) + return stateDB.GetState(oracle, common.BigToHash(aggregatorMapSlot)) +} + +func getAggregatorAddress(stateDB contract.StateDB, oracle, underlying common.Address) common.Address { + aggregatorMapSlot := aggregatorMapSlot(underlying) + aggregatorSlot := hu.Add(aggregatorMapSlot, big.NewInt(1)) + return common.BytesToAddress(stateDB.GetState(oracle, common.BigToHash(aggregatorSlot)).Bytes()) +} + +func getCustomOraclePrice(stateDB contract.StateDB, aggregator common.Address) *big.Int { + roundId := stateDB.GetState(aggregator, common.BigToHash(big.NewInt(CUSTOM_ORACLE_ROUND_ID_SLOT))).Bytes() + entriesSlot := new(big.Int).SetBytes(crypto.Keccak256(append(common.LeftPadBytes(roundId, 32), common.BigToHash(big.NewInt(CUSTOM_ORACLE_ENTRIES_SLOT)).Bytes()...))) + priceSlot := hu.Add(entriesSlot, big.NewInt(1)) + return hu.Div(fromTwosComplement(stateDB.GetState(aggregator, common.BigToHash(priceSlot)).Bytes()), big.NewInt(100)) // we use 6 decimals precision everywhere } From fd72b1c0f42a817c13b149c96a5f4da8599fc50b Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Fri, 5 Apr 2024 09:22:07 +0700 Subject: [PATCH 2/2] add log, clean comment --- precompile/contracts/bibliophile/oracle.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/precompile/contracts/bibliophile/oracle.go b/precompile/contracts/bibliophile/oracle.go index 3cec3b4a86..50634f16bd 100644 --- a/precompile/contracts/bibliophile/oracle.go +++ b/precompile/contracts/bibliophile/oracle.go @@ -8,14 +8,15 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" ) var ( RED_STONE_VALUES_MAPPING_STORAGE_LOCATION = common.HexToHash("0x4dd0c77efa6f6d590c97573d8c70b714546e7311202ff7c11c484cc841d91bfc") // keccak256("RedStone.oracleValuesMapping"); RED_STONE_LATEST_ROUND_ID_STORAGE_LOCATION = common.HexToHash("0xc68d7f1ee07d8668991a8951e720010c9d44c2f11c06b5cac61fbc4083263938") // keccak256("RedStone.latestRoundId"); - AGGREGATOR_MAP_SLOT int64 = 1 - RED_STONE_ADAPTER_SLOT int64 = 2 + AGGREGATOR_MAP_SLOT int64 = 1 + RED_STONE_ADAPTER_SLOT int64 = 2 CUSTOM_ORACLE_ROUND_ID_SLOT int64 = 0 CUSTOM_ORACLE_ENTRIES_SLOT int64 = 1 ) @@ -31,19 +32,26 @@ func getUnderlyingPrice(stateDB contract.StateDB, market common.Address) *big.In func getUnderlyingPrice_(stateDB contract.StateDB, underlying common.Address) *big.Int { oracle := getOracleAddress(stateDB) // this comes from margin account + + // 1. Check for redstone feed id feedId := getRedStoneFeedId(stateDB, oracle, underlying) - aggregator := getAggregatorAddress(stateDB, oracle, underlying) if feedId.Big().Sign() != 0 { // redstone oracle is configured for this market redStoneAdapter := getRedStoneAdapterAddress(stateDB, oracle) redstonePrice := getRedStonePrice(stateDB, redStoneAdapter, feedId) - // log.Info("redstone-price", "amm", market, "price", redstonePrice) return redstonePrice - } else if aggregator.Big().Sign() != 0 { // double check the assumption that no other aggregator with non-zero feedId is configured before + } + + // 2. Check for custom oracle + aggregator := getAggregatorAddress(stateDB, oracle, underlying) + if aggregator.Big().Sign() != 0 { // custom oracle is configured for this market - return getCustomOraclePrice(stateDB, aggregator) + price := getCustomOraclePrice(stateDB, aggregator) + log.Info("custom-oracle-price", "underlying", underlying, "price", price) + return price } - // neither red stone nor custom oracle is enabled for this market, we use the default TestOracle + + // 3. neither red stone nor custom oracle is enabled for this market, we use the default TestOracle slot := crypto.Keccak256(append(common.LeftPadBytes(underlying.Bytes(), 32), common.BigToHash(big.NewInt(TEST_ORACLE_PRICES_MAPPING_SLOT)).Bytes()...)) return fromTwosComplement(stateDB.GetState(oracle, common.BytesToHash(slot)).Bytes()) }