From 2a37207eccef561003a87f44ca86e79b712b45f4 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Mon, 25 Sep 2023 14:54:07 +0100 Subject: [PATCH 01/11] getNormalisedMargin --- plugin/evm/orderbook/config_service.go | 6 ++ .../evm/orderbook/hubbleutils/hubble_math.go | 8 +++ .../evm/orderbook/hubbleutils/margin_math.go | 30 ++++++++++ plugin/evm/orderbook/liquidations.go | 16 +++-- plugin/evm/orderbook/matching_pipeline.go | 9 ++- plugin/evm/orderbook/memory_database.go | 6 +- precompile/contracts/bibliophile/amm.go | 46 --------------- precompile/contracts/bibliophile/api.go | 4 +- .../contracts/bibliophile/clearing_house.go | 9 ++- precompile/contracts/bibliophile/client.go | 6 ++ .../contracts/bibliophile/client_mock.go | 15 +++++ .../contracts/bibliophile/margin_account.go | 48 +++++++++++++-- precompile/contracts/bibliophile/oracle.go | 59 +++++++++++++++++++ precompile/contracts/bibliophile/redstone.go | 25 -------- 14 files changed, 198 insertions(+), 89 deletions(-) create mode 100644 plugin/evm/orderbook/hubbleutils/margin_math.go create mode 100644 precompile/contracts/bibliophile/oracle.go delete mode 100644 precompile/contracts/bibliophile/redstone.go diff --git a/plugin/evm/orderbook/config_service.go b/plugin/evm/orderbook/config_service.go index cc0477ca8b..f5d0660c06 100644 --- a/plugin/evm/orderbook/config_service.go +++ b/plugin/evm/orderbook/config_service.go @@ -5,6 +5,7 @@ import ( "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/core/state" + hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" "github.com/ava-labs/subnet-evm/precompile/contracts/bibliophile" "github.com/ethereum/go-ethereum/common" ) @@ -17,6 +18,7 @@ type IConfigService interface { getMinSizeRequirement(market Market) *big.Int GetActiveMarketsCount() int64 GetUnderlyingPrices() []*big.Int + GetCollaterals() []hu.Collateral GetLastPremiumFraction(market Market, trader *common.Address) *big.Int GetCumulativePremiumFraction(market Market) *big.Int GetAcceptableBounds(market Market) (*big.Int, *big.Int) @@ -82,6 +84,10 @@ func (cs *ConfigService) GetUnderlyingPrices() []*big.Int { return bibliophile.GetUnderlyingPrices(cs.getStateAtCurrentBlock()) } +func (cs *ConfigService) GetCollaterals() []hu.Collateral { + return bibliophile.GetCollaterals(cs.getStateAtCurrentBlock()) +} + func (cs *ConfigService) GetLastPremiumFraction(market Market, trader *common.Address) *big.Int { markets := bibliophile.GetMarkets(cs.getStateAtCurrentBlock()) return bibliophile.GetLastPremiumFraction(cs.getStateAtCurrentBlock(), markets[market], trader) diff --git a/plugin/evm/orderbook/hubbleutils/hubble_math.go b/plugin/evm/orderbook/hubbleutils/hubble_math.go index 3960ed77f0..5bf10d7d7f 100644 --- a/plugin/evm/orderbook/hubbleutils/hubble_math.go +++ b/plugin/evm/orderbook/hubbleutils/hubble_math.go @@ -57,3 +57,11 @@ func RoundOff(a, b *big.Int) *big.Int { func Mod(a, b *big.Int) *big.Int { return new(big.Int).Mod(a, b) } + +func Scale(a *big.Int, decimals uint8) *big.Int { + return Mul(a, new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals)), nil)) +} + +func Unscale(a *big.Int, decimals uint8) *big.Int { + return Div(a, new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals)), nil)) +} diff --git a/plugin/evm/orderbook/hubbleutils/margin_math.go b/plugin/evm/orderbook/hubbleutils/margin_math.go new file mode 100644 index 0000000000..b3ea25de8e --- /dev/null +++ b/plugin/evm/orderbook/hubbleutils/margin_math.go @@ -0,0 +1,30 @@ +package hubbleutils + +import ( + "math/big" +) + +type Collateral struct { + Price *big.Int // scaled by 1e6 + Weight *big.Int // scaled by 1e6 + Decimals uint8 +} + +func GetNormalizedMargin(assets []Collateral, margin []*big.Int) *big.Int { + weighted, _ := WeightedAndSpotCollateral(assets, margin) + return weighted +} + +func WeightedAndSpotCollateral(assets []Collateral, margin []*big.Int) (weighted, spot *big.Int) { + weighted = big.NewInt(0) + spot = big.NewInt(0) + for i, asset := range assets { + if margin[i].Sign() == 0 { + continue + } + numerator := Mul(margin[i], asset.Price) // margin[i] is scaled by asset.Decimal + spot.Add(spot, Unscale(numerator, asset.Decimals)) + weighted.Add(weighted, Unscale(Mul(numerator, asset.Weight), asset.Decimals+6)) + } + return weighted, spot +} diff --git a/plugin/evm/orderbook/liquidations.go b/plugin/evm/orderbook/liquidations.go index c59347e597..7310fa8126 100644 --- a/plugin/evm/orderbook/liquidations.go +++ b/plugin/evm/orderbook/liquidations.go @@ -22,8 +22,8 @@ func (liq LiquidablePosition) GetUnfilledSize() *big.Int { return big.NewInt(0).Sub(liq.Size, liq.FilledSize) } -func calcMarginFraction(trader *Trader, pendingFunding *big.Int, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, markets []Market) *big.Int { - margin := new(big.Int).Sub(getNormalisedMargin(trader), pendingFunding) +func calcMarginFraction(trader *Trader, pendingFunding *big.Int, oraclePrices map[Market]*big.Int, assets []hu.Collateral, lastPrices map[Market]*big.Int, markets []Market) *big.Int { + margin := new(big.Int).Sub(getNormalisedMargin(trader, assets), pendingFunding) notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(trader, margin, Maintenance_Margin, oraclePrices, lastPrices, markets) if notionalPosition.Sign() == 0 { return big.NewInt(math.MaxInt64) @@ -39,9 +39,13 @@ func sortLiquidableSliceByMarginFraction(positions []LiquidablePosition) []Liqui return positions } -func getNormalisedMargin(trader *Trader) *big.Int { - return trader.Margin.Deposited[HUSD] - // @todo: Write for multi-collateral +func getNormalisedMargin(trader *Trader, assets []hu.Collateral) *big.Int { + numAssets := len(assets) + margin := make([]*big.Int, numAssets) + for i := 0; i < numAssets; i++ { + margin[i] = trader.Margin.Deposited[Collateral(i)] + } + return hu.GetNormalizedMargin(assets, margin) } func getTotalFunding(trader *Trader, markets []Market) *big.Int { @@ -55,7 +59,7 @@ func getTotalFunding(trader *Trader, markets []Market) *big.Int { } func getNotionalPosition(price *big.Int, size *big.Int) *big.Int { - return big.NewInt(0).Abs(hu.Div1e18(big.NewInt(0).Mul(size, price))) + return big.NewInt(0).Abs(hu.Div1e18(hu.Mul(size, price))) } type MarginMode uint8 diff --git a/plugin/evm/orderbook/matching_pipeline.go b/plugin/evm/orderbook/matching_pipeline.go index 29ca32eb96..8de8f3648d 100644 --- a/plugin/evm/orderbook/matching_pipeline.go +++ b/plugin/evm/orderbook/matching_pipeline.go @@ -6,6 +6,8 @@ import ( "sync" "time" + hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" + b "github.com/ava-labs/subnet-evm/precompile/contracts/bibliophile" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -71,9 +73,10 @@ func (pipeline *MatchingPipeline) Run(blockNumber *big.Int) bool { // fetch the underlying price and run the matching engine underlyingPrices := pipeline.GetUnderlyingPrices() + assets := pipeline.GetCollaterals() // build trader map - liquidablePositions, ordersToCancel := pipeline.db.GetNaughtyTraders(underlyingPrices, markets) + liquidablePositions, ordersToCancel := pipeline.db.GetNaughtyTraders(underlyingPrices, assets, markets) cancellableOrderIds := pipeline.cancelLimitOrders(ordersToCancel) orderMap := make(map[Market]*Orders) for _, market := range markets { @@ -117,6 +120,10 @@ func (pipeline *MatchingPipeline) GetUnderlyingPrices() map[Market]*big.Int { return underlyingPrices } +func (pipeline *MatchingPipeline) GetCollaterals() []hu.Collateral { + return pipeline.configService.GetCollaterals() +} + func (pipeline *MatchingPipeline) cancelLimitOrders(cancellableOrders map[common.Address][]Order) map[common.Hash]struct{} { cancellableOrderIds := map[common.Hash]struct{}{} // @todo: if there are too many cancellable orders, they might not fit in a single block. Need to adjust for that. diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index cb2d7062c9..ebbaf80d0a 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -240,7 +240,7 @@ type LimitOrderDatabase interface { Accept(acceptedBlockNumber uint64, blockTimestamp uint64) SetOrderStatus(orderId common.Hash, status Status, info string, blockNumber uint64) error RevertLastStatus(orderId common.Hash) error - GetNaughtyTraders(oraclePrices map[Market]*big.Int, markets []Market) ([]LiquidablePosition, map[common.Address][]Order) + GetNaughtyTraders(oraclePrices map[Market]*big.Int, assets []hu.Collateral, markets []Market) ([]LiquidablePosition, map[common.Address][]Order) GetAllOpenOrdersForTrader(trader common.Address) []Order GetOpenOrdersForTraderByType(trader common.Address, orderType OrderType) []Order UpdateLastPremiumFraction(market Market, trader common.Address, lastPremiumFraction *big.Int, cumlastPremiumFraction *big.Int) @@ -913,7 +913,7 @@ func determinePositionToLiquidate(trader *Trader, addr common.Address, marginFra return liquidable } -func (db *InMemoryDatabase) GetNaughtyTraders(oraclePrices map[Market]*big.Int, markets []Market) ([]LiquidablePosition, map[common.Address][]Order) { +func (db *InMemoryDatabase) GetNaughtyTraders(oraclePrices map[Market]*big.Int, assets []hu.Collateral, markets []Market) ([]LiquidablePosition, map[common.Address][]Order) { db.mu.RLock() defer db.mu.RUnlock() @@ -926,7 +926,7 @@ func (db *InMemoryDatabase) GetNaughtyTraders(oraclePrices map[Market]*big.Int, for addr, trader := range db.TraderMap { pendingFunding := getTotalFunding(trader, markets) - marginFraction := calcMarginFraction(trader, pendingFunding, oraclePrices, db.LastPrice, markets) + marginFraction := calcMarginFraction(trader, pendingFunding, oraclePrices, assets, db.LastPrice, markets) if marginFraction.Cmp(db.configService.getMaintenanceMargin()) == -1 { log.Info("below maintenanceMargin", "trader", addr.String(), "marginFraction", prettifyScaledBigInt(marginFraction, 6)) if len(minSizes) == 0 { diff --git a/precompile/contracts/bibliophile/amm.go b/precompile/contracts/bibliophile/amm.go index 263cd38137..9e46683a59 100644 --- a/precompile/contracts/bibliophile/amm.go +++ b/precompile/contracts/bibliophile/amm.go @@ -15,12 +15,9 @@ const ( MAX_ORACLE_SPREAD_RATIO_SLOT int64 = 3 MAX_LIQUIDATION_RATIO_SLOT int64 = 4 MIN_SIZE_REQUIREMENT_SLOT int64 = 5 - ORACLE_SLOT int64 = 6 UNDERLYING_ASSET_SLOT int64 = 7 MAX_LIQUIDATION_PRICE_SPREAD int64 = 12 MULTIPLIER_SLOT int64 = 13 - RED_STONE_ADAPTER_SLOT int64 = 16 - RED_STONE_FEED_ID_SLOT int64 = 17 IMPACT_MARGIN_NOTIONAL_SLOT int64 = 22 LAST_TRADE_PRICE_SLOT int64 = 23 BIDS_SLOT int64 = 24 @@ -88,10 +85,6 @@ func getMultiplier(stateDB contract.StateDB, market common.Address) *big.Int { return stateDB.GetState(market, common.BigToHash(big.NewInt(MULTIPLIER_SLOT))).Big() } -func getOracleAddress(stateDB contract.StateDB, market common.Address) common.Address { - return common.BytesToAddress(stateDB.GetState(market, common.BigToHash(big.NewInt(ORACLE_SLOT))).Bytes()) -} - func getUnderlyingAssetAddress(stateDB contract.StateDB, market common.Address) common.Address { return common.BytesToAddress(stateDB.GetState(market, common.BigToHash(big.NewInt(UNDERLYING_ASSET_SLOT))).Bytes()) } @@ -101,45 +94,6 @@ func getUnderlyingPriceForMarket(stateDB contract.StateDB, marketID int64) *big. return getUnderlyingPrice(stateDB, market) } -func getRedStoneAdapterAddress(stateDB contract.StateDB, market common.Address) common.Address { - return common.BytesToAddress(stateDB.GetState(market, common.BigToHash(big.NewInt(RED_STONE_ADAPTER_SLOT))).Bytes()) -} - -func getRedStoneFeedId(stateDB contract.StateDB, market common.Address) common.Hash { - return stateDB.GetState(market, common.BigToHash(big.NewInt(RED_STONE_FEED_ID_SLOT))) -} - -func getUnderlyingPrice(stateDB contract.StateDB, market common.Address) *big.Int { - redStoneAdapter := getRedStoneAdapterAddress(stateDB, market) - if redStoneAdapter.Hash().Big().Sign() != 0 { - feedId := getRedStoneFeedId(stateDB, market) - // first we check the feedId, if it is set, it should imply we are using a redstone oracle - // log.Info("red-stone-feed-id", "feedId", feedId.String()) - if feedId.Big().Sign() != 0 { - // redstone oracle is configured for this market - redstonePrice := getRedStonePrice(stateDB, redStoneAdapter, feedId) - // log.Info("redstone-price", "amm", market, "price", redstonePrice) - return redstonePrice - } - /* else { - // just log the red stone price, for testing before deployment - var feedId common.Hash - if strings.EqualFold(market.String(), "0xa72b463C21dA61cCc86069cFab82e9e8491152a0") { // eth amm - feedId = common.HexToHash("0x4554480000000000000000000000000000000000000000000000000000000000") - } else if strings.EqualFold(market.String(), "0xd80e57dB448b0692C396B890eE9c791D7386dAdC") { // avax amm - feedId = common.HexToHash("0x4156415800000000000000000000000000000000000000000000000000000000") - } - // redstonePrice := getRedStonePrice(stateDB, redStoneAdapter, feedId) - // log.Info("log-only-redstone-price", "amm", market, "price", redstonePrice) - } */ - } - // red stone oracle is not enabled for this market, we use the default TestOracle - oracle := getOracleAddress(stateDB, market) - underlying := getUnderlyingAssetAddress(stateDB, market) - slot := crypto.Keccak256(append(common.LeftPadBytes(underlying.Bytes(), 32), common.LeftPadBytes(big.NewInt(TEST_ORACLE_PRICES_MAPPING_SLOT).Bytes(), 32)...)) - return fromTwosComplement(stateDB.GetState(oracle, common.BytesToHash(slot)).Bytes()) -} - // Trader State func positionsStorageSlot(trader *common.Address) *big.Int { diff --git a/precompile/contracts/bibliophile/api.go b/precompile/contracts/bibliophile/api.go index 359e70f943..fa6fe17d79 100644 --- a/precompile/contracts/bibliophile/api.go +++ b/precompile/contracts/bibliophile/api.go @@ -117,12 +117,12 @@ func GetAMMVariables(stateDB contract.StateDB, ammAddress common.Address, ammInd maxLiquidationRatio := GetMaxLiquidationRatio(stateDB, ammIndex) maxLiquidationPriceSpread := GetMaxLiquidationPriceSpread(stateDB, ammIndex) minSizeRequirement := GetMinSizeRequirement(stateDB, ammIndex) - oracleAddress := getOracleAddress(stateDB, ammAddress) + oracleAddress := getOracleAddress(stateDB) underlyingAssetAddress := getUnderlyingAssetAddress(stateDB, ammAddress) underlyingPriceForMarket := getUnderlyingPriceForMarket(stateDB, ammIndex) underlyingPrice := getUnderlyingPrice(stateDB, ammAddress) redStoneAdapterAddress := getRedStoneAdapterAddress(stateDB, ammAddress) - redStoneFeedId := getRedStoneFeedId(stateDB, ammAddress) + redStoneFeedId := getRedStoneFeedId(stateDB, oracleAddress, underlyingAssetAddress) bidsHead := getBidsHead(stateDB, ammAddress) bidsHeadSize := getBidSize(stateDB, ammAddress, bidsHead) asksHead := getAsksHead(stateDB, ammAddress) diff --git a/precompile/contracts/bibliophile/clearing_house.go b/precompile/contracts/bibliophile/clearing_house.go index 126c0568f3..35f4e4b965 100644 --- a/precompile/contracts/bibliophile/clearing_house.go +++ b/precompile/contracts/bibliophile/clearing_house.go @@ -49,7 +49,6 @@ func GetMarkets(stateDB contract.StateDB) []common.Address { for i := int64(0); i < numMarkets; i++ { amm := stateDB.GetState(common.HexToAddress(CLEARING_HOUSE_GENESIS_ADDRESS), common.BigToHash(new(big.Int).Add(baseStorageSlot, big.NewInt(i)))) markets[i] = common.BytesToAddress(amm.Bytes()) - } return markets } @@ -121,6 +120,14 @@ func GetUnderlyingPrices(stateDB contract.StateDB) []*big.Int { return underlyingPrices } +func GetCollateralPrices(stateDB contract.StateDB) []*big.Int { + underlyingPrices := make([]*big.Int, 0) + // for _, market := range GetMarkets(stateDB) { + // underlyingPrices = append(underlyingPrices, getUnderlyingPrice(stateDB, market)) + // } + return underlyingPrices +} + func getPosSizes(stateDB contract.StateDB, trader *common.Address) []*big.Int { positionSizes := make([]*big.Int, 0) for _, market := range GetMarkets(stateDB) { diff --git a/precompile/contracts/bibliophile/client.go b/precompile/contracts/bibliophile/client.go index 0815ae9774..853990b247 100644 --- a/precompile/contracts/bibliophile/client.go +++ b/precompile/contracts/bibliophile/client.go @@ -3,6 +3,7 @@ package bibliophile import ( "math/big" + hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" "github.com/ava-labs/subnet-evm/precompile/contract" "github.com/ethereum/go-ethereum/common" ) @@ -48,6 +49,7 @@ type BibliophileClient interface { GetAccessibleState() contract.AccessibleState GetNotionalPositionAndMargin(trader common.Address, includeFundingPayments bool, mode uint8) (*big.Int, *big.Int) HasReferrer(trader common.Address) bool + // GetCollaterals() []hu.Collateral } // Define a structure that will implement the Bibliophile interface @@ -189,3 +191,7 @@ func (b *bibliophileClient) GetNotionalPositionAndMargin(trader common.Address, func (b *bibliophileClient) HasReferrer(trader common.Address) bool { return hasReferrer(b.accessibleState.GetStateDB(), trader) } + +// func (b *bibliophileClient) GetCollaterals() []hu.Collateral { +// return getCollaterals(b.accessibleState.GetStateDB()) +// } diff --git a/precompile/contracts/bibliophile/client_mock.go b/precompile/contracts/bibliophile/client_mock.go index a914fc80d9..1fcd080b85 100644 --- a/precompile/contracts/bibliophile/client_mock.go +++ b/precompile/contracts/bibliophile/client_mock.go @@ -8,6 +8,7 @@ import ( big "math/big" reflect "reflect" + hubbleutils "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" contract "github.com/ava-labs/subnet-evm/precompile/contract" common "github.com/ethereum/go-ethereum/common" gomock "github.com/golang/mock/gomock" @@ -149,6 +150,20 @@ func (mr *MockBibliophileClientMockRecorder) GetBlockPlaced(orderHash interface{ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockPlaced", reflect.TypeOf((*MockBibliophileClient)(nil).GetBlockPlaced), orderHash) } +// GetCollaterals mocks base method. +func (m *MockBibliophileClient) GetCollaterals(stateDB contract.StateDB) []hubbleutils.Collateral { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCollaterals", stateDB) + ret0, _ := ret[0].([]hubbleutils.Collateral) + return ret0 +} + +// GetCollaterals indicates an expected call of GetCollaterals. +func (mr *MockBibliophileClientMockRecorder) GetCollaterals(stateDB interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCollaterals", reflect.TypeOf((*MockBibliophileClient)(nil).GetCollaterals), stateDB) +} + // GetImpactMarginNotional mocks base method. func (m *MockBibliophileClient) GetImpactMarginNotional(ammAddress common.Address) *big.Int { m.ctrl.T.Helper() diff --git a/precompile/contracts/bibliophile/margin_account.go b/precompile/contracts/bibliophile/margin_account.go index 68a446b201..9823f4e2e0 100644 --- a/precompile/contracts/bibliophile/margin_account.go +++ b/precompile/contracts/bibliophile/margin_account.go @@ -12,18 +12,24 @@ import ( const ( MARGIN_ACCOUNT_GENESIS_ADDRESS = "0x0300000000000000000000000000000000000001" + ORACLE_SLOT int64 = 4 + SUPPORTED_COLLATERAL_SLOT int64 = 8 VAR_MARGIN_MAPPING_SLOT int64 = 10 VAR_RESERVED_MARGIN_SLOT int64 = 11 ) func GetNormalizedMargin(stateDB contract.StateDB, trader common.Address) *big.Int { - // this is only written for single hUSD collateral - // TODO: generalize for multiple collaterals - return getMargin(stateDB, big.NewInt(0), trader) + assets := getCollaterals(stateDB) + numAssets := len(assets) + margin := make([]*big.Int, numAssets) + for i := 0; i < numAssets; i++ { + margin[i] = getMargin(stateDB, big.NewInt(int64(i)), trader) + } + return hu.GetNormalizedMargin(assets, margin) } -func getMargin(stateDB contract.StateDB, collateralIdx *big.Int, trader common.Address) *big.Int { - marginStorageSlot := crypto.Keccak256(append(common.LeftPadBytes(collateralIdx.Bytes(), 32), common.LeftPadBytes(big.NewInt(VAR_MARGIN_MAPPING_SLOT).Bytes(), 32)...)) +func getMargin(stateDB contract.StateDB, idx *big.Int, trader common.Address) *big.Int { + marginStorageSlot := crypto.Keccak256(append(common.LeftPadBytes(idx.Bytes(), 32), common.LeftPadBytes(big.NewInt(VAR_MARGIN_MAPPING_SLOT).Bytes(), 32)...)) marginStorageSlot = crypto.Keccak256(append(common.LeftPadBytes(trader.Bytes(), 32), marginStorageSlot...)) return fromTwosComplement(stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BytesToHash(marginStorageSlot)).Bytes()) } @@ -44,3 +50,35 @@ func GetAvailableMargin(stateDB contract.StateDB, trader common.Address) *big.In // log.Info("GetAvailableMargin", "trader", trader, "notionalPostion", notionalPostion, "margin", margin, "utitlizedMargin", utitlizedMargin, "reservedMargin", reservedMargin) return big.NewInt(0).Sub(big.NewInt(0).Sub(margin, utitlizedMargin), reservedMargin) } + +func getOracleAddress(stateDB contract.StateDB) common.Address { + return common.BytesToAddress(stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BigToHash(big.NewInt(ORACLE_SLOT))).Bytes()) +} + +func GetCollaterals(stateDB contract.StateDB) []hu.Collateral { + numAssets := getCollateralCount(stateDB) + assets := make([]hu.Collateral, numAssets) + for i := uint8(0); i < numAssets; i++ { + assets[i] = getCollateralAt(stateDB, i) + } + return assets +} + +func getCollateralCount(stateDB contract.StateDB) uint8 { + rawVal := stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BytesToHash(common.LeftPadBytes(big.NewInt(SUPPORTED_COLLATERAL_SLOT).Bytes(), 32))) + return uint8(new(big.Int).SetBytes(rawVal.Bytes()).Uint64()) +} + +func getCollateralAt(stateDB contract.StateDB, idx uint8) hu.Collateral { + baseSlot := hu.Add(collateralStorageSlot(), big.NewInt(int64(idx))) + tokenAddress := common.BytesToAddress(stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BigToHash(baseSlot)).Bytes()) + return hu.Collateral{ + Weight: stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BigToHash(hu.Add(baseSlot, big.NewInt(1)))).Big(), + Decimals: uint8(stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BigToHash(hu.Add(baseSlot, big.NewInt(2)))).Big().Uint64()), + Price: getUnderlyingPrice_(stateDB, tokenAddress), + } +} + +func collateralStorageSlot() *big.Int { + return new(big.Int).SetBytes(crypto.Keccak256(common.LeftPadBytes(big.NewInt(SUPPORTED_COLLATERAL_SLOT).Bytes(), 32))) +} diff --git a/precompile/contracts/bibliophile/oracle.go b/precompile/contracts/bibliophile/oracle.go new file mode 100644 index 0000000000..5c918ab80c --- /dev/null +++ b/precompile/contracts/bibliophile/oracle.go @@ -0,0 +1,59 @@ +package bibliophile + +import ( + "math/big" + + "github.com/ava-labs/subnet-evm/precompile/contract" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +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 = 3 +) + +func getUnderlyingPrice(stateDB contract.StateDB, market common.Address) *big.Int { + return getUnderlyingPrice_(stateDB, getUnderlyingAssetAddress(stateDB, market)) +} + +func getUnderlyingPrice_(stateDB contract.StateDB, underlying common.Address) *big.Int { + oracle := getOracleAddress(stateDB) // this comes from margin account + feedId := getRedStoneFeedId(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 + } + // red stone oracle is not enabled for this market, we use the default TestOracle + slot := crypto.Keccak256(append(common.LeftPadBytes(underlying.Bytes(), 32), common.LeftPadBytes(big.NewInt(TEST_ORACLE_PRICES_MAPPING_SLOT).Bytes(), 32)...)) + return fromTwosComplement(stateDB.GetState(oracle, common.BytesToHash(slot)).Bytes()) +} + +func getRedStoneAdapterAddress(stateDB contract.StateDB, oracle common.Address) common.Address { + return common.BytesToAddress(stateDB.GetState(oracle, common.BigToHash(big.NewInt(RED_STONE_ADAPTER_SLOT))).Bytes()) +} + +func getRedStonePrice(stateDB contract.StateDB, adapterAddress common.Address, redStoneFeedId common.Hash) *big.Int { + latestRoundId := getlatestRoundId(stateDB, adapterAddress) + slot := common.BytesToHash(crypto.Keccak256(append(append(redStoneFeedId.Bytes(), common.LeftPadBytes(latestRoundId.Bytes(), 32)...), RED_STONE_VALUES_MAPPING_STORAGE_LOCATION.Bytes()...))) + return new(big.Int).Div(fromTwosComplement(stateDB.GetState(adapterAddress, slot).Bytes()), big.NewInt(100)) // we use 6 decimals precision everywhere +} + +func getlatestRoundId(stateDB contract.StateDB, adapterAddress common.Address) *big.Int { + return fromTwosComplement(stateDB.GetState(adapterAddress, RED_STONE_LATEST_ROUND_ID_STORAGE_LOCATION).Bytes()) +} + +func getRedStoneFeedId(stateDB contract.StateDB, oracle, underlying common.Address) common.Hash { + return stateDB.GetState(oracle, common.BytesToHash(aggregatorMapSlot(underlying))) +} + +func aggregatorMapSlot(underlying common.Address) []byte { + return crypto.Keccak256(append(common.LeftPadBytes(underlying.Bytes(), 32), common.LeftPadBytes(big.NewInt(AGGREGATOR_MAP_SLOT).Bytes(), 32)...)) +} diff --git a/precompile/contracts/bibliophile/redstone.go b/precompile/contracts/bibliophile/redstone.go deleted file mode 100644 index db3939458a..0000000000 --- a/precompile/contracts/bibliophile/redstone.go +++ /dev/null @@ -1,25 +0,0 @@ -package bibliophile - -import ( - "math/big" - - "github.com/ava-labs/subnet-evm/precompile/contract" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -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"); -) - -func getRedStonePrice(stateDB contract.StateDB, adapterAddress common.Address, redStoneFeedId common.Hash) *big.Int { - latestRoundId := getlatestRoundId(stateDB, adapterAddress) - slot := common.BytesToHash(crypto.Keccak256(append(append(redStoneFeedId.Bytes(), common.LeftPadBytes(latestRoundId.Bytes(), 32)...), RED_STONE_VALUES_MAPPING_STORAGE_LOCATION.Bytes()...))) - return new(big.Int).Div(fromTwosComplement(stateDB.GetState(adapterAddress, slot).Bytes()), big.NewInt(100)) // we use 6 decimals precision everywhere -} - -func getlatestRoundId(stateDB contract.StateDB, adapterAddress common.Address) *big.Int { - return fromTwosComplement(stateDB.GetState(adapterAddress, RED_STONE_LATEST_ROUND_ID_STORAGE_LOCATION).Bytes()) -} From 8bea582c47b7ffa4ae377117f83ad61300d85ebd Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Mon, 25 Sep 2023 17:08:20 +0100 Subject: [PATCH 02/11] refactor GetAvailableMargin --- .../evm/orderbook/hubbleutils/margin_math.go | 132 +++++++++++++++++- plugin/evm/orderbook/liquidations.go | 76 ++-------- plugin/evm/orderbook/matching_pipeline.go | 1 - plugin/evm/orderbook/memory_database.go | 81 +++++------ plugin/evm/orderbook/service.go | 12 +- plugin/evm/orderbook/trading_apis.go | 3 +- precompile/contracts/bibliophile/amm.go | 63 ++------- .../contracts/bibliophile/clearing_house.go | 41 +++--- precompile/contracts/bibliophile/client.go | 2 +- .../contracts/bibliophile/margin_account.go | 28 ++-- 10 files changed, 220 insertions(+), 219 deletions(-) diff --git a/plugin/evm/orderbook/hubbleutils/margin_math.go b/plugin/evm/orderbook/hubbleutils/margin_math.go index b3ea25de8e..aa840cbe50 100644 --- a/plugin/evm/orderbook/hubbleutils/margin_math.go +++ b/plugin/evm/orderbook/hubbleutils/margin_math.go @@ -1,6 +1,7 @@ package hubbleutils import ( + "encoding/json" "math/big" ) @@ -10,21 +11,142 @@ type Collateral struct { Decimals uint8 } -func GetNormalizedMargin(assets []Collateral, margin []*big.Int) *big.Int { - weighted, _ := WeightedAndSpotCollateral(assets, margin) +type Market = int + +type Position struct { + OpenNotional *big.Int `json:"open_notional"` + Size *big.Int `json:"size"` + UnrealisedFunding *big.Int `json:"unrealised_funding"` + LastPremiumFraction *big.Int `json:"last_premium_fraction"` + LiquidationThreshold *big.Int `json:"liquidation_threshold"` +} + +func (p *Position) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + OpenNotional string `json:"open_notional"` + Size string `json:"size"` + UnrealisedFunding string `json:"unrealised_funding"` + LastPremiumFraction string `json:"last_premium_fraction"` + LiquidationThreshold string `json:"liquidation_threshold"` + }{ + OpenNotional: p.OpenNotional.String(), + Size: p.Size.String(), + UnrealisedFunding: p.UnrealisedFunding.String(), + LastPremiumFraction: p.LastPremiumFraction.String(), + LiquidationThreshold: p.LiquidationThreshold.String(), + }) +} + +type Trader struct { + Positions map[Market]*Position `json:"positions"` // position for every market + Margin Margin `json:"margin"` // available margin/balance for every market +} + +type Margin struct { + Reserved *big.Int `json:"reserved"` + Deposited map[Collateral]*big.Int `json:"deposited"` +} + +func GetNormalizedMargin(assets []Collateral, margins []*big.Int) *big.Int { + weighted, _ := WeightedAndSpotCollateral(assets, margins) return weighted } -func WeightedAndSpotCollateral(assets []Collateral, margin []*big.Int) (weighted, spot *big.Int) { +func WeightedAndSpotCollateral(assets []Collateral, margins []*big.Int) (weighted, spot *big.Int) { weighted = big.NewInt(0) spot = big.NewInt(0) for i, asset := range assets { - if margin[i].Sign() == 0 { + if margins[i].Sign() == 0 { continue } - numerator := Mul(margin[i], asset.Price) // margin[i] is scaled by asset.Decimal + numerator := Mul(margins[i], asset.Price) // margin[i] is scaled by asset.Decimal spot.Add(spot, Unscale(numerator, asset.Decimals)) weighted.Add(weighted, Unscale(Mul(numerator, asset.Weight), asset.Decimals+6)) } return weighted, spot } + +func GetAvailableMargin(positions map[Market]*Position, margins []*big.Int, pendingFunding *big.Int, reservedMargin *big.Int, assets []Collateral, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, minAllowableMargin *big.Int, markets []Market) *big.Int { + notionalPosition, margin := GetNotionalPositionAndMargin(positions, margins, Min_Allowable_Margin, pendingFunding, assets, oraclePrices, lastPrices, markets) + return GetAvailableMargin_(notionalPosition, margin, reservedMargin, minAllowableMargin) +} + +func GetAvailableMargin_(notionalPosition, margin, reservedMargin, minAllowableMargin *big.Int) *big.Int { + utilisedMargin := Div1e6(Mul(notionalPosition, minAllowableMargin)) + return Sub(margin, Add(utilisedMargin, reservedMargin)) +} + +type MarginMode = uint8 + +const ( + Maintenance_Margin MarginMode = iota + Min_Allowable_Margin +) + +func GetNotionalPositionAndMargin(positions map[Market]*Position, margins []*big.Int, marginMode MarginMode, pendingFunding *big.Int, assets []Collateral, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, markets []Market) (*big.Int, *big.Int) { + margin := Sub(GetNormalizedMargin(assets, margins), pendingFunding) + notionalPosition, unrealizedPnl := GetTotalNotionalPositionAndUnrealizedPnl(positions, margin, marginMode, oraclePrices, lastPrices, markets) + return notionalPosition, Add(margin, unrealizedPnl) +} + +func GetTotalNotionalPositionAndUnrealizedPnl(positions map[Market]*Position, margin *big.Int, marginMode MarginMode, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, markets []Market) (*big.Int, *big.Int) { + notionalPosition := big.NewInt(0) + unrealizedPnl := big.NewInt(0) + for _, market := range markets { + _notionalPosition, _unrealizedPnl := GetOptimalPnl(market, oraclePrices[market], lastPrices[market], positions, margin, marginMode) + notionalPosition.Add(notionalPosition, _notionalPosition) + unrealizedPnl.Add(unrealizedPnl, _unrealizedPnl) + } + return notionalPosition, unrealizedPnl +} + +func GetOptimalPnl(market Market, oraclePrice *big.Int, lastPrice *big.Int, positions map[Market]*Position, margin *big.Int, marginMode MarginMode) (notionalPosition *big.Int, uPnL *big.Int) { + position := positions[market] + if position == nil || position.Size.Sign() == 0 { + return big.NewInt(0), big.NewInt(0) + } + + // based on last price + notionalPosition, unrealizedPnl, lastPriceBasedMF := GetPositionMetadata( + lastPrice, + position.OpenNotional, + position.Size, + margin, + ) + // log.Info("in getOptimalPnl", "notionalPosition", notionalPosition, "unrealizedPnl", unrealizedPnl, "lastPriceBasedMF", lastPriceBasedMF) + + // based on oracle price + oracleBasedNotional, oracleBasedUnrealizedPnl, oracleBasedMF := GetPositionMetadata( + oraclePrice, + position.OpenNotional, + position.Size, + margin, + ) + // log.Info("in getOptimalPnl", "oracleBasedNotional", oracleBasedNotional, "oracleBasedUnrealizedPnl", oracleBasedUnrealizedPnl, "oracleBasedMF", oracleBasedMF) + + if (marginMode == Maintenance_Margin && oracleBasedMF.Cmp(lastPriceBasedMF) == 1) || // for liquidations + (marginMode == Min_Allowable_Margin && oracleBasedMF.Cmp(lastPriceBasedMF) == -1) { // for increasing leverage + return oracleBasedNotional, oracleBasedUnrealizedPnl + } + return notionalPosition, unrealizedPnl +} + +func GetPositionMetadata(price *big.Int, openNotional *big.Int, size *big.Int, margin *big.Int) (notionalPosition *big.Int, unrealisedPnl *big.Int, marginFraction *big.Int) { + // log.Info("in GetPositionMetadata", "price", price, "openNotional", openNotional, "size", size, "margin", margin) + notionalPosition = getNotionalPosition(price, size) + uPnL := new(big.Int) + if notionalPosition.Cmp(big.NewInt(0)) == 0 { + return big.NewInt(0), big.NewInt(0), big.NewInt(0) + } + if size.Cmp(big.NewInt(0)) > 0 { + uPnL = new(big.Int).Sub(notionalPosition, openNotional) + } else { + uPnL = new(big.Int).Sub(openNotional, notionalPosition) + } + mf := new(big.Int).Div(Mul1e6(new(big.Int).Add(margin, uPnL)), notionalPosition) + return notionalPosition, uPnL, mf +} + +func getNotionalPosition(price *big.Int, size *big.Int) *big.Int { + return big.NewInt(0).Abs(Div1e18(Mul(size, price))) +} diff --git a/plugin/evm/orderbook/liquidations.go b/plugin/evm/orderbook/liquidations.go index 7310fa8126..0004a2815f 100644 --- a/plugin/evm/orderbook/liquidations.go +++ b/plugin/evm/orderbook/liquidations.go @@ -22,9 +22,9 @@ func (liq LiquidablePosition) GetUnfilledSize() *big.Int { return big.NewInt(0).Sub(liq.Size, liq.FilledSize) } -func calcMarginFraction(trader *Trader, pendingFunding *big.Int, oraclePrices map[Market]*big.Int, assets []hu.Collateral, lastPrices map[Market]*big.Int, markets []Market) *big.Int { +func calcMarginFraction(trader *Trader, pendingFunding *big.Int, assets []hu.Collateral, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, markets []Market) *big.Int { margin := new(big.Int).Sub(getNormalisedMargin(trader, assets), pendingFunding) - notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(trader, margin, Maintenance_Margin, oraclePrices, lastPrices, markets) + notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(trader, margin, hu.Maintenance_Margin, oraclePrices, lastPrices, markets) if notionalPosition.Sign() == 0 { return big.NewInt(math.MaxInt64) } @@ -40,12 +40,15 @@ func sortLiquidableSliceByMarginFraction(positions []LiquidablePosition) []Liqui } func getNormalisedMargin(trader *Trader, assets []hu.Collateral) *big.Int { - numAssets := len(assets) + return hu.GetNormalizedMargin(assets, getMargins(trader, len(assets))) +} + +func getMargins(trader *Trader, numAssets int) []*big.Int { margin := make([]*big.Int, numAssets) for i := 0; i < numAssets; i++ { margin[i] = trader.Margin.Deposited[Collateral(i)] } - return hu.GetNormalizedMargin(assets, margin) + return margin } func getTotalFunding(trader *Trader, markets []Market) *big.Int { @@ -58,73 +61,14 @@ func getTotalFunding(trader *Trader, markets []Market) *big.Int { return totalPendingFunding } -func getNotionalPosition(price *big.Int, size *big.Int) *big.Int { - return big.NewInt(0).Abs(hu.Div1e18(hu.Mul(size, price))) -} - -type MarginMode uint8 - -const ( - Maintenance_Margin MarginMode = iota - Min_Allowable_Margin -) +type MarginMode = hu.MarginMode func getTotalNotionalPositionAndUnrealizedPnl(trader *Trader, margin *big.Int, marginMode MarginMode, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, markets []Market) (*big.Int, *big.Int) { - notionalPosition := big.NewInt(0) - unrealizedPnl := big.NewInt(0) - for _, market := range markets { - _notionalPosition, _unrealizedPnl := getOptimalPnl(market, oraclePrices[market], lastPrices[market], trader, margin, marginMode) - notionalPosition.Add(notionalPosition, _notionalPosition) - unrealizedPnl.Add(unrealizedPnl, _unrealizedPnl) - } - return notionalPosition, unrealizedPnl -} - -func getOptimalPnl(market Market, oraclePrice *big.Int, lastPrice *big.Int, trader *Trader, margin *big.Int, marginMode MarginMode) (notionalPosition *big.Int, uPnL *big.Int) { - position := trader.Positions[market] - if position == nil || position.Size.Sign() == 0 { - return big.NewInt(0), big.NewInt(0) - } - - // based on last price - notionalPosition, unrealizedPnl, lastPriceBasedMF := getPositionMetadata( - lastPrice, - position.OpenNotional, - position.Size, - margin, - ) - // log.Info("in getOptimalPnl", "notionalPosition", notionalPosition, "unrealizedPnl", unrealizedPnl, "lastPriceBasedMF", lastPriceBasedMF) - - // based on oracle price - oracleBasedNotional, oracleBasedUnrealizedPnl, oracleBasedMF := getPositionMetadata( - oraclePrice, - position.OpenNotional, - position.Size, - margin, - ) - // log.Info("in getOptimalPnl", "oracleBasedNotional", oracleBasedNotional, "oracleBasedUnrealizedPnl", oracleBasedUnrealizedPnl, "oracleBasedMF", oracleBasedMF) - - if (marginMode == Maintenance_Margin && oracleBasedMF.Cmp(lastPriceBasedMF) == 1) || // for liquidations - (marginMode == Min_Allowable_Margin && oracleBasedMF.Cmp(lastPriceBasedMF) == -1) { // for increasing leverage - return oracleBasedNotional, oracleBasedUnrealizedPnl - } - return notionalPosition, unrealizedPnl + return hu.GetTotalNotionalPositionAndUnrealizedPnl(trader.Positions, margin, marginMode, oraclePrices, lastPrices, markets) } func getPositionMetadata(price *big.Int, openNotional *big.Int, size *big.Int, margin *big.Int) (notionalPosition *big.Int, unrealisedPnl *big.Int, marginFraction *big.Int) { - // log.Info("in getPositionMetadata", "price", price, "openNotional", openNotional, "size", size, "margin", margin) - notionalPosition = getNotionalPosition(price, size) - uPnL := new(big.Int) - if notionalPosition.Cmp(big.NewInt(0)) == 0 { - return big.NewInt(0), big.NewInt(0), big.NewInt(0) - } - if size.Cmp(big.NewInt(0)) > 0 { - uPnL = new(big.Int).Sub(notionalPosition, openNotional) - } else { - uPnL = new(big.Int).Sub(openNotional, notionalPosition) - } - mf := new(big.Int).Div(hu.Mul1e6(new(big.Int).Add(margin, uPnL)), notionalPosition) - return notionalPosition, uPnL, mf + return hu.GetPositionMetadata(price, openNotional, size, margin) } func prettifyScaledBigInt(number *big.Int, precision int8) string { diff --git a/plugin/evm/orderbook/matching_pipeline.go b/plugin/evm/orderbook/matching_pipeline.go index 8de8f3648d..818d63a549 100644 --- a/plugin/evm/orderbook/matching_pipeline.go +++ b/plugin/evm/orderbook/matching_pipeline.go @@ -7,7 +7,6 @@ import ( "time" hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" - b "github.com/ava-labs/subnet-evm/precompile/contracts/bibliophile" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index ebbaf80d0a..6999f66f84 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -51,7 +51,9 @@ const ( RETRY_AFTER_BLOCKS = 10 ) -type Market int64 +type Market = hu.Market // int64 + +// type Market int64 type Collateral int @@ -179,29 +181,31 @@ func (order Order) ToOrderMin() OrderMin { } } -type Position struct { - OpenNotional *big.Int `json:"open_notional"` - Size *big.Int `json:"size"` - UnrealisedFunding *big.Int `json:"unrealised_funding"` - LastPremiumFraction *big.Int `json:"last_premium_fraction"` - LiquidationThreshold *big.Int `json:"liquidation_threshold"` -} - -func (p *Position) MarshalJSON() ([]byte, error) { - return json.Marshal(&struct { - OpenNotional string `json:"open_notional"` - Size string `json:"size"` - UnrealisedFunding string `json:"unrealised_funding"` - LastPremiumFraction string `json:"last_premium_fraction"` - LiquidationThreshold string `json:"liquidation_threshold"` - }{ - OpenNotional: p.OpenNotional.String(), - Size: p.Size.String(), - UnrealisedFunding: p.UnrealisedFunding.String(), - LastPremiumFraction: p.LastPremiumFraction.String(), - LiquidationThreshold: p.LiquidationThreshold.String(), - }) -} +type Position = hu.Position + +// { +// OpenNotional *big.Int `json:"open_notional"` +// Size *big.Int `json:"size"` +// UnrealisedFunding *big.Int `json:"unrealised_funding"` +// LastPremiumFraction *big.Int `json:"last_premium_fraction"` +// LiquidationThreshold *big.Int `json:"liquidation_threshold"` +// } + +// func (p *Position) MarshalJSON() ([]byte, error) { +// return json.Marshal(&struct { +// OpenNotional string `json:"open_notional"` +// Size string `json:"size"` +// UnrealisedFunding string `json:"unrealised_funding"` +// LastPremiumFraction string `json:"last_premium_fraction"` +// LiquidationThreshold string `json:"liquidation_threshold"` +// }{ +// OpenNotional: p.OpenNotional.String(), +// Size: p.Size.String(), +// UnrealisedFunding: p.UnrealisedFunding.String(), +// LastPremiumFraction: p.LastPremiumFraction.String(), +// LiquidationThreshold: p.LiquidationThreshold.String(), +// }) +// } type Margin struct { Reserved *big.Int `json:"reserved"` @@ -926,7 +930,7 @@ func (db *InMemoryDatabase) GetNaughtyTraders(oraclePrices map[Market]*big.Int, for addr, trader := range db.TraderMap { pendingFunding := getTotalFunding(trader, markets) - marginFraction := calcMarginFraction(trader, pendingFunding, oraclePrices, assets, db.LastPrice, markets) + marginFraction := calcMarginFraction(trader, pendingFunding, assets, oraclePrices, db.LastPrice, markets) if marginFraction.Cmp(db.configService.getMaintenanceMargin()) == -1 { log.Info("below maintenanceMargin", "trader", addr.String(), "marginFraction", prettifyScaledBigInt(marginFraction, 6)) if len(minSizes) == 0 { @@ -941,7 +945,7 @@ func (db *InMemoryDatabase) GetNaughtyTraders(oraclePrices map[Market]*big.Int, continue } // has orders that might be cancellable - availableMargin := getAvailableMargin(trader, pendingFunding, oraclePrices, db.LastPrice, db.configService.getMinAllowableMargin(), markets) + availableMargin := getAvailableMargin(trader, pendingFunding, assets, oraclePrices, db.LastPrice, db.configService.getMinAllowableMargin(), markets) // availableMargin := getAvailableMarginWithDebugInfo(addr, trader, pendingFunding, oraclePrices, db.LastPrice, db.configService.getMinAllowableMargin(), markets) if availableMargin.Sign() == -1 { foundCancellableOrders := db.determineOrdersToCancel(addr, trader, availableMargin, oraclePrices, ordersToCancel) @@ -1086,29 +1090,8 @@ func getBlankTrader() *Trader { } } -func getAvailableMargin(trader *Trader, pendingFunding *big.Int, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, minAllowableMargin *big.Int, markets []Market) *big.Int { - margin := new(big.Int).Sub(getNormalisedMargin(trader), pendingFunding) - notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(trader, margin, Min_Allowable_Margin, oraclePrices, lastPrices, markets) - utilisedMargin := hu.Div1e6(new(big.Int).Mul(notionalPosition, minAllowableMargin)) - return new(big.Int).Sub( - new(big.Int).Add(margin, unrealizePnL), - new(big.Int).Add(utilisedMargin, trader.Margin.Reserved), - ) -} - -func getAvailableMarginWithDebugInfo(addr common.Address, trader *Trader, pendingFunding *big.Int, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, minAllowableMargin *big.Int, markets []Market) *big.Int { - margin := new(big.Int).Sub(getNormalisedMargin(trader), pendingFunding) - notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(trader, margin, Min_Allowable_Margin, oraclePrices, lastPrices, markets) - utilisedMargin := hu.Div1e6(new(big.Int).Mul(notionalPosition, minAllowableMargin)) - availableMargin := new(big.Int).Sub( - new(big.Int).Add(margin, unrealizePnL), - new(big.Int).Add(utilisedMargin, trader.Margin.Reserved), - ) - if availableMargin.Sign() == -1 { - log.Info("availableMargin < 0", "addr", addr.String(), "pendingFunding", pendingFunding, "margin", margin, "notionalPosition", notionalPosition, "unrealizePnL", unrealizePnL, "utilisedMargin", utilisedMargin, "Reserved", trader.Margin.Reserved) - log.Info("prices", "oraclePrices", oraclePrices, "lastPrices", lastPrices) - } - return availableMargin +func getAvailableMargin(trader *Trader, pendingFunding *big.Int, assets []hu.Collateral, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, minAllowableMargin *big.Int, markets []Market) *big.Int { + return hu.GetAvailableMargin(trader.Positions, getMargins(trader, len(assets)), pendingFunding, trader.Margin.Reserved, assets, oraclePrices, lastPrices, minAllowableMargin, markets) } // deepCopyOrder deep copies the LimitOrder struct diff --git a/plugin/evm/orderbook/service.go b/plugin/evm/orderbook/service.go index 05921293d7..fcecda3637 100644 --- a/plugin/evm/orderbook/service.go +++ b/plugin/evm/orderbook/service.go @@ -107,19 +107,19 @@ func (api *OrderBookAPI) GetDebugData(ctx context.Context, trader string) GetDeb markets[i] = Market(i) oraclePrices[Market(i)] = prices[Market(i)] } - + assets := api.configService.GetCollaterals() for addr, trader := range traderMap { pendingFunding := getTotalFunding(&trader, markets) - margin := new(big.Int).Sub(getNormalisedMargin(&trader), pendingFunding) - notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(&trader, margin, Min_Allowable_Margin, oraclePrices, lastPrices, markets) - marginFraction := calcMarginFraction(&trader, pendingFunding, oraclePrices, lastPrices, markets) - availableMargin := getAvailableMargin(&trader, pendingFunding, oraclePrices, lastPrices, api.configService.getMinAllowableMargin(), markets) + margin := new(big.Int).Sub(getNormalisedMargin(&trader, assets), pendingFunding) + notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(&trader, margin, hu.Min_Allowable_Margin, oraclePrices, lastPrices, markets) + marginFraction := calcMarginFraction(&trader, pendingFunding, assets, oraclePrices, lastPrices, markets) + availableMargin := getAvailableMargin(&trader, pendingFunding, assets, oraclePrices, lastPrices, api.configService.getMinAllowableMargin(), markets) utilisedMargin := hu.Div1e6(new(big.Int).Mul(notionalPosition, minAllowableMargin)) response.MarginFraction[addr] = marginFraction response.AvailableMargin[addr] = availableMargin response.PendingFunding[addr] = pendingFunding - response.Margin[addr] = getNormalisedMargin(&trader) + response.Margin[addr] = getNormalisedMargin(&trader, assets) response.UtilisedMargin[addr] = utilisedMargin response.NotionalPosition[addr] = notionalPosition response.UnrealizePnL[addr] = unrealizePnL diff --git a/plugin/evm/orderbook/trading_apis.go b/plugin/evm/orderbook/trading_apis.go index a3c3c95781..af6edd89b2 100644 --- a/plugin/evm/orderbook/trading_apis.go +++ b/plugin/evm/orderbook/trading_apis.go @@ -177,8 +177,9 @@ func (api *TradingAPI) GetMarginAndPositions(ctx context.Context, trader string) markets[i] = Market(i) } + assets := api.configService.GetCollaterals() pendingFunding := getTotalFunding(traderInfo, markets) - margin := new(big.Int).Sub(getNormalisedMargin(traderInfo), pendingFunding) + margin := new(big.Int).Sub(getNormalisedMargin(traderInfo, assets), pendingFunding) response.Margin = utils.BigIntToDecimal(margin, 6, 8) response.ReservedMargin = utils.BigIntToDecimal(traderInfo.Margin.Reserved, 6, 8) diff --git a/precompile/contracts/bibliophile/amm.go b/precompile/contracts/bibliophile/amm.go index 9e46683a59..8f9541509b 100644 --- a/precompile/contracts/bibliophile/amm.go +++ b/precompile/contracts/bibliophile/amm.go @@ -18,12 +18,12 @@ const ( UNDERLYING_ASSET_SLOT int64 = 7 MAX_LIQUIDATION_PRICE_SPREAD int64 = 12 MULTIPLIER_SLOT int64 = 13 - IMPACT_MARGIN_NOTIONAL_SLOT int64 = 22 - LAST_TRADE_PRICE_SLOT int64 = 23 - BIDS_SLOT int64 = 24 - ASKS_SLOT int64 = 25 - BIDS_HEAD_SLOT int64 = 26 - ASKS_HEAD_SLOT int64 = 27 + IMPACT_MARGIN_NOTIONAL_SLOT int64 = 20 + LAST_TRADE_PRICE_SLOT int64 = 21 + BIDS_SLOT int64 = 22 + ASKS_SLOT int64 = 23 + BIDS_HEAD_SLOT int64 = 24 + ASKS_HEAD_SLOT int64 = 25 ) const ( @@ -140,6 +140,13 @@ func getImpactMarginNotional(stateDB contract.StateDB, market common.Address) *b return stateDB.GetState(market, common.BigToHash(big.NewInt(IMPACT_MARGIN_NOTIONAL_SLOT))).Big() } +func getPosition(stateDB contract.StateDB, market common.Address, trader *common.Address) *hu.Position { + return &hu.Position{ + Size: getSize(stateDB, market, trader), + OpenNotional: getOpenNotional(stateDB, market, trader), + } +} + // Utils func getPendingFundingPayment(stateDB contract.StateDB, market common.Address, trader *common.Address) *big.Int { @@ -147,50 +154,6 @@ func getPendingFundingPayment(stateDB contract.StateDB, market common.Address, t return hu.Div1e18(new(big.Int).Mul(new(big.Int).Sub(cumulativePremiumFraction, GetLastPremiumFraction(stateDB, market, trader)), getSize(stateDB, market, trader))) } -func getOptimalPnl(stateDB contract.StateDB, market common.Address, oraclePrice *big.Int, lastPrice *big.Int, trader *common.Address, margin *big.Int, marginMode MarginMode) (notionalPosition *big.Int, uPnL *big.Int) { - size := getSize(stateDB, market, trader) - if size.Sign() == 0 { - return big.NewInt(0), big.NewInt(0) - } - - openNotional := getOpenNotional(stateDB, market, trader) - // based on last price - notionalPosition, unrealizedPnl, lastPriceBasedMF := getPositionMetadata( - lastPrice, - openNotional, - size, - margin, - ) - - // based on oracle price - oracleBasedNotional, oracleBasedUnrealizedPnl, oracleBasedMF := getPositionMetadata( - oraclePrice, - openNotional, - size, - margin, - ) - - if (marginMode == Maintenance_Margin && oracleBasedMF.Cmp(lastPriceBasedMF) == 1) || // for liquidations - (marginMode == Min_Allowable_Margin && oracleBasedMF.Cmp(lastPriceBasedMF) == -1) { // for increasing leverage - return oracleBasedNotional, oracleBasedUnrealizedPnl - } - return notionalPosition, unrealizedPnl -} - -func getPositionMetadata(price *big.Int, openNotional *big.Int, size *big.Int, margin *big.Int) (notionalPos *big.Int, uPnl *big.Int, marginFraction *big.Int) { - notionalPos = hu.Div1e18(new(big.Int).Mul(price, new(big.Int).Abs(size))) - if notionalPos.Sign() == 0 { - return big.NewInt(0), big.NewInt(0), big.NewInt(0) - } - if size.Sign() == 1 { - uPnl = new(big.Int).Sub(notionalPos, openNotional) - } else { - uPnl = new(big.Int).Sub(openNotional, notionalPos) - } - marginFraction = new(big.Int).Div(hu.Mul1e6(new(big.Int).Add(margin, uPnl)), notionalPos) - return notionalPos, uPnl, marginFraction -} - // Common Utils func fromTwosComplement(b []byte) *big.Int { t := new(big.Int).SetBytes(b) diff --git a/precompile/contracts/bibliophile/clearing_house.go b/precompile/contracts/bibliophile/clearing_house.go index 35f4e4b965..fe7c926e79 100644 --- a/precompile/contracts/bibliophile/clearing_house.go +++ b/precompile/contracts/bibliophile/clearing_house.go @@ -65,28 +65,27 @@ type GetNotionalPositionAndMarginOutput struct { } func getNotionalPositionAndMargin(stateDB contract.StateDB, input *GetNotionalPositionAndMarginInput) GetNotionalPositionAndMarginOutput { - margin := GetNormalizedMargin(stateDB, input.Trader) + markets := GetMarkets(stateDB) + numMarkets := len(markets) + positions := make(map[int]*hu.Position, numMarkets) + underlyingPrices := make(map[int]*big.Int, numMarkets) + lastPrices := make(map[int]*big.Int, numMarkets) + var marketIds []int + for i, market := range markets { + positions[i] = getPosition(stateDB, getMarketAddressFromMarketID(int64(i), stateDB), &input.Trader) + underlyingPrices[i] = getUnderlyingPrice(stateDB, market) + lastPrices[i] = getLastPrice(stateDB, market) + marketIds = append(marketIds, i) + } + pendingFunding := big.NewInt(0) if input.IncludeFundingPayments { - margin.Sub(margin, GetTotalFunding(stateDB, &input.Trader)) + pendingFunding = GetTotalFunding(stateDB, &input.Trader) } - notionalPosition, unrealizedPnl := GetTotalNotionalPositionAndUnrealizedPnl(stateDB, &input.Trader, margin, GetMarginMode(input.Mode)) + notionalPosition, margin := hu.GetNotionalPositionAndMargin(positions, getMargins(stateDB, input.Trader), input.Mode, pendingFunding, GetCollaterals(stateDB), underlyingPrices, lastPrices, marketIds) return GetNotionalPositionAndMarginOutput{ NotionalPosition: notionalPosition, - Margin: hu.Add(margin, unrealizedPnl), - } -} - -func GetTotalNotionalPositionAndUnrealizedPnl(stateDB contract.StateDB, trader *common.Address, margin *big.Int, marginMode MarginMode) (*big.Int, *big.Int) { - notionalPosition := big.NewInt(0) - unrealizedPnl := big.NewInt(0) - for _, market := range GetMarkets(stateDB) { - lastPrice := getLastPrice(stateDB, market) - oraclePrice := getUnderlyingPrice(stateDB, market) - _notionalPosition, _unrealizedPnl := getOptimalPnl(stateDB, market, oraclePrice, lastPrice, trader, margin, marginMode) - notionalPosition.Add(notionalPosition, _notionalPosition) - unrealizedPnl.Add(unrealizedPnl, _unrealizedPnl) + Margin: margin, } - return notionalPosition, unrealizedPnl } func GetTotalFunding(stateDB contract.StateDB, trader *common.Address) *big.Int { @@ -120,14 +119,6 @@ func GetUnderlyingPrices(stateDB contract.StateDB) []*big.Int { return underlyingPrices } -func GetCollateralPrices(stateDB contract.StateDB) []*big.Int { - underlyingPrices := make([]*big.Int, 0) - // for _, market := range GetMarkets(stateDB) { - // underlyingPrices = append(underlyingPrices, getUnderlyingPrice(stateDB, market)) - // } - return underlyingPrices -} - func getPosSizes(stateDB contract.StateDB, trader *common.Address) []*big.Int { positionSizes := make([]*big.Int, 0) for _, market := range GetMarkets(stateDB) { diff --git a/precompile/contracts/bibliophile/client.go b/precompile/contracts/bibliophile/client.go index 853990b247..9afc10102a 100644 --- a/precompile/contracts/bibliophile/client.go +++ b/precompile/contracts/bibliophile/client.go @@ -3,7 +3,7 @@ package bibliophile import ( "math/big" - hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" + // hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" "github.com/ava-labs/subnet-evm/precompile/contract" "github.com/ethereum/go-ethereum/common" ) diff --git a/precompile/contracts/bibliophile/margin_account.go b/precompile/contracts/bibliophile/margin_account.go index 9823f4e2e0..2d4a488199 100644 --- a/precompile/contracts/bibliophile/margin_account.go +++ b/precompile/contracts/bibliophile/margin_account.go @@ -19,13 +19,18 @@ const ( ) func GetNormalizedMargin(stateDB contract.StateDB, trader common.Address) *big.Int { - assets := getCollaterals(stateDB) - numAssets := len(assets) - margin := make([]*big.Int, numAssets) - for i := 0; i < numAssets; i++ { - margin[i] = getMargin(stateDB, big.NewInt(int64(i)), trader) + assets := GetCollaterals(stateDB) + margins := getMargins(stateDB, trader) + return hu.GetNormalizedMargin(assets, margins) +} + +func getMargins(stateDB contract.StateDB, trader common.Address) []*big.Int { + numAssets := getCollateralCount(stateDB) + margins := make([]*big.Int, numAssets) + for i := uint8(0); i < numAssets; i++ { + margins[i] = getMargin(stateDB, big.NewInt(int64(i)), trader) } - return hu.GetNormalizedMargin(assets, margin) + return margins } func getMargin(stateDB contract.StateDB, idx *big.Int, trader common.Address) *big.Int { @@ -40,15 +45,8 @@ func getReservedMargin(stateDB contract.StateDB, trader common.Address) *big.Int } func GetAvailableMargin(stateDB contract.StateDB, trader common.Address) *big.Int { - includeFundingPayment := true - mode := uint8(1) // Min_Allowable_Margin - output := getNotionalPositionAndMargin(stateDB, &GetNotionalPositionAndMarginInput{Trader: trader, IncludeFundingPayments: includeFundingPayment, Mode: mode}) - notionalPostion := output.NotionalPosition - margin := output.Margin - utitlizedMargin := hu.Div1e6(big.NewInt(0).Mul(notionalPostion, GetMinAllowableMargin(stateDB))) - reservedMargin := getReservedMargin(stateDB, trader) - // log.Info("GetAvailableMargin", "trader", trader, "notionalPostion", notionalPostion, "margin", margin, "utitlizedMargin", utitlizedMargin, "reservedMargin", reservedMargin) - return big.NewInt(0).Sub(big.NewInt(0).Sub(margin, utitlizedMargin), reservedMargin) + output := getNotionalPositionAndMargin(stateDB, &GetNotionalPositionAndMarginInput{Trader: trader, IncludeFundingPayments: true, Mode: uint8(1)}) // Min_Allowable_Margin + return hu.GetAvailableMargin_(output.NotionalPosition, output.Margin, getReservedMargin(stateDB, trader), GetMinAllowableMargin(stateDB)) } func getOracleAddress(stateDB contract.StateDB) common.Address { From 9436a10c7e48930e2c52d282d0d4015102da18b8 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Tue, 26 Sep 2023 09:05:25 +0100 Subject: [PATCH 03/11] fix tests --- .../evm/orderbook/hubbleutils/margin_math.go | 8 +- plugin/evm/orderbook/liquidations_test.go | 94 ++++++++++--------- plugin/evm/orderbook/memory_database_test.go | 17 ++-- plugin/evm/orderbook/mocks.go | 12 +-- precompile/contracts/bibliophile/client.go | 5 - 5 files changed, 67 insertions(+), 69 deletions(-) diff --git a/plugin/evm/orderbook/hubbleutils/margin_math.go b/plugin/evm/orderbook/hubbleutils/margin_math.go index aa840cbe50..6660953dfb 100644 --- a/plugin/evm/orderbook/hubbleutils/margin_math.go +++ b/plugin/evm/orderbook/hubbleutils/margin_math.go @@ -56,7 +56,7 @@ func WeightedAndSpotCollateral(assets []Collateral, margins []*big.Int) (weighte weighted = big.NewInt(0) spot = big.NewInt(0) for i, asset := range assets { - if margins[i].Sign() == 0 { + if margins[i] == nil || margins[i].Sign() == 0 { continue } numerator := Mul(margins[i], asset.Price) // margin[i] is scaled by asset.Decimal @@ -73,7 +73,7 @@ func GetAvailableMargin(positions map[Market]*Position, margins []*big.Int, pend func GetAvailableMargin_(notionalPosition, margin, reservedMargin, minAllowableMargin *big.Int) *big.Int { utilisedMargin := Div1e6(Mul(notionalPosition, minAllowableMargin)) - return Sub(margin, Add(utilisedMargin, reservedMargin)) + return Sub(Sub(margin, utilisedMargin), reservedMargin) } type MarginMode = uint8 @@ -133,7 +133,7 @@ func GetOptimalPnl(market Market, oraclePrice *big.Int, lastPrice *big.Int, posi func GetPositionMetadata(price *big.Int, openNotional *big.Int, size *big.Int, margin *big.Int) (notionalPosition *big.Int, unrealisedPnl *big.Int, marginFraction *big.Int) { // log.Info("in GetPositionMetadata", "price", price, "openNotional", openNotional, "size", size, "margin", margin) - notionalPosition = getNotionalPosition(price, size) + notionalPosition = GetNotionalPosition(price, size) uPnL := new(big.Int) if notionalPosition.Cmp(big.NewInt(0)) == 0 { return big.NewInt(0), big.NewInt(0), big.NewInt(0) @@ -147,6 +147,6 @@ func GetPositionMetadata(price *big.Int, openNotional *big.Int, size *big.Int, m return notionalPosition, uPnL, mf } -func getNotionalPosition(price *big.Int, size *big.Int) *big.Int { +func GetNotionalPosition(price *big.Int, size *big.Int) *big.Int { return big.NewInt(0).Abs(Div1e18(Mul(size, price))) } diff --git a/plugin/evm/orderbook/liquidations_test.go b/plugin/evm/orderbook/liquidations_test.go index aba124bb96..7f12d5399d 100644 --- a/plugin/evm/orderbook/liquidations_test.go +++ b/plugin/evm/orderbook/liquidations_test.go @@ -12,10 +12,11 @@ import ( func TestGetLiquidableTraders(t *testing.T) { var market Market = Market(0) collateral := HUSD + assets := []hu.Collateral{{Price: big.NewInt(1e6), Weight: big.NewInt(1e6), Decimals: 6}} t.Run("When no trader exist", func(t *testing.T) { db := getDatabase() oraclePrices := map[Market]*big.Int{market: hu.Mul1e6(big.NewInt(110))} - liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, []Market{market}) + liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, assets, []Market{market}) assert.Equal(t, 0, len(liquidablePositions)) }) @@ -34,7 +35,7 @@ func TestGetLiquidableTraders(t *testing.T) { } db.LastPrice = map[Market]*big.Int{market: hu.Mul1e6(big.NewInt(100))} oraclePrices := map[Market]*big.Int{market: hu.Mul1e6(big.NewInt(110))} - liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, []Market{market}) + liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, assets, []Market{market}) assert.Equal(t, 0, len(liquidablePositions)) }) @@ -66,31 +67,31 @@ func TestGetLiquidableTraders(t *testing.T) { // assertions begin // for long trader _trader := &longTrader - assert.Equal(t, marginLong, getNormalisedMargin(_trader)) + assert.Equal(t, marginLong, getNormalisedMargin(_trader, assets)) assert.Equal(t, pendingFundingLong, getTotalFunding(_trader, []Market{market})) // open notional = 90 * 10 = 900 // last price: notional = 50 * 10 = 500, pnl = 500-900 = -400, mf = (500-42-400)/500 = 0.116 // oracle price: notional = 49 * 10 = 490, pnl = 490-900 = -410, mf = (500-42-410)/490 = 0.097 - // for Min_Allowable_Margin we select the min of 2 hence orale_mf - notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginLong, pendingFundingLong), Min_Allowable_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) + // for hu.Min_Allowable_Margin we select the min of 2 hence orale_mf + notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginLong, pendingFundingLong), hu.Min_Allowable_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, hu.Mul1e6(big.NewInt(490)), notionalPosition) assert.Equal(t, hu.Mul1e6(big.NewInt(-410)), unrealizePnL) - availableMargin := getAvailableMargin(_trader, pendingFundingLong, oraclePrices, db.GetLastPrices(), db.configService.getMinAllowableMargin(), []Market{market}) + availableMargin := getAvailableMargin(_trader, pendingFundingLong, assets, oraclePrices, db.GetLastPrices(), db.configService.getMinAllowableMargin(), []Market{market}) // availableMargin = 500 - 42 (pendingFundingLong) - 410 (uPnL) - 490/5 = -50 assert.Equal(t, hu.Mul1e6(big.NewInt(-50)), availableMargin) - // for Maintenance_Margin we select the max of 2 hence, last_mf - notionalPosition, unrealizePnL = getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginLong, pendingFundingLong), Maintenance_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) + // for hu.Maintenance_Margin we select the max of 2 hence, last_mf + notionalPosition, unrealizePnL = getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginLong, pendingFundingLong), hu.Maintenance_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, hu.Mul1e6(big.NewInt(500)), notionalPosition) assert.Equal(t, hu.Mul1e6(big.NewInt(-400)), unrealizePnL) - marginFraction := calcMarginFraction(_trader, pendingFundingLong, oraclePrices, db.GetLastPrices(), []Market{market}) + marginFraction := calcMarginFraction(_trader, pendingFundingLong, assets, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, new(big.Int).Div(hu.Mul1e6(new(big.Int).Add(new(big.Int).Sub(marginLong, pendingFundingLong), unrealizePnL)), notionalPosition), marginFraction) - liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, []Market{market}) + liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, assets, []Market{market}) assert.Equal(t, 0, len(liquidablePositions)) }) @@ -115,31 +116,31 @@ func TestGetLiquidableTraders(t *testing.T) { // assertions begin // for long trader _trader := &longTrader - assert.Equal(t, marginLong, getNormalisedMargin(_trader)) + assert.Equal(t, marginLong, getNormalisedMargin(_trader, assets)) assert.Equal(t, pendingFundingLong, getTotalFunding(_trader, []Market{market})) // open notional = 90 * 10 = 900 // last price: notional = 49 * 10 = 490, pnl = 490-900 = -410, mf = (500-42-410)/490 = 0.097 // oracle price: notional = 50 * 10 = 500, pnl = 500-900 = -400, mf = (500-42-400)/500 = 0.116 - // for Min_Allowable_Margin we select the min of 2 hence last_mf - notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginLong, pendingFundingLong), Min_Allowable_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) + // for hu.Min_Allowable_Margin we select the min of 2 hence last_mf + notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginLong, pendingFundingLong), hu.Min_Allowable_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, hu.Mul1e6(big.NewInt(490)), notionalPosition) assert.Equal(t, hu.Mul1e6(big.NewInt(-410)), unrealizePnL) - availableMargin := getAvailableMargin(_trader, pendingFundingLong, oraclePrices, db.GetLastPrices(), db.configService.getMinAllowableMargin(), []Market{market}) + availableMargin := getAvailableMargin(_trader, pendingFundingLong, assets, oraclePrices, db.GetLastPrices(), db.configService.getMinAllowableMargin(), []Market{market}) // availableMargin = 500 - 42 (pendingFundingLong) - 410 (uPnL) - 490/5 = -50 assert.Equal(t, hu.Mul1e6(big.NewInt(-50)), availableMargin) - // for Maintenance_Margin we select the max of 2 hence, oracle_mf - notionalPosition, unrealizePnL = getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginLong, pendingFundingLong), Maintenance_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) + // for hu.Maintenance_Margin we select the max of 2 hence, oracle_mf + notionalPosition, unrealizePnL = getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginLong, pendingFundingLong), hu.Maintenance_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, hu.Mul1e6(big.NewInt(500)), notionalPosition) assert.Equal(t, hu.Mul1e6(big.NewInt(-400)), unrealizePnL) - marginFraction := calcMarginFraction(_trader, pendingFundingLong, oraclePrices, db.GetLastPrices(), []Market{market}) + marginFraction := calcMarginFraction(_trader, pendingFundingLong, assets, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, new(big.Int).Div(hu.Mul1e6(new(big.Int).Add(new(big.Int).Sub(marginLong, pendingFundingLong), unrealizePnL)), notionalPosition), marginFraction) - liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, []Market{market}) + liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, assets, []Market{market}) assert.Equal(t, 0, len(liquidablePositions)) }) }) @@ -171,31 +172,31 @@ func TestGetLiquidableTraders(t *testing.T) { // assertions begin _trader := &shortTrader - assert.Equal(t, marginShort, getNormalisedMargin(_trader)) + assert.Equal(t, marginShort, getNormalisedMargin(_trader, assets)) assert.Equal(t, pendingFundingShort, getTotalFunding(_trader, []Market{market})) // open notional = 105 * 20 = 2100 // last price: notional = 142 * 20 = 2840, pnl = 2100-2840 = -740, mf = (1000+37-740)/2840 = 0.104 // oracle price based notional = 143 * 20 = 2860, pnl = 2100-2860 = -760, mf = (1000+37-760)/2860 = 0.096 - // for Min_Allowable_Margin we select the min of 2 hence, oracle_mf - notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginShort, pendingFundingShort), Min_Allowable_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) + // for hu.Min_Allowable_Margin we select the min of 2 hence, oracle_mf + notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginShort, pendingFundingShort), hu.Min_Allowable_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, hu.Mul1e6(big.NewInt(2860)), notionalPosition) assert.Equal(t, hu.Mul1e6(big.NewInt(-760)), unrealizePnL) - availableMargin := getAvailableMargin(_trader, pendingFundingShort, oraclePrices, db.GetLastPrices(), db.configService.getMinAllowableMargin(), []Market{market}) + availableMargin := getAvailableMargin(_trader, pendingFundingShort, assets, oraclePrices, db.GetLastPrices(), db.configService.getMinAllowableMargin(), []Market{market}) // availableMargin = 1000 + 37 (pendingFundingShort) -760 (uPnL) - 2860/5 = -295 assert.Equal(t, hu.Mul1e6(big.NewInt(-295)), availableMargin) - // for Maintenance_Margin we select the max of 2 hence, last_mf - notionalPosition, unrealizePnL = getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginShort, pendingFundingShort), Maintenance_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) + // for hu.Maintenance_Margin we select the max of 2 hence, last_mf + notionalPosition, unrealizePnL = getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginShort, pendingFundingShort), hu.Maintenance_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, hu.Mul1e6(big.NewInt(2840)), notionalPosition) assert.Equal(t, hu.Mul1e6(big.NewInt(-740)), unrealizePnL) - marginFraction := calcMarginFraction(_trader, pendingFundingShort, oraclePrices, db.GetLastPrices(), []Market{market}) + marginFraction := calcMarginFraction(_trader, pendingFundingShort, assets, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, new(big.Int).Div(hu.Mul1e6(new(big.Int).Add(new(big.Int).Sub(marginShort, pendingFundingShort), unrealizePnL)), notionalPosition), marginFraction) - liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, []Market{market}) + liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, assets, []Market{market}) assert.Equal(t, 0, len(liquidablePositions)) }) @@ -219,40 +220,41 @@ func TestGetLiquidableTraders(t *testing.T) { // assertions begin _trader := &shortTrader - assert.Equal(t, marginShort, getNormalisedMargin(_trader)) + assert.Equal(t, marginShort, getNormalisedMargin(_trader, assets)) assert.Equal(t, pendingFundingShort, getTotalFunding(_trader, []Market{market})) // open notional = 105 * 20 = 2100 // last price: = 143 * 20 = 2860, pnl = 2100-2860 = -760, mf = (1000+37-760)/2860 = 0.096 // oracle price: notional = 142 * 20 = 2840, pnl = 2100-2840 = -740, mf = (1000+37-740)/2840 = 0.104 - // for Min_Allowable_Margin we select the min of 2 hence, last_mf - notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginShort, pendingFundingShort), Min_Allowable_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) + // for hu.Min_Allowable_Margin we select the min of 2 hence, last_mf + notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginShort, pendingFundingShort), hu.Min_Allowable_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, hu.Mul1e6(big.NewInt(2860)), notionalPosition) assert.Equal(t, hu.Mul1e6(big.NewInt(-760)), unrealizePnL) - availableMargin := getAvailableMargin(_trader, pendingFundingShort, oraclePrices, db.GetLastPrices(), db.configService.getMinAllowableMargin(), []Market{market}) + availableMargin := getAvailableMargin(_trader, pendingFundingShort, assets, oraclePrices, db.GetLastPrices(), db.configService.getMinAllowableMargin(), []Market{market}) // availableMargin = 1000 + 37 (pendingFundingShort) - 760 (uPnL) - 2860/5 = -295 assert.Equal(t, hu.Mul1e6(big.NewInt(-295)), availableMargin) - // for Maintenance_Margin we select the max of 2 hence, oracle_mf - notionalPosition, unrealizePnL = getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginShort, pendingFundingShort), Maintenance_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) + // for hu.Maintenance_Margin we select the max of 2 hence, oracle_mf + notionalPosition, unrealizePnL = getTotalNotionalPositionAndUnrealizedPnl(_trader, new(big.Int).Add(marginShort, pendingFundingShort), hu.Maintenance_Margin, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, hu.Mul1e6(big.NewInt(2840)), notionalPosition) assert.Equal(t, hu.Mul1e6(big.NewInt(-740)), unrealizePnL) - marginFraction := calcMarginFraction(_trader, pendingFundingShort, oraclePrices, db.GetLastPrices(), []Market{market}) + marginFraction := calcMarginFraction(_trader, pendingFundingShort, assets, oraclePrices, db.GetLastPrices(), []Market{market}) assert.Equal(t, new(big.Int).Div(hu.Mul1e6(new(big.Int).Add(new(big.Int).Sub(marginShort, pendingFundingShort), unrealizePnL)), notionalPosition), marginFraction) - liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, []Market{market}) + liquidablePositions, _ := db.GetNaughtyTraders(oraclePrices, assets, []Market{market}) assert.Equal(t, 0, len(liquidablePositions)) }) }) } func TestGetNormalisedMargin(t *testing.T) { + assets := []hu.Collateral{{Price: big.NewInt(1e6), Weight: big.NewInt(1e6), Decimals: 6}} t.Run("When trader has no margin", func(t *testing.T) { trader := Trader{} - assert.Equal(t, trader.Margin.Deposited[HUSD], getNormalisedMargin(&trader)) + assert.Equal(t, big.NewInt(0), getNormalisedMargin(&trader, assets)) }) t.Run("When trader has margin in HUSD", func(t *testing.T) { margin := hu.Mul1e6(big.NewInt(10)) @@ -261,7 +263,7 @@ func TestGetNormalisedMargin(t *testing.T) { HUSD: margin, }}, } - assert.Equal(t, margin, getNormalisedMargin(&trader)) + assert.Equal(t, margin, getNormalisedMargin(&trader, assets)) }) } @@ -270,13 +272,13 @@ func TestGetNotionalPosition(t *testing.T) { price := hu.Mul1e6(big.NewInt(10)) size := hu.Mul1e18(big.NewInt(20)) expectedNotionalPosition := hu.Div1e18(big.NewInt(0).Mul(price, size)) - assert.Equal(t, expectedNotionalPosition, getNotionalPosition(price, size)) + assert.Equal(t, expectedNotionalPosition, hu.GetNotionalPosition(price, size)) }) t.Run("When size is negative, it return abs value", func(t *testing.T) { price := hu.Mul1e6(big.NewInt(10)) size := hu.Mul1e18(big.NewInt(-20)) expectedNotionalPosition := hu.Div1e18(big.NewInt(0).Abs(big.NewInt(0).Mul(price, size))) - assert.Equal(t, expectedNotionalPosition, getNotionalPosition(price, size)) + assert.Equal(t, expectedNotionalPosition, hu.GetNotionalPosition(price, size)) }) } @@ -288,12 +290,12 @@ func TestGetPositionMetadata(t *testing.T) { newPrice := hu.Mul1e6(big.NewInt(15)) position := &Position{ Size: size, - OpenNotional: getNotionalPosition(entryPrice, size), + OpenNotional: hu.GetNotionalPosition(entryPrice, size), } arbitaryMarginValue := hu.Mul1e6(big.NewInt(69)) notionalPosition, uPnL, mf := getPositionMetadata(newPrice, position.OpenNotional, position.Size, arbitaryMarginValue) - assert.Equal(t, getNotionalPosition(newPrice, size), notionalPosition) + assert.Equal(t, hu.GetNotionalPosition(newPrice, size), notionalPosition) expectedPnl := hu.Div1e18(big.NewInt(0).Mul(big.NewInt(0).Sub(newPrice, entryPrice), size)) assert.Equal(t, expectedPnl, uPnL) assert.Equal(t, new(big.Int).Div(hu.Mul1e6(new(big.Int).Add(arbitaryMarginValue, uPnL)), notionalPosition), mf) @@ -304,11 +306,11 @@ func TestGetPositionMetadata(t *testing.T) { newPrice := hu.Mul1e6(big.NewInt(15)) position := &Position{ Size: size, - OpenNotional: getNotionalPosition(entryPrice, size), + OpenNotional: hu.GetNotionalPosition(entryPrice, size), } notionalPosition, uPnL, _ := getPositionMetadata(newPrice, position.OpenNotional, position.Size, big.NewInt(0)) - assert.Equal(t, getNotionalPosition(newPrice, size), notionalPosition) + assert.Equal(t, hu.GetNotionalPosition(newPrice, size), notionalPosition) expectedPnl := hu.Div1e18(big.NewInt(0).Mul(big.NewInt(0).Sub(newPrice, entryPrice), size)) assert.Equal(t, expectedPnl, uPnL) }) @@ -320,11 +322,11 @@ func TestGetPositionMetadata(t *testing.T) { newPrice := hu.Mul1e6(big.NewInt(5)) position := &Position{ Size: size, - OpenNotional: getNotionalPosition(entryPrice, size), + OpenNotional: hu.GetNotionalPosition(entryPrice, size), } notionalPosition, uPnL, _ := getPositionMetadata(newPrice, position.OpenNotional, position.Size, big.NewInt(0)) - assert.Equal(t, getNotionalPosition(newPrice, size), notionalPosition) + assert.Equal(t, hu.GetNotionalPosition(newPrice, size), notionalPosition) expectedPnl := hu.Div1e18(big.NewInt(0).Mul(big.NewInt(0).Sub(newPrice, entryPrice), size)) assert.Equal(t, expectedPnl, uPnL) }) @@ -334,10 +336,10 @@ func TestGetPositionMetadata(t *testing.T) { newPrice := hu.Mul1e6(big.NewInt(5)) position := &Position{ Size: size, - OpenNotional: getNotionalPosition(entryPrice, size), + OpenNotional: hu.GetNotionalPosition(entryPrice, size), } notionalPosition, uPnL, _ := getPositionMetadata(newPrice, position.OpenNotional, position.Size, big.NewInt(0)) - assert.Equal(t, getNotionalPosition(newPrice, size), notionalPosition) + assert.Equal(t, hu.GetNotionalPosition(newPrice, size), notionalPosition) expectedPnl := hu.Div1e18(big.NewInt(0).Mul(big.NewInt(0).Sub(newPrice, entryPrice), size)) assert.Equal(t, expectedPnl, uPnL) }) diff --git a/plugin/evm/orderbook/memory_database_test.go b/plugin/evm/orderbook/memory_database_test.go index bafd3b2f2c..ba0377520f 100644 --- a/plugin/evm/orderbook/memory_database_test.go +++ b/plugin/evm/orderbook/memory_database_test.go @@ -20,6 +20,7 @@ var status Status = Placed var blockNumber = big.NewInt(2) var market = Market(0) +var assets = []hu.Collateral{{Price: big.NewInt(1e6), Weight: big.NewInt(1e6), Decimals: 6}} func TestgetDatabase(t *testing.T) { inMemoryDatabase := getDatabase() @@ -405,27 +406,27 @@ func TestGetCancellableOrders(t *testing.T) { // Setup completed, assertions start here _trader := inMemoryDatabase.TraderMap[trader] assert.Equal(t, big.NewInt(0), getTotalFunding(_trader, []Market{market})) - assert.Equal(t, depositMargin, getNormalisedMargin(_trader)) + assert.Equal(t, depositMargin, getNormalisedMargin(_trader, assets)) // last price based notional = 9 * 10 = 90, pnl = 0, mf = (40-0)/90 = 0.44 // oracle price based notional = 9 * 11 = 99, pnl = -9, mf = (40-9)/99 = 0.31 - // for Min_Allowable_Margin we select the min of 2 hence, oracle based mf - notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(_trader, depositMargin, Min_Allowable_Margin, priceMap, inMemoryDatabase.GetLastPrices(), []Market{market}) + // for hu.Min_Allowable_Margin we select the min of 2 hence, oracle based mf + notionalPosition, unrealizePnL := getTotalNotionalPositionAndUnrealizedPnl(_trader, depositMargin, hu.Min_Allowable_Margin, priceMap, inMemoryDatabase.GetLastPrices(), []Market{market}) assert.Equal(t, hu.Mul1e6(big.NewInt(99)), notionalPosition) assert.Equal(t, hu.Mul1e6(big.NewInt(-9)), unrealizePnL) - // for Maintenance_Margin we select the max of 2 hence, last price based mf - notionalPosition, unrealizePnL = getTotalNotionalPositionAndUnrealizedPnl(_trader, depositMargin, Maintenance_Margin, priceMap, inMemoryDatabase.GetLastPrices(), []Market{market}) + // for hu.Maintenance_Margin we select the max of 2 hence, last price based mf + notionalPosition, unrealizePnL = getTotalNotionalPositionAndUnrealizedPnl(_trader, depositMargin, hu.Maintenance_Margin, priceMap, inMemoryDatabase.GetLastPrices(), []Market{market}) assert.Equal(t, hu.Mul1e6(big.NewInt(90)), notionalPosition) assert.Equal(t, big.NewInt(0), unrealizePnL) - marginFraction := calcMarginFraction(_trader, big.NewInt(0), priceMap, inMemoryDatabase.GetLastPrices(), []Market{market}) + marginFraction := calcMarginFraction(_trader, big.NewInt(0), assets, priceMap, inMemoryDatabase.GetLastPrices(), []Market{market}) assert.Equal(t, new(big.Int).Div(hu.Mul1e6(depositMargin /* uPnL = 0 */), notionalPosition), marginFraction) - availableMargin := getAvailableMargin(_trader, big.NewInt(0), priceMap, inMemoryDatabase.GetLastPrices(), inMemoryDatabase.configService.getMinAllowableMargin(), []Market{market}) + availableMargin := getAvailableMargin(_trader, big.NewInt(0), assets, priceMap, inMemoryDatabase.GetLastPrices(), inMemoryDatabase.configService.getMinAllowableMargin(), []Market{market}) // availableMargin = 40 - 9 - (99 + (10+9+8) * 3)/5 = -5 assert.Equal(t, hu.Mul1e6(big.NewInt(-5)), availableMargin) - _, ordersToCancel := inMemoryDatabase.GetNaughtyTraders(priceMap, []Market{market}) + _, ordersToCancel := inMemoryDatabase.GetNaughtyTraders(priceMap, assets, []Market{market}) // t.Log("####", "ordersToCancel", ordersToCancel) assert.Equal(t, 1, len(ordersToCancel)) // only one trader diff --git a/plugin/evm/orderbook/mocks.go b/plugin/evm/orderbook/mocks.go index 3dc444d538..8a6c754ad0 100644 --- a/plugin/evm/orderbook/mocks.go +++ b/plugin/evm/orderbook/mocks.go @@ -4,6 +4,7 @@ import ( "math/big" "github.com/ava-labs/subnet-evm/core/types" + hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/mock" ) @@ -115,7 +116,7 @@ func (db *MockLimitOrderDatabase) GetLastPrices() map[Market]*big.Int { return map[Market]*big.Int{} } -func (db *MockLimitOrderDatabase) GetNaughtyTraders(oraclePrices map[Market]*big.Int, markets []Market) ([]LiquidablePosition, map[common.Address][]Order) { +func (db *MockLimitOrderDatabase) GetNaughtyTraders(oraclePrices map[Market]*big.Int, assets []hu.Collateral, markets []Market) ([]LiquidablePosition, map[common.Address][]Order) { return []LiquidablePosition{}, map[common.Address][]Order{} } @@ -225,11 +226,6 @@ func (mcs *MockConfigService) GetAcceptableBoundsForLiquidation(market Market) ( return args.Get(0).(*big.Int), args.Get(1).(*big.Int) } -// func (mcs *MockConfigService) getOracleSpreadThreshold(market Market) *big.Int { -// args := mcs.Called() -// return args.Get(0).(*big.Int) -// } - func (mcs *MockConfigService) getLiquidationSpreadThreshold(market Market) *big.Int { return big.NewInt(1e4) } @@ -277,6 +273,10 @@ func (cs *MockConfigService) GetCumulativePremiumFractionAtBlock(market Market, return big.NewInt(0) } +func (cs *MockConfigService) GetCollaterals() []hu.Collateral { + return []hu.Collateral{{Price: big.NewInt(1e6), Weight: big.NewInt(1e6), Decimals: 6}} +} + func NewMockConfigService() *MockConfigService { return &MockConfigService{} } diff --git a/precompile/contracts/bibliophile/client.go b/precompile/contracts/bibliophile/client.go index 9afc10102a..64acad0baf 100644 --- a/precompile/contracts/bibliophile/client.go +++ b/precompile/contracts/bibliophile/client.go @@ -49,7 +49,6 @@ type BibliophileClient interface { GetAccessibleState() contract.AccessibleState GetNotionalPositionAndMargin(trader common.Address, includeFundingPayments bool, mode uint8) (*big.Int, *big.Int) HasReferrer(trader common.Address) bool - // GetCollaterals() []hu.Collateral } // Define a structure that will implement the Bibliophile interface @@ -191,7 +190,3 @@ func (b *bibliophileClient) GetNotionalPositionAndMargin(trader common.Address, func (b *bibliophileClient) HasReferrer(trader common.Address) bool { return hasReferrer(b.accessibleState.GetStateDB(), trader) } - -// func (b *bibliophileClient) GetCollaterals() []hu.Collateral { -// return getCollaterals(b.accessibleState.GetStateDB()) -// } From e0dbd9614a573a3af66d5b328c2b8827981f128b Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Tue, 26 Sep 2023 09:39:00 +0100 Subject: [PATCH 04/11] update slot --- precompile/contracts/bibliophile/amm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precompile/contracts/bibliophile/amm.go b/precompile/contracts/bibliophile/amm.go index 8f9541509b..d922e72624 100644 --- a/precompile/contracts/bibliophile/amm.go +++ b/precompile/contracts/bibliophile/amm.go @@ -28,7 +28,7 @@ const ( const ( // this slot is from TestOracle.sol - TEST_ORACLE_PRICES_MAPPING_SLOT int64 = 3 + TEST_ORACLE_PRICES_MAPPING_SLOT int64 = 4 ) // AMM State From 06a8e764cb079d1a7f2f44c5e52c95d757e90985 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Tue, 26 Sep 2023 10:12:21 +0100 Subject: [PATCH 05/11] rename vars --- precompile/contracts/bibliophile/margin_account.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/precompile/contracts/bibliophile/margin_account.go b/precompile/contracts/bibliophile/margin_account.go index 2d4a488199..9ffe1ba158 100644 --- a/precompile/contracts/bibliophile/margin_account.go +++ b/precompile/contracts/bibliophile/margin_account.go @@ -14,8 +14,8 @@ const ( MARGIN_ACCOUNT_GENESIS_ADDRESS = "0x0300000000000000000000000000000000000001" ORACLE_SLOT int64 = 4 SUPPORTED_COLLATERAL_SLOT int64 = 8 - VAR_MARGIN_MAPPING_SLOT int64 = 10 - VAR_RESERVED_MARGIN_SLOT int64 = 11 + MARGIN_MAPPING_SLOT int64 = 10 + RESERVED_MARGIN_SLOT int64 = 11 ) func GetNormalizedMargin(stateDB contract.StateDB, trader common.Address) *big.Int { @@ -34,13 +34,13 @@ func getMargins(stateDB contract.StateDB, trader common.Address) []*big.Int { } func getMargin(stateDB contract.StateDB, idx *big.Int, trader common.Address) *big.Int { - marginStorageSlot := crypto.Keccak256(append(common.LeftPadBytes(idx.Bytes(), 32), common.LeftPadBytes(big.NewInt(VAR_MARGIN_MAPPING_SLOT).Bytes(), 32)...)) + marginStorageSlot := crypto.Keccak256(append(common.LeftPadBytes(idx.Bytes(), 32), common.LeftPadBytes(big.NewInt(MARGIN_MAPPING_SLOT).Bytes(), 32)...)) marginStorageSlot = crypto.Keccak256(append(common.LeftPadBytes(trader.Bytes(), 32), marginStorageSlot...)) return fromTwosComplement(stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BytesToHash(marginStorageSlot)).Bytes()) } func getReservedMargin(stateDB contract.StateDB, trader common.Address) *big.Int { - baseMappingHash := crypto.Keccak256(append(common.LeftPadBytes(trader.Bytes(), 32), common.LeftPadBytes(big.NewInt(VAR_RESERVED_MARGIN_SLOT).Bytes(), 32)...)) + baseMappingHash := crypto.Keccak256(append(common.LeftPadBytes(trader.Bytes(), 32), common.LeftPadBytes(big.NewInt(RESERVED_MARGIN_SLOT).Bytes(), 32)...)) return stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BytesToHash(baseMappingHash)).Big() } From b5afa43000ece98ba81d5edfa79af9dde4c384fc Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Wed, 27 Sep 2023 09:22:52 +0100 Subject: [PATCH 06/11] cleanup comments --- plugin/evm/orderbook/memory_database.go | 30 ++-------------------- precompile/contracts/bibliophile/client.go | 1 - 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index 6999f66f84..b467625796 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -51,11 +51,9 @@ const ( RETRY_AFTER_BLOCKS = 10 ) -type Market = hu.Market // int64 +type Market = hu.Market -// type Market int64 - -type Collateral int +type Collateral = int const ( HUSD Collateral = iota @@ -183,30 +181,6 @@ func (order Order) ToOrderMin() OrderMin { type Position = hu.Position -// { -// OpenNotional *big.Int `json:"open_notional"` -// Size *big.Int `json:"size"` -// UnrealisedFunding *big.Int `json:"unrealised_funding"` -// LastPremiumFraction *big.Int `json:"last_premium_fraction"` -// LiquidationThreshold *big.Int `json:"liquidation_threshold"` -// } - -// func (p *Position) MarshalJSON() ([]byte, error) { -// return json.Marshal(&struct { -// OpenNotional string `json:"open_notional"` -// Size string `json:"size"` -// UnrealisedFunding string `json:"unrealised_funding"` -// LastPremiumFraction string `json:"last_premium_fraction"` -// LiquidationThreshold string `json:"liquidation_threshold"` -// }{ -// OpenNotional: p.OpenNotional.String(), -// Size: p.Size.String(), -// UnrealisedFunding: p.UnrealisedFunding.String(), -// LastPremiumFraction: p.LastPremiumFraction.String(), -// LiquidationThreshold: p.LiquidationThreshold.String(), -// }) -// } - type Margin struct { Reserved *big.Int `json:"reserved"` Deposited map[Collateral]*big.Int `json:"deposited"` diff --git a/precompile/contracts/bibliophile/client.go b/precompile/contracts/bibliophile/client.go index 64acad0baf..0815ae9774 100644 --- a/precompile/contracts/bibliophile/client.go +++ b/precompile/contracts/bibliophile/client.go @@ -3,7 +3,6 @@ package bibliophile import ( "math/big" - // hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" "github.com/ava-labs/subnet-evm/precompile/contract" "github.com/ethereum/go-ethereum/common" ) From 303764059c099826163cf943c7a321b6347cfe13 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Wed, 27 Sep 2023 13:58:33 +0100 Subject: [PATCH 07/11] baseSlot for collateral idx*3 --- precompile/contracts/bibliophile/margin_account.go | 7 ++++--- precompile/contracts/bibliophile/oracle.go | 9 +++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/precompile/contracts/bibliophile/margin_account.go b/precompile/contracts/bibliophile/margin_account.go index 9ffe1ba158..9ae5aa4bd8 100644 --- a/precompile/contracts/bibliophile/margin_account.go +++ b/precompile/contracts/bibliophile/margin_account.go @@ -63,12 +63,13 @@ func GetCollaterals(stateDB contract.StateDB) []hu.Collateral { } func getCollateralCount(stateDB contract.StateDB) uint8 { - rawVal := stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BytesToHash(common.LeftPadBytes(big.NewInt(SUPPORTED_COLLATERAL_SLOT).Bytes(), 32))) + rawVal := stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BigToHash(big.NewInt(SUPPORTED_COLLATERAL_SLOT))) return uint8(new(big.Int).SetBytes(rawVal.Bytes()).Uint64()) } func getCollateralAt(stateDB contract.StateDB, idx uint8) hu.Collateral { - baseSlot := hu.Add(collateralStorageSlot(), big.NewInt(int64(idx))) + // struct Collateral { IERC20 token; uint weight; uint8 decimals; } + baseSlot := hu.Add(collateralStorageSlot(), big.NewInt(int64(idx)*3)) // collateral struct size = 3 * 32 bytes tokenAddress := common.BytesToAddress(stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BigToHash(baseSlot)).Bytes()) return hu.Collateral{ Weight: stateDB.GetState(common.HexToAddress(MARGIN_ACCOUNT_GENESIS_ADDRESS), common.BigToHash(hu.Add(baseSlot, big.NewInt(1)))).Big(), @@ -78,5 +79,5 @@ func getCollateralAt(stateDB contract.StateDB, idx uint8) hu.Collateral { } func collateralStorageSlot() *big.Int { - return new(big.Int).SetBytes(crypto.Keccak256(common.LeftPadBytes(big.NewInt(SUPPORTED_COLLATERAL_SLOT).Bytes(), 32))) + return new(big.Int).SetBytes(crypto.Keccak256(common.BigToHash(big.NewInt(SUPPORTED_COLLATERAL_SLOT)).Bytes())) } diff --git a/precompile/contracts/bibliophile/oracle.go b/precompile/contracts/bibliophile/oracle.go index 5c918ab80c..4fc606c3b9 100644 --- a/precompile/contracts/bibliophile/oracle.go +++ b/precompile/contracts/bibliophile/oracle.go @@ -32,7 +32,7 @@ func getUnderlyingPrice_(stateDB contract.StateDB, underlying common.Address) *b return redstonePrice } // red stone oracle is not enabled for this market, we use the default TestOracle - slot := crypto.Keccak256(append(common.LeftPadBytes(underlying.Bytes(), 32), common.LeftPadBytes(big.NewInt(TEST_ORACLE_PRICES_MAPPING_SLOT).Bytes(), 32)...)) + 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()) } @@ -51,9 +51,6 @@ func getlatestRoundId(stateDB contract.StateDB, adapterAddress common.Address) * } func getRedStoneFeedId(stateDB contract.StateDB, oracle, underlying common.Address) common.Hash { - return stateDB.GetState(oracle, common.BytesToHash(aggregatorMapSlot(underlying))) -} - -func aggregatorMapSlot(underlying common.Address) []byte { - return crypto.Keccak256(append(common.LeftPadBytes(underlying.Bytes(), 32), common.LeftPadBytes(big.NewInt(AGGREGATOR_MAP_SLOT).Bytes(), 32)...)) + aggregatorMapSlot := crypto.Keccak256(append(common.LeftPadBytes(underlying.Bytes(), 32), common.BigToHash(big.NewInt(AGGREGATOR_MAP_SLOT)).Bytes()...)) + return stateDB.GetState(oracle, common.BytesToHash(aggregatorMapSlot)) } From a4586a0293347784cc12d5ae3656212338692d5a Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Thu, 28 Sep 2023 17:02:54 +0100 Subject: [PATCH 08/11] userState/hubbleState in margin_math --- .../orderbook/hubbleutils/data_structures.go | 55 ++++++++ .../evm/orderbook/hubbleutils/margin_math.go | 123 ++++++------------ plugin/evm/orderbook/liquidations.go | 13 +- plugin/evm/orderbook/memory_database.go | 16 ++- .../contracts/bibliophile/clearing_house.go | 15 ++- 5 files changed, 138 insertions(+), 84 deletions(-) create mode 100644 plugin/evm/orderbook/hubbleutils/data_structures.go diff --git a/plugin/evm/orderbook/hubbleutils/data_structures.go b/plugin/evm/orderbook/hubbleutils/data_structures.go new file mode 100644 index 0000000000..e6b7834a98 --- /dev/null +++ b/plugin/evm/orderbook/hubbleutils/data_structures.go @@ -0,0 +1,55 @@ +package hubbleutils + +import ( + "encoding/json" + "math/big" +) + +type MarginMode = uint8 + +const ( + Maintenance_Margin MarginMode = iota + Min_Allowable_Margin +) + +type Collateral struct { + Price *big.Int // scaled by 1e6 + Weight *big.Int // scaled by 1e6 + Decimals uint8 +} + +type Market = int + +type Position struct { + OpenNotional *big.Int `json:"open_notional"` + Size *big.Int `json:"size"` + UnrealisedFunding *big.Int `json:"unrealised_funding"` + LastPremiumFraction *big.Int `json:"last_premium_fraction"` + LiquidationThreshold *big.Int `json:"liquidation_threshold"` +} + +func (p *Position) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + OpenNotional string `json:"open_notional"` + Size string `json:"size"` + UnrealisedFunding string `json:"unrealised_funding"` + LastPremiumFraction string `json:"last_premium_fraction"` + LiquidationThreshold string `json:"liquidation_threshold"` + }{ + OpenNotional: p.OpenNotional.String(), + Size: p.Size.String(), + UnrealisedFunding: p.UnrealisedFunding.String(), + LastPremiumFraction: p.LastPremiumFraction.String(), + LiquidationThreshold: p.LiquidationThreshold.String(), + }) +} + +type Trader struct { + Positions map[Market]*Position `json:"positions"` // position for every market + Margin Margin `json:"margin"` // available margin/balance for every market +} + +type Margin struct { + Reserved *big.Int `json:"reserved"` + Deposited map[Collateral]*big.Int `json:"deposited"` +} diff --git a/plugin/evm/orderbook/hubbleutils/margin_math.go b/plugin/evm/orderbook/hubbleutils/margin_math.go index 6660953dfb..c6a7b800de 100644 --- a/plugin/evm/orderbook/hubbleutils/margin_math.go +++ b/plugin/evm/orderbook/hubbleutils/margin_math.go @@ -1,74 +1,27 @@ package hubbleutils import ( - "encoding/json" "math/big" ) -type Collateral struct { - Price *big.Int // scaled by 1e6 - Weight *big.Int // scaled by 1e6 - Decimals uint8 +type HubbleState struct { + Assets []Collateral + OraclePrices map[Market]*big.Int + LastPrices map[Market]*big.Int + ActiveMarkets []Market + MinAllowableMargin *big.Int } -type Market = int - -type Position struct { - OpenNotional *big.Int `json:"open_notional"` - Size *big.Int `json:"size"` - UnrealisedFunding *big.Int `json:"unrealised_funding"` - LastPremiumFraction *big.Int `json:"last_premium_fraction"` - LiquidationThreshold *big.Int `json:"liquidation_threshold"` -} - -func (p *Position) MarshalJSON() ([]byte, error) { - return json.Marshal(&struct { - OpenNotional string `json:"open_notional"` - Size string `json:"size"` - UnrealisedFunding string `json:"unrealised_funding"` - LastPremiumFraction string `json:"last_premium_fraction"` - LiquidationThreshold string `json:"liquidation_threshold"` - }{ - OpenNotional: p.OpenNotional.String(), - Size: p.Size.String(), - UnrealisedFunding: p.UnrealisedFunding.String(), - LastPremiumFraction: p.LastPremiumFraction.String(), - LiquidationThreshold: p.LiquidationThreshold.String(), - }) -} - -type Trader struct { - Positions map[Market]*Position `json:"positions"` // position for every market - Margin Margin `json:"margin"` // available margin/balance for every market -} - -type Margin struct { - Reserved *big.Int `json:"reserved"` - Deposited map[Collateral]*big.Int `json:"deposited"` -} - -func GetNormalizedMargin(assets []Collateral, margins []*big.Int) *big.Int { - weighted, _ := WeightedAndSpotCollateral(assets, margins) - return weighted -} - -func WeightedAndSpotCollateral(assets []Collateral, margins []*big.Int) (weighted, spot *big.Int) { - weighted = big.NewInt(0) - spot = big.NewInt(0) - for i, asset := range assets { - if margins[i] == nil || margins[i].Sign() == 0 { - continue - } - numerator := Mul(margins[i], asset.Price) // margin[i] is scaled by asset.Decimal - spot.Add(spot, Unscale(numerator, asset.Decimals)) - weighted.Add(weighted, Unscale(Mul(numerator, asset.Weight), asset.Decimals+6)) - } - return weighted, spot +type UserState struct { + Positions map[Market]*Position + Margins []*big.Int + PendingFunding *big.Int + ReservedMargin *big.Int } -func GetAvailableMargin(positions map[Market]*Position, margins []*big.Int, pendingFunding *big.Int, reservedMargin *big.Int, assets []Collateral, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, minAllowableMargin *big.Int, markets []Market) *big.Int { - notionalPosition, margin := GetNotionalPositionAndMargin(positions, margins, Min_Allowable_Margin, pendingFunding, assets, oraclePrices, lastPrices, markets) - return GetAvailableMargin_(notionalPosition, margin, reservedMargin, minAllowableMargin) +func GetAvailableMargin(hState *HubbleState, userState *UserState) *big.Int { + notionalPosition, margin := GetNotionalPositionAndMargin(hState, userState, Min_Allowable_Margin) + return GetAvailableMargin_(notionalPosition, margin, userState.ReservedMargin, hState.MinAllowableMargin) } func GetAvailableMargin_(notionalPosition, margin, reservedMargin, minAllowableMargin *big.Int) *big.Int { @@ -76,53 +29,43 @@ func GetAvailableMargin_(notionalPosition, margin, reservedMargin, minAllowableM return Sub(Sub(margin, utilisedMargin), reservedMargin) } -type MarginMode = uint8 - -const ( - Maintenance_Margin MarginMode = iota - Min_Allowable_Margin -) - -func GetNotionalPositionAndMargin(positions map[Market]*Position, margins []*big.Int, marginMode MarginMode, pendingFunding *big.Int, assets []Collateral, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, markets []Market) (*big.Int, *big.Int) { - margin := Sub(GetNormalizedMargin(assets, margins), pendingFunding) - notionalPosition, unrealizedPnl := GetTotalNotionalPositionAndUnrealizedPnl(positions, margin, marginMode, oraclePrices, lastPrices, markets) +func GetNotionalPositionAndMargin(hState *HubbleState, userState *UserState, marginMode MarginMode) (*big.Int, *big.Int) { + margin := Sub(GetNormalizedMargin(hState.Assets, userState.Margins), userState.PendingFunding) + notionalPosition, unrealizedPnl := GetTotalNotionalPositionAndUnrealizedPnl(hState, userState, margin, marginMode) return notionalPosition, Add(margin, unrealizedPnl) } -func GetTotalNotionalPositionAndUnrealizedPnl(positions map[Market]*Position, margin *big.Int, marginMode MarginMode, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, markets []Market) (*big.Int, *big.Int) { +func GetTotalNotionalPositionAndUnrealizedPnl(hState *HubbleState, userState *UserState, margin *big.Int, marginMode MarginMode) (*big.Int, *big.Int) { notionalPosition := big.NewInt(0) unrealizedPnl := big.NewInt(0) - for _, market := range markets { - _notionalPosition, _unrealizedPnl := GetOptimalPnl(market, oraclePrices[market], lastPrices[market], positions, margin, marginMode) + for _, market := range hState.ActiveMarkets { + _notionalPosition, _unrealizedPnl := GetOptimalPnl(hState, userState.Positions[market], margin, market, marginMode) notionalPosition.Add(notionalPosition, _notionalPosition) unrealizedPnl.Add(unrealizedPnl, _unrealizedPnl) } return notionalPosition, unrealizedPnl } -func GetOptimalPnl(market Market, oraclePrice *big.Int, lastPrice *big.Int, positions map[Market]*Position, margin *big.Int, marginMode MarginMode) (notionalPosition *big.Int, uPnL *big.Int) { - position := positions[market] +func GetOptimalPnl(hState *HubbleState, position *Position, margin *big.Int, market Market, marginMode MarginMode) (notionalPosition *big.Int, uPnL *big.Int) { if position == nil || position.Size.Sign() == 0 { return big.NewInt(0), big.NewInt(0) } // based on last price notionalPosition, unrealizedPnl, lastPriceBasedMF := GetPositionMetadata( - lastPrice, + hState.LastPrices[market], position.OpenNotional, position.Size, margin, ) - // log.Info("in getOptimalPnl", "notionalPosition", notionalPosition, "unrealizedPnl", unrealizedPnl, "lastPriceBasedMF", lastPriceBasedMF) // based on oracle price oracleBasedNotional, oracleBasedUnrealizedPnl, oracleBasedMF := GetPositionMetadata( - oraclePrice, + hState.OraclePrices[market], position.OpenNotional, position.Size, margin, ) - // log.Info("in getOptimalPnl", "oracleBasedNotional", oracleBasedNotional, "oracleBasedUnrealizedPnl", oracleBasedUnrealizedPnl, "oracleBasedMF", oracleBasedMF) if (marginMode == Maintenance_Margin && oracleBasedMF.Cmp(lastPriceBasedMF) == 1) || // for liquidations (marginMode == Min_Allowable_Margin && oracleBasedMF.Cmp(lastPriceBasedMF) == -1) { // for increasing leverage @@ -132,7 +75,6 @@ func GetOptimalPnl(market Market, oraclePrice *big.Int, lastPrice *big.Int, posi } func GetPositionMetadata(price *big.Int, openNotional *big.Int, size *big.Int, margin *big.Int) (notionalPosition *big.Int, unrealisedPnl *big.Int, marginFraction *big.Int) { - // log.Info("in GetPositionMetadata", "price", price, "openNotional", openNotional, "size", size, "margin", margin) notionalPosition = GetNotionalPosition(price, size) uPnL := new(big.Int) if notionalPosition.Cmp(big.NewInt(0)) == 0 { @@ -150,3 +92,22 @@ func GetPositionMetadata(price *big.Int, openNotional *big.Int, size *big.Int, m func GetNotionalPosition(price *big.Int, size *big.Int) *big.Int { return big.NewInt(0).Abs(Div1e18(Mul(size, price))) } + +func GetNormalizedMargin(assets []Collateral, margins []*big.Int) *big.Int { + weighted, _ := WeightedAndSpotCollateral(assets, margins) + return weighted +} + +func WeightedAndSpotCollateral(assets []Collateral, margins []*big.Int) (weighted, spot *big.Int) { + weighted = big.NewInt(0) + spot = big.NewInt(0) + for i, asset := range assets { + if margins[i] == nil || margins[i].Sign() == 0 { + continue + } + numerator := Mul(margins[i], asset.Price) // margin[i] is scaled by asset.Decimal + spot.Add(spot, Unscale(numerator, asset.Decimals)) + weighted.Add(weighted, Unscale(Mul(numerator, asset.Weight), asset.Decimals+6)) + } + return weighted, spot +} diff --git a/plugin/evm/orderbook/liquidations.go b/plugin/evm/orderbook/liquidations.go index 0004a2815f..5bd66e1936 100644 --- a/plugin/evm/orderbook/liquidations.go +++ b/plugin/evm/orderbook/liquidations.go @@ -64,7 +64,18 @@ func getTotalFunding(trader *Trader, markets []Market) *big.Int { type MarginMode = hu.MarginMode func getTotalNotionalPositionAndUnrealizedPnl(trader *Trader, margin *big.Int, marginMode MarginMode, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, markets []Market) (*big.Int, *big.Int) { - return hu.GetTotalNotionalPositionAndUnrealizedPnl(trader.Positions, margin, marginMode, oraclePrices, lastPrices, markets) + return hu.GetTotalNotionalPositionAndUnrealizedPnl( + &hu.HubbleState{ + OraclePrices: oraclePrices, + LastPrices: lastPrices, + ActiveMarkets: markets, + }, + &hu.UserState{ + Positions: trader.Positions, + }, + margin, + marginMode, + ) } func getPositionMetadata(price *big.Int, openNotional *big.Int, size *big.Int, margin *big.Int) (notionalPosition *big.Int, unrealisedPnl *big.Int, marginFraction *big.Int) { diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index b467625796..25d2fac61d 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -1065,7 +1065,21 @@ func getBlankTrader() *Trader { } func getAvailableMargin(trader *Trader, pendingFunding *big.Int, assets []hu.Collateral, oraclePrices map[Market]*big.Int, lastPrices map[Market]*big.Int, minAllowableMargin *big.Int, markets []Market) *big.Int { - return hu.GetAvailableMargin(trader.Positions, getMargins(trader, len(assets)), pendingFunding, trader.Margin.Reserved, assets, oraclePrices, lastPrices, minAllowableMargin, markets) + return hu.GetAvailableMargin( + &hu.HubbleState{ + Assets: assets, + OraclePrices: oraclePrices, + LastPrices: lastPrices, + ActiveMarkets: markets, + MinAllowableMargin: minAllowableMargin, + }, + &hu.UserState{ + Positions: trader.Positions, + Margins: getMargins(trader, len(assets)), + PendingFunding: pendingFunding, + ReservedMargin: trader.Margin.Reserved, + }, + ) } // deepCopyOrder deep copies the LimitOrder struct diff --git a/precompile/contracts/bibliophile/clearing_house.go b/precompile/contracts/bibliophile/clearing_house.go index fe7c926e79..a393c30278 100644 --- a/precompile/contracts/bibliophile/clearing_house.go +++ b/precompile/contracts/bibliophile/clearing_house.go @@ -81,7 +81,20 @@ func getNotionalPositionAndMargin(stateDB contract.StateDB, input *GetNotionalPo if input.IncludeFundingPayments { pendingFunding = GetTotalFunding(stateDB, &input.Trader) } - notionalPosition, margin := hu.GetNotionalPositionAndMargin(positions, getMargins(stateDB, input.Trader), input.Mode, pendingFunding, GetCollaterals(stateDB), underlyingPrices, lastPrices, marketIds) + notionalPosition, margin := hu.GetNotionalPositionAndMargin( + &hu.HubbleState{ + Assets: GetCollaterals(stateDB), + OraclePrices: underlyingPrices, + LastPrices: lastPrices, + ActiveMarkets: marketIds, + }, + &hu.UserState{ + Positions: positions, + Margins: getMargins(stateDB, input.Trader), + PendingFunding: pendingFunding, + }, + input.Mode, + ) return GetNotionalPositionAndMarginOutput{ NotionalPosition: notionalPosition, Margin: margin, From 600f74b5576dc7b7cc20a40094158fda254b76d0 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Thu, 28 Sep 2023 17:13:28 +0100 Subject: [PATCH 09/11] hu.Position --- .../contract_events_processor_test.go | 15 +++------ .../orderbook/hubbleutils/data_structures.go | 28 ++++------------- plugin/evm/orderbook/liquidations.go | 10 +++++- plugin/evm/orderbook/liquidations_test.go | 15 +++------ plugin/evm/orderbook/memory_database.go | 31 ++++++++++++++++--- 5 files changed, 52 insertions(+), 47 deletions(-) diff --git a/plugin/evm/orderbook/contract_events_processor_test.go b/plugin/evm/orderbook/contract_events_processor_test.go index ec8983d679..7b751bfcb4 100644 --- a/plugin/evm/orderbook/contract_events_processor_test.go +++ b/plugin/evm/orderbook/contract_events_processor_test.go @@ -111,8 +111,7 @@ func TestOrderBookMarginAccountClearingHouseEventInLog(t *testing.T) { unrealisedFunding := hu.Mul1e6(big.NewInt(1)) market := Market(0) position := &Position{ - OpenNotional: openNotional, - Size: size, + Position: hu.Position{OpenNotional: openNotional, Size: size}, UnrealisedFunding: unrealisedFunding, LastPremiumFraction: lastPremiumFraction, LiquidationThreshold: liquidationThreshold, @@ -455,8 +454,7 @@ func TestHandleClearingHouseEvent(t *testing.T) { db := getDatabase() cep := newcep(t, db) position := &Position{ - OpenNotional: openNotional, - Size: size, + Position: hu.Position{OpenNotional: openNotional, Size: size}, UnrealisedFunding: unrealisedFunding, LastPremiumFraction: lastPremiumFraction, LiquidationThreshold: liquidationThreshold, @@ -493,8 +491,7 @@ func TestHandleClearingHouseEvent(t *testing.T) { db := getDatabase() cep := newcep(t, db) position := &Position{ - OpenNotional: openNotional, - Size: size, + Position: hu.Position{OpenNotional: openNotional, Size: size}, UnrealisedFunding: unrealisedFunding, LastPremiumFraction: lastPremiumFraction, LiquidationThreshold: liquidationThreshold, @@ -529,8 +526,7 @@ func TestHandleClearingHouseEvent(t *testing.T) { db := getDatabase() cep := newcep(t, db) position := &Position{ - OpenNotional: openNotional, - Size: size, + Position: hu.Position{OpenNotional: openNotional, Size: size}, UnrealisedFunding: unrealisedFunding, LastPremiumFraction: lastPremiumFraction, LiquidationThreshold: liquidationThreshold, @@ -577,8 +573,7 @@ func TestHandleClearingHouseEvent(t *testing.T) { db := getDatabase() cep := newcep(t, db) position := &Position{ - OpenNotional: openNotional, - Size: size, + Position: hu.Position{OpenNotional: openNotional, Size: size}, UnrealisedFunding: unrealisedFunding, LastPremiumFraction: lastPremiumFraction, LiquidationThreshold: liquidationThreshold, diff --git a/plugin/evm/orderbook/hubbleutils/data_structures.go b/plugin/evm/orderbook/hubbleutils/data_structures.go index e6b7834a98..fbae81a41e 100644 --- a/plugin/evm/orderbook/hubbleutils/data_structures.go +++ b/plugin/evm/orderbook/hubbleutils/data_structures.go @@ -1,7 +1,7 @@ package hubbleutils import ( - "encoding/json" + // "encoding/json" "math/big" ) @@ -21,27 +21,11 @@ type Collateral struct { type Market = int type Position struct { - OpenNotional *big.Int `json:"open_notional"` - Size *big.Int `json:"size"` - UnrealisedFunding *big.Int `json:"unrealised_funding"` - LastPremiumFraction *big.Int `json:"last_premium_fraction"` - LiquidationThreshold *big.Int `json:"liquidation_threshold"` -} - -func (p *Position) MarshalJSON() ([]byte, error) { - return json.Marshal(&struct { - OpenNotional string `json:"open_notional"` - Size string `json:"size"` - UnrealisedFunding string `json:"unrealised_funding"` - LastPremiumFraction string `json:"last_premium_fraction"` - LiquidationThreshold string `json:"liquidation_threshold"` - }{ - OpenNotional: p.OpenNotional.String(), - Size: p.Size.String(), - UnrealisedFunding: p.UnrealisedFunding.String(), - LastPremiumFraction: p.LastPremiumFraction.String(), - LiquidationThreshold: p.LiquidationThreshold.String(), - }) + OpenNotional *big.Int `json:"open_notional"` + Size *big.Int `json:"size"` + // UnrealisedFunding *big.Int `json:"unrealised_funding"` + // LastPremiumFraction *big.Int `json:"last_premium_fraction"` + // LiquidationThreshold *big.Int `json:"liquidation_threshold"` } type Trader struct { diff --git a/plugin/evm/orderbook/liquidations.go b/plugin/evm/orderbook/liquidations.go index 5bd66e1936..0dd97b903f 100644 --- a/plugin/evm/orderbook/liquidations.go +++ b/plugin/evm/orderbook/liquidations.go @@ -71,7 +71,7 @@ func getTotalNotionalPositionAndUnrealizedPnl(trader *Trader, margin *big.Int, m ActiveMarkets: markets, }, &hu.UserState{ - Positions: trader.Positions, + Positions: translatePositions(trader.Positions), }, margin, marginMode, @@ -85,3 +85,11 @@ func getPositionMetadata(price *big.Int, openNotional *big.Int, size *big.Int, m func prettifyScaledBigInt(number *big.Int, precision int8) string { return new(big.Float).Quo(new(big.Float).SetInt(number), big.NewFloat(math.Pow10(int(precision)))).String() } + +func translatePositions(positions map[int]*Position) map[int]*hu.Position { + huPositions := make(map[int]*hu.Position) + for key, value := range positions { + huPositions[key] = &value.Position + } + return huPositions +} diff --git a/plugin/evm/orderbook/liquidations_test.go b/plugin/evm/orderbook/liquidations_test.go index 7f12d5399d..337f3d5efe 100644 --- a/plugin/evm/orderbook/liquidations_test.go +++ b/plugin/evm/orderbook/liquidations_test.go @@ -289,8 +289,7 @@ func TestGetPositionMetadata(t *testing.T) { entryPrice := hu.Mul1e6(big.NewInt(10)) newPrice := hu.Mul1e6(big.NewInt(15)) position := &Position{ - Size: size, - OpenNotional: hu.GetNotionalPosition(entryPrice, size), + Position: hu.Position{OpenNotional: hu.GetNotionalPosition(entryPrice, size), Size: size}, } arbitaryMarginValue := hu.Mul1e6(big.NewInt(69)) @@ -305,8 +304,7 @@ func TestGetPositionMetadata(t *testing.T) { entryPrice := hu.Mul1e6(big.NewInt(10)) newPrice := hu.Mul1e6(big.NewInt(15)) position := &Position{ - Size: size, - OpenNotional: hu.GetNotionalPosition(entryPrice, size), + Position: hu.Position{OpenNotional: hu.GetNotionalPosition(entryPrice, size), Size: size}, } notionalPosition, uPnL, _ := getPositionMetadata(newPrice, position.OpenNotional, position.Size, big.NewInt(0)) @@ -321,8 +319,7 @@ func TestGetPositionMetadata(t *testing.T) { entryPrice := hu.Mul1e6(big.NewInt(10)) newPrice := hu.Mul1e6(big.NewInt(5)) position := &Position{ - Size: size, - OpenNotional: hu.GetNotionalPosition(entryPrice, size), + Position: hu.Position{OpenNotional: hu.GetNotionalPosition(entryPrice, size), Size: size}, } notionalPosition, uPnL, _ := getPositionMetadata(newPrice, position.OpenNotional, position.Size, big.NewInt(0)) @@ -335,8 +332,7 @@ func TestGetPositionMetadata(t *testing.T) { entryPrice := hu.Mul1e6(big.NewInt(10)) newPrice := hu.Mul1e6(big.NewInt(5)) position := &Position{ - Size: size, - OpenNotional: hu.GetNotionalPosition(entryPrice, size), + Position: hu.Position{OpenNotional: hu.GetNotionalPosition(entryPrice, size), Size: size}, } notionalPosition, uPnL, _ := getPositionMetadata(newPrice, position.OpenNotional, position.Size, big.NewInt(0)) assert.Equal(t, hu.GetNotionalPosition(newPrice, size), notionalPosition) @@ -351,8 +347,7 @@ func getPosition(market Market, openNotional *big.Int, size *big.Int, unrealized liquidationThreshold = getLiquidationThreshold(maxLiquidationRatio, minSizeRequirement, size) } return &Position{ - OpenNotional: openNotional, - Size: size, + Position: hu.Position{OpenNotional: openNotional, Size: size}, UnrealisedFunding: unrealizedFunding, LastPremiumFraction: lastPremiumFraction, LiquidationThreshold: liquidationThreshold, diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index 25d2fac61d..fab59b370e 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -179,7 +179,28 @@ func (order Order) ToOrderMin() OrderMin { } } -type Position = hu.Position +type Position struct { + hu.Position + UnrealisedFunding *big.Int `json:"unrealised_funding"` + LastPremiumFraction *big.Int `json:"last_premium_fraction"` + LiquidationThreshold *big.Int `json:"liquidation_threshold"` +} + +func (p *Position) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + OpenNotional string `json:"open_notional"` + Size string `json:"size"` + UnrealisedFunding string `json:"unrealised_funding"` + LastPremiumFraction string `json:"last_premium_fraction"` + LiquidationThreshold string `json:"liquidation_threshold"` + }{ + OpenNotional: p.OpenNotional.String(), + Size: p.Size.String(), + UnrealisedFunding: p.UnrealisedFunding.String(), + LastPremiumFraction: p.LastPremiumFraction.String(), + LiquidationThreshold: p.LiquidationThreshold.String(), + }) +} type Margin struct { Reserved *big.Int `json:"reserved"` @@ -1074,7 +1095,7 @@ func getAvailableMargin(trader *Trader, pendingFunding *big.Int, assets []hu.Col MinAllowableMargin: minAllowableMargin, }, &hu.UserState{ - Positions: trader.Positions, + Positions: translatePositions(trader.Positions), Margins: getMargins(trader, len(assets)), PendingFunding: pendingFunding, ReservedMargin: trader.Margin.Reserved, @@ -1105,8 +1126,10 @@ func deepCopyTrader(order *Trader) *Trader { positions := map[Market]*Position{} for market, position := range order.Positions { positions[market] = &Position{ - OpenNotional: big.NewInt(0).Set(position.OpenNotional), - Size: big.NewInt(0).Set(position.Size), + Position: hu.Position{ + OpenNotional: big.NewInt(0).Set(position.OpenNotional), + Size: big.NewInt(0).Set(position.Size), + }, UnrealisedFunding: big.NewInt(0).Set(position.UnrealisedFunding), LastPremiumFraction: big.NewInt(0).Set(position.LastPremiumFraction), LiquidationThreshold: big.NewInt(0).Set(position.LiquidationThreshold), From c96488442bc46b2313d39ebe368e1c5ccab6d388 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Thu, 28 Sep 2023 12:46:32 +0100 Subject: [PATCH 10/11] ticks.getQuote --- precompile/contracts/ticks/logic.go | 67 +++++++-- precompile/contracts/ticks/logic_test.go | 167 +++++++++++++++++++++-- 2 files changed, 215 insertions(+), 19 deletions(-) diff --git a/precompile/contracts/ticks/logic.go b/precompile/contracts/ticks/logic.go index 3c51b1d36b..86a3fef8d8 100644 --- a/precompile/contracts/ticks/logic.go +++ b/precompile/contracts/ticks/logic.go @@ -56,8 +56,8 @@ func _sampleImpactBid(bibliophile b.BibliophileClient, ammAddress common.Address return big.NewInt(0) } impactMarginNotional := new(big.Int).Mul(_impactMarginNotional, big.NewInt(1e12)) - accNotional := big.NewInt(0) - accBaseQ := big.NewInt(0) + accNotional := big.NewInt(0) // 18 decimals + accBaseQ := big.NewInt(0) // 18 decimals tick := bibliophile.GetBidsHead(ammAddress) for tick.Sign() != 0 { amount := bibliophile.GetBidSize(ammAddress, tick) @@ -90,8 +90,8 @@ func _sampleImpactAsk(bibliophile b.BibliophileClient, ammAddress common.Address } impactMarginNotional := new(big.Int).Mul(_impactMarginNotional, big.NewInt(1e12)) tick := bibliophile.GetAsksHead(ammAddress) - accNotional := big.NewInt(0) - accBaseQ := big.NewInt(0) + accNotional := big.NewInt(0) // 18 decimals + accBaseQ := big.NewInt(0) // 18 decimals for tick.Sign() != 0 { amount := bibliophile.GetAskSize(ammAddress, tick) accumulator := new(big.Int).Add(accNotional, hu.Div1e6(big.NewInt(0).Mul(amount, tick))) @@ -109,10 +109,6 @@ func _sampleImpactAsk(bibliophile b.BibliophileClient, ammAddress common.Address return new(big.Int).Div(hu.Mul1e6(impactMarginNotional), new(big.Int).Add(baseQAtTick, accBaseQ)) // return value is in 6 decimals } -func GetQuote(bibliophile b.BibliophileClient, ammAddress common.Address, baseAssetQuantity *big.Int) *big.Int { - return big.NewInt(0) -} - func GetBaseQuote(bibliophile b.BibliophileClient, ammAddress common.Address, quoteAssetQuantity *big.Int) *big.Int { if quoteAssetQuantity.Sign() > 0 { // get the qoute to long quoteQuantity dollars return _sampleImpactAsk(bibliophile, ammAddress, quoteAssetQuantity) @@ -120,3 +116,58 @@ func GetBaseQuote(bibliophile b.BibliophileClient, ammAddress common.Address, qu // get the qoute to short quoteQuantity dollars return _sampleImpactBid(bibliophile, ammAddress, new(big.Int).Neg(quoteAssetQuantity)) } + +func GetQuote(bibliophile b.BibliophileClient, ammAddress common.Address, baseAssetQuantity *big.Int) *big.Int { + if baseAssetQuantity.Sign() > 0 { + return _sampleAsk(bibliophile, ammAddress, baseAssetQuantity) + } + return _sampleBid(bibliophile, ammAddress, new(big.Int).Neg(baseAssetQuantity)) +} + +func _sampleAsk(bibliophile b.BibliophileClient, ammAddress common.Address, baseAssetQuantity *big.Int) *big.Int { + if baseAssetQuantity.Sign() <= 0 { + return big.NewInt(0) + } + tick := bibliophile.GetAsksHead(ammAddress) + accNotional := big.NewInt(0) // 18 decimals + accBaseQ := big.NewInt(0) // 18 decimals + for tick.Sign() != 0 { + amount := bibliophile.GetAskSize(ammAddress, tick) + accumulator := hu.Add(accBaseQ, amount) + if accumulator.Cmp(baseAssetQuantity) >= 0 { + break + } + accNotional.Add(accNotional, hu.Div1e6(hu.Mul(amount, tick))) + accBaseQ = accumulator + tick = bibliophile.GetNextAskPrice(ammAddress, tick) + } + if tick.Sign() == 0 { + return big.NewInt(0) // insufficient liquidity + } + notionalAtTick := hu.Div1e6(hu.Mul(hu.Sub(baseAssetQuantity, accBaseQ), tick)) + return hu.Div(hu.Mul1e6(hu.Add(accNotional, notionalAtTick)), baseAssetQuantity) // return value is in 6 decimals +} + +func _sampleBid(bibliophile b.BibliophileClient, ammAddress common.Address, baseAssetQuantity *big.Int) *big.Int { + if baseAssetQuantity.Sign() <= 0 { + return big.NewInt(0) + } + tick := bibliophile.GetBidsHead(ammAddress) + accNotional := big.NewInt(0) // 18 decimals + accBaseQ := big.NewInt(0) // 18 decimals + for tick.Sign() != 0 { + amount := bibliophile.GetBidSize(ammAddress, tick) + accumulator := hu.Add(accBaseQ, amount) + if accumulator.Cmp(baseAssetQuantity) >= 0 { + break + } + accNotional.Add(accNotional, hu.Div1e6(hu.Mul(amount, tick))) + accBaseQ = accumulator + tick = bibliophile.GetNextBidPrice(ammAddress, tick) + } + if tick.Sign() == 0 { + return big.NewInt(0) // insufficient liquidity + } + notionalAtTick := hu.Div1e6(hu.Mul(hu.Sub(baseAssetQuantity, accBaseQ), tick)) + return hu.Div(hu.Mul1e6(hu.Add(accNotional, notionalAtTick)), baseAssetQuantity) // return value is in 6 decimals +} diff --git a/precompile/contracts/ticks/logic_test.go b/precompile/contracts/ticks/logic_test.go index 6be3ec5345..bc6d3882eb 100644 --- a/precompile/contracts/ticks/logic_test.go +++ b/precompile/contracts/ticks/logic_test.go @@ -287,7 +287,7 @@ func TestSampleImpactBid(t *testing.T) { output := SampleImpactBid(mockBibliophile, ammAddress) assert.Equal(t, big.NewInt(0), output) }) - t.Run("when there are multiple bids, it tries to fill with available bids and average price is returned for rest", func(t *testing.T) { + t.Run("when there are multiple bids", func(t *testing.T) { bids := []*big.Int{bidsHead, big.NewInt(2100000), big.NewInt(2200000), big.NewInt(2300000)} size := big.NewInt(1e18) // 1 ether mockBibliophile.EXPECT().GetImpactMarginNotional(ammAddress).Return(impactMarginNotional).Times(1) @@ -316,22 +316,22 @@ func TestSampleImpactBid(t *testing.T) { }) t.Run("when bids in orderbook are enough to cover impactMarginNotional", func(t *testing.T) { t.Run("when there is only one bid in orderbook it returns bidsHead", func(t *testing.T) { - newBidsHead := impactMarginNotional + bidsHead := impactMarginNotional mockBibliophile.EXPECT().GetImpactMarginNotional(ammAddress).Return(impactMarginNotional).Times(1) - mockBibliophile.EXPECT().GetBidsHead(ammAddress).Return(newBidsHead).Times(1) - mockBibliophile.EXPECT().GetBidSize(ammAddress, newBidsHead).Return(big.NewInt(1e18)).Times(1) + mockBibliophile.EXPECT().GetBidsHead(ammAddress).Return(bidsHead).Times(1) + mockBibliophile.EXPECT().GetBidSize(ammAddress, bidsHead).Return(big.NewInt(1e18)).Times(1) output := SampleImpactBid(mockBibliophile, ammAddress) - assert.Equal(t, newBidsHead, output) + assert.Equal(t, bidsHead, output) }) t.Run("when there are multiple bids, it tries to fill with available bids and average price is returned for rest", func(t *testing.T) { - newBidsHead := big.NewInt(2000000000) // 2000 units - bids := []*big.Int{newBidsHead} + bidsHead := big.NewInt(2000000000) // 2000 units + bids := []*big.Int{bidsHead} for i := int64(1); i < 6; i++ { - bids = append(bids, big.NewInt(0).Sub(newBidsHead, big.NewInt(i))) + bids = append(bids, big.NewInt(0).Sub(bidsHead, big.NewInt(i))) } size := big.NewInt(6e17) // 0.6 ether mockBibliophile.EXPECT().GetImpactMarginNotional(ammAddress).Return(impactMarginNotional).Times(1) - mockBibliophile.EXPECT().GetBidsHead(ammAddress).Return(newBidsHead).Times(1) + mockBibliophile.EXPECT().GetBidsHead(ammAddress).Return(bidsHead).Times(1) mockBibliophile.EXPECT().GetNextBidPrice(ammAddress, bids[0]).Return(bids[1]).Times(1) mockBibliophile.EXPECT().GetNextBidPrice(ammAddress, bids[1]).Return(bids[2]).Times(1) mockBibliophile.EXPECT().GetNextBidPrice(ammAddress, bids[2]).Return(bids[3]).Times(1) @@ -348,7 +348,6 @@ func TestSampleImpactBid(t *testing.T) { filledQuote.Add(filledQuote, (hu.Div(hu.Mul(bids[i], size), big.NewInt(1e18)))) } unfulFilledQuote := big.NewInt(0).Sub(impactMarginNotional, filledQuote) - fmt.Println("unfulFilledQuote", unfulFilledQuote, "totalBaseQ", totalBaseQ, "filledQuote", filledQuote) // as quantity is in 1e18 baseQ = price * 1e18 / price baseQAtTick := big.NewInt(0).Div(big.NewInt(0).Mul(unfulFilledQuote, big.NewInt(1e18)), bids[3]) expectedOutput := big.NewInt(0).Div(big.NewInt(0).Mul(impactMarginNotional, big.NewInt(1e18)), big.NewInt(0).Add(totalBaseQ, baseQAtTick)) @@ -388,7 +387,7 @@ func TestSampleImpactAsk(t *testing.T) { output := SampleImpactAsk(mockBibliophile, ammAddress) assert.Equal(t, big.NewInt(0), output) }) - t.Run("when there are multiple asks, it tries to fill with available asks and average price is returned for rest", func(t *testing.T) { + t.Run("when there are multiple asks", func(t *testing.T) { asks := []*big.Int{asksHead, big.NewInt(2100000), big.NewInt(2200000), big.NewInt(2300000)} size := big.NewInt(1e18) // 1 ether mockBibliophile.EXPECT().GetImpactMarginNotional(ammAddress).Return(impactMarginNotional).Times(1) @@ -454,3 +453,149 @@ func TestSampleImpactAsk(t *testing.T) { }) }) } + +func TestSampleBid(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockBibliophile := b.NewMockBibliophileClient(ctrl) + ammAddress := common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8") + bidsHead := big.NewInt(20 * 1e6) // $20 + baseAssetQuantity := big.NewInt(1e18) // 1 ether + t.Run("when bidsHead is 0", func(t *testing.T) { + mockBibliophile.EXPECT().GetBidsHead(ammAddress).Return(big.NewInt(0)).Times(1) + output := _sampleBid(mockBibliophile, ammAddress, baseAssetQuantity) + assert.Equal(t, big.NewInt(0), output) + }) + t.Run("when bidsHead > 0", func(t *testing.T) { + t.Run("when bids in orderbook are not enough to cover baseAssetQuantity", func(t *testing.T) { + t.Run("when there is only one bid in orderbook it returns 0", func(t *testing.T) { + mockBibliophile.EXPECT().GetBidsHead(ammAddress).Return(bidsHead).Times(1) + mockBibliophile.EXPECT().GetBidSize(ammAddress, bidsHead).Return(hu.Sub(baseAssetQuantity, big.NewInt(1))).Times(1) + mockBibliophile.EXPECT().GetNextBidPrice(ammAddress, bidsHead).Return(big.NewInt(0)).Times(1) + output := _sampleBid(mockBibliophile, ammAddress, baseAssetQuantity) + assert.Equal(t, big.NewInt(0), output) + }) + t.Run("when there are multiple bids", func(t *testing.T) { + bids := []*big.Int{bidsHead, big.NewInt(2100000), big.NewInt(2200000), big.NewInt(2300000)} + size := big.NewInt(24 * 1e16) // 0.24 ether + mockBibliophile.EXPECT().GetBidsHead(ammAddress).Return(bidsHead).Times(1) + for i := 0; i < len(bids); i++ { + mockBibliophile.EXPECT().GetBidSize(ammAddress, bids[i]).Return(size).Times(1) + if i != len(bids)-1 { + mockBibliophile.EXPECT().GetNextBidPrice(ammAddress, bids[i]).Return(bids[i+1]).Times(1) + } else { + mockBibliophile.EXPECT().GetNextBidPrice(ammAddress, bids[i]).Return(big.NewInt(0)).Times(1) + } + } + output := _sampleBid(mockBibliophile, ammAddress, baseAssetQuantity) + assert.Equal(t, big.NewInt(0), output) + }) + }) + t.Run("when bids in orderbook are enough to cover baseAssetQuantity", func(t *testing.T) { + t.Run("when there is only one bid in orderbook it returns bidsHead", func(t *testing.T) { + mockBibliophile.EXPECT().GetBidsHead(ammAddress).Return(bidsHead).Times(1) + mockBibliophile.EXPECT().GetBidSize(ammAddress, bidsHead).Return(baseAssetQuantity).Times(1) + output := _sampleBid(mockBibliophile, ammAddress, baseAssetQuantity) + assert.Equal(t, bidsHead, output) + }) + t.Run("when there are multiple bids, it tries to fill with available bids and average price is returned for rest", func(t *testing.T) { + bids := []*big.Int{bidsHead} + for i := int64(1); i < 6; i++ { + bids = append(bids, hu.Sub(bidsHead, big.NewInt(i))) + } + size := big.NewInt(3e17) // 0.3 ether + mockBibliophile.EXPECT().GetBidsHead(ammAddress).Return(bidsHead).Times(1) + mockBibliophile.EXPECT().GetNextBidPrice(ammAddress, bids[0]).Return(bids[1]).Times(1) + mockBibliophile.EXPECT().GetNextBidPrice(ammAddress, bids[1]).Return(bids[2]).Times(1) + mockBibliophile.EXPECT().GetNextBidPrice(ammAddress, bids[2]).Return(bids[3]).Times(1) + mockBibliophile.EXPECT().GetBidSize(ammAddress, bids[0]).Return(size).Times(1) + mockBibliophile.EXPECT().GetBidSize(ammAddress, bids[1]).Return(size).Times(1) + mockBibliophile.EXPECT().GetBidSize(ammAddress, bids[2]).Return(size).Times(1) + mockBibliophile.EXPECT().GetBidSize(ammAddress, bids[3]).Return(size).Times(1) + + output := _sampleBid(mockBibliophile, ammAddress, baseAssetQuantity) + accBaseQ := hu.Mul(size, big.NewInt(3)) + accNotional := big.NewInt(0) + for i := 0; i < 3; i++ { + accNotional.Add(accNotional, (hu.Div1e6(hu.Mul(bids[i], size)))) + } + notionalAtTick := hu.Div1e6(hu.Mul(hu.Sub(baseAssetQuantity, accBaseQ), bids[3])) + expectedOutput := hu.Div(hu.Mul1e6(hu.Add(accNotional, notionalAtTick)), baseAssetQuantity) + assert.Equal(t, expectedOutput, output) + }) + }) + }) +} + +func TestSampleAsk(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockBibliophile := b.NewMockBibliophileClient(ctrl) + ammAddress := common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8") + asksHead := big.NewInt(20 * 1e6) // $20 + baseAssetQuantity := big.NewInt(1e18) // 1 ether + t.Run("when asksHead is 0", func(t *testing.T) { + mockBibliophile.EXPECT().GetAsksHead(ammAddress).Return(big.NewInt(0)).Times(1) + output := _sampleAsk(mockBibliophile, ammAddress, baseAssetQuantity) + assert.Equal(t, big.NewInt(0), output) + }) + t.Run("when asksHead > 0", func(t *testing.T) { + t.Run("when asks in orderbook are not enough to cover baseAssetQuantity", func(t *testing.T) { + t.Run("when there is only one ask in orderbook it returns 0", func(t *testing.T) { + mockBibliophile.EXPECT().GetAsksHead(ammAddress).Return(asksHead).Times(1) + mockBibliophile.EXPECT().GetAskSize(ammAddress, asksHead).Return(hu.Sub(baseAssetQuantity, big.NewInt(1))).Times(1) + mockBibliophile.EXPECT().GetNextAskPrice(ammAddress, asksHead).Return(big.NewInt(0)).Times(1) + output := _sampleAsk(mockBibliophile, ammAddress, baseAssetQuantity) + assert.Equal(t, big.NewInt(0), output) + }) + t.Run("when there are multiple asks, it tries to fill with available asks", func(t *testing.T) { + asks := []*big.Int{asksHead, big.NewInt(2100000), big.NewInt(2200000), big.NewInt(2300000)} + size := big.NewInt(24 * 1e16) // 0.24 ether + mockBibliophile.EXPECT().GetAsksHead(ammAddress).Return(asksHead).Times(1) + for i := 0; i < len(asks); i++ { + mockBibliophile.EXPECT().GetAskSize(ammAddress, asks[i]).Return(size).Times(1) + if i != len(asks)-1 { + mockBibliophile.EXPECT().GetNextAskPrice(ammAddress, asks[i]).Return(asks[i+1]).Times(1) + } else { + mockBibliophile.EXPECT().GetNextAskPrice(ammAddress, asks[i]).Return(big.NewInt(0)).Times(1) + } + } + output := _sampleAsk(mockBibliophile, ammAddress, baseAssetQuantity) + assert.Equal(t, big.NewInt(0), output) + }) + }) + t.Run("when asks in orderbook are enough to cover baseAssetQuantity", func(t *testing.T) { + t.Run("when there is only one ask in orderbook it returns asksHead", func(t *testing.T) { + mockBibliophile.EXPECT().GetAsksHead(ammAddress).Return(asksHead).Times(1) + mockBibliophile.EXPECT().GetAskSize(ammAddress, asksHead).Return(baseAssetQuantity).Times(1) + output := _sampleAsk(mockBibliophile, ammAddress, baseAssetQuantity) + assert.Equal(t, asksHead, output) + }) + t.Run("when there are multiple asks, it tries to fill with available asks and average price is returned for rest", func(t *testing.T) { + asks := []*big.Int{asksHead} + for i := int64(1); i < 6; i++ { + asks = append(asks, hu.Sub(asksHead, big.NewInt(i))) + } + size := big.NewInt(31e16) // 0.31 ether + mockBibliophile.EXPECT().GetAsksHead(ammAddress).Return(asksHead).Times(1) + mockBibliophile.EXPECT().GetNextAskPrice(ammAddress, asks[0]).Return(asks[1]).Times(1) + mockBibliophile.EXPECT().GetNextAskPrice(ammAddress, asks[1]).Return(asks[2]).Times(1) + mockBibliophile.EXPECT().GetNextAskPrice(ammAddress, asks[2]).Return(asks[3]).Times(1) + mockBibliophile.EXPECT().GetAskSize(ammAddress, asks[0]).Return(size).Times(1) + mockBibliophile.EXPECT().GetAskSize(ammAddress, asks[1]).Return(size).Times(1) + mockBibliophile.EXPECT().GetAskSize(ammAddress, asks[2]).Return(size).Times(1) + mockBibliophile.EXPECT().GetAskSize(ammAddress, asks[3]).Return(size).Times(1) + + output := _sampleAsk(mockBibliophile, ammAddress, baseAssetQuantity) + accBaseQ := hu.Mul(size, big.NewInt(3)) + accNotional := big.NewInt(0) + for i := 0; i < 3; i++ { + accNotional.Add(accNotional, (hu.Div1e6(hu.Mul(asks[i], size)))) + } + notionalAtTick := hu.Div1e6(hu.Mul(hu.Sub(baseAssetQuantity, accBaseQ), asks[3])) + expectedOutput := hu.Div(hu.Mul1e6(hu.Add(accNotional, notionalAtTick)), baseAssetQuantity) + assert.Equal(t, expectedOutput, output) + }) + }) + }) +} From 74b1141ee1fe09bab7da5dddc1521c5d567f3dd7 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Fri, 29 Sep 2023 11:58:07 +0100 Subject: [PATCH 11/11] misc changes --- .../evm/orderbook/hubbleutils/margin_math.go | 8 +- precompile/contracts/bibliophile/amm.go | 23 +- precompile/contracts/bibliophile/api.go | 2 +- precompile/contracts/bibliophile/oracle.go | 7 +- tests/orderbook/abi/AMM.json | 2225 ++++++++--------- tests/orderbook/abi/MarginAccount.json | 2208 ++++++++-------- tests/orderbook/abi/Oracle.json | 284 +-- .../bibliophile/variablesReadFromSlotTests.js | 37 +- tests/orderbook/juror/JurorTests.js | 12 +- tests/orderbook/tests/test.js | 4 +- tests/orderbook/utils.js | 2 +- 11 files changed, 2404 insertions(+), 2408 deletions(-) diff --git a/plugin/evm/orderbook/hubbleutils/margin_math.go b/plugin/evm/orderbook/hubbleutils/margin_math.go index c6a7b800de..3bd8bdec53 100644 --- a/plugin/evm/orderbook/hubbleutils/margin_math.go +++ b/plugin/evm/orderbook/hubbleutils/margin_math.go @@ -77,15 +77,15 @@ func GetOptimalPnl(hState *HubbleState, position *Position, margin *big.Int, mar func GetPositionMetadata(price *big.Int, openNotional *big.Int, size *big.Int, margin *big.Int) (notionalPosition *big.Int, unrealisedPnl *big.Int, marginFraction *big.Int) { notionalPosition = GetNotionalPosition(price, size) uPnL := new(big.Int) - if notionalPosition.Cmp(big.NewInt(0)) == 0 { + if notionalPosition.Sign() == 0 { return big.NewInt(0), big.NewInt(0), big.NewInt(0) } if size.Cmp(big.NewInt(0)) > 0 { - uPnL = new(big.Int).Sub(notionalPosition, openNotional) + uPnL = Sub(notionalPosition, openNotional) } else { - uPnL = new(big.Int).Sub(openNotional, notionalPosition) + uPnL = Sub(openNotional, notionalPosition) } - mf := new(big.Int).Div(Mul1e6(new(big.Int).Add(margin, uPnL)), notionalPosition) + mf := Div(Mul1e6(Add(margin, uPnL)), notionalPosition) return notionalPosition, uPnL, mf } diff --git a/precompile/contracts/bibliophile/amm.go b/precompile/contracts/bibliophile/amm.go index d922e72624..86cebaa6f6 100644 --- a/precompile/contracts/bibliophile/amm.go +++ b/precompile/contracts/bibliophile/amm.go @@ -15,20 +15,15 @@ const ( MAX_ORACLE_SPREAD_RATIO_SLOT int64 = 3 MAX_LIQUIDATION_RATIO_SLOT int64 = 4 MIN_SIZE_REQUIREMENT_SLOT int64 = 5 - UNDERLYING_ASSET_SLOT int64 = 7 - MAX_LIQUIDATION_PRICE_SPREAD int64 = 12 - MULTIPLIER_SLOT int64 = 13 - IMPACT_MARGIN_NOTIONAL_SLOT int64 = 20 - LAST_TRADE_PRICE_SLOT int64 = 21 - BIDS_SLOT int64 = 22 - ASKS_SLOT int64 = 23 - BIDS_HEAD_SLOT int64 = 24 - ASKS_HEAD_SLOT int64 = 25 -) - -const ( - // this slot is from TestOracle.sol - TEST_ORACLE_PRICES_MAPPING_SLOT int64 = 4 + UNDERLYING_ASSET_SLOT int64 = 6 + MAX_LIQUIDATION_PRICE_SPREAD int64 = 11 + MULTIPLIER_SLOT int64 = 12 + IMPACT_MARGIN_NOTIONAL_SLOT int64 = 19 + LAST_TRADE_PRICE_SLOT int64 = 20 + BIDS_SLOT int64 = 21 + ASKS_SLOT int64 = 22 + BIDS_HEAD_SLOT int64 = 23 + ASKS_HEAD_SLOT int64 = 24 ) // AMM State diff --git a/precompile/contracts/bibliophile/api.go b/precompile/contracts/bibliophile/api.go index fa6fe17d79..5858056c3b 100644 --- a/precompile/contracts/bibliophile/api.go +++ b/precompile/contracts/bibliophile/api.go @@ -121,7 +121,7 @@ func GetAMMVariables(stateDB contract.StateDB, ammAddress common.Address, ammInd underlyingAssetAddress := getUnderlyingAssetAddress(stateDB, ammAddress) underlyingPriceForMarket := getUnderlyingPriceForMarket(stateDB, ammIndex) underlyingPrice := getUnderlyingPrice(stateDB, ammAddress) - redStoneAdapterAddress := getRedStoneAdapterAddress(stateDB, ammAddress) + redStoneAdapterAddress := getRedStoneAdapterAddress(stateDB, oracleAddress) redStoneFeedId := getRedStoneFeedId(stateDB, oracleAddress, underlyingAssetAddress) bidsHead := getBidsHead(stateDB, ammAddress) bidsHeadSize := getBidSize(stateDB, ammAddress, bidsHead) diff --git a/precompile/contracts/bibliophile/oracle.go b/precompile/contracts/bibliophile/oracle.go index 4fc606c3b9..394abd17de 100644 --- a/precompile/contracts/bibliophile/oracle.go +++ b/precompile/contracts/bibliophile/oracle.go @@ -14,7 +14,12 @@ var ( RED_STONE_LATEST_ROUND_ID_STORAGE_LOCATION = common.HexToHash("0xc68d7f1ee07d8668991a8951e720010c9d44c2f11c06b5cac61fbc4083263938") // keccak256("RedStone.latestRoundId"); AGGREGATOR_MAP_SLOT int64 = 1 - RED_STONE_ADAPTER_SLOT int64 = 3 + RED_STONE_ADAPTER_SLOT int64 = 2 +) + +const ( + // this slot is from TestOracle.sol + TEST_ORACLE_PRICES_MAPPING_SLOT int64 = 3 ) func getUnderlyingPrice(stateDB contract.StateDB, market common.Address) *big.Int { diff --git a/tests/orderbook/abi/AMM.json b/tests/orderbook/abi/AMM.json index c826447fb4..879faa24dd 100644 --- a/tests/orderbook/abi/AMM.json +++ b/tests/orderbook/abi/AMM.json @@ -1,1135 +1,1094 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "_clearingHouse", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "asks", - "outputs": [ - { - "internalType": "uint256", - "name": "nextTick", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "asksHead", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "bids", - "outputs": [ - { - "internalType": "uint256", - "name": "nextTick", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "bidsHead", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "clearingHouse", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "cumulativePremiumFraction", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "fundingPeriod", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getAcceptableBounds", - "outputs": [ - { - "internalType": "uint256", - "name": "upperBound", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lowerBound", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getAcceptableBoundsForLiquidation", - "outputs": [ - { - "internalType": "uint256", - "name": "upperBound", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lowerBound", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - } - ], - "name": "getNotionalPositionAndUnrealizedPnl", - "outputs": [ - { - "internalType": "uint256", - "name": "notionalPosition", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "unrealizedPnl", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "int256", - "name": "positionSize", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "openNotional", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "unrealizedPnl", - "type": "int256" - }, - { - "internalType": "int256", - "name": "baseAssetQuantity", - "type": "int256" - } - ], - "name": "getOpenNotionalWhileReducingPosition", - "outputs": [ - { - "internalType": "uint256", - "name": "remainOpenNotional", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "realizedPnl", - "type": "int256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "internalType": "int256", - "name": "margin", - "type": "int256" - }, - { - "internalType": "enum IClearingHouse.Mode", - "name": "mode", - "type": "uint8" - } - ], - "name": "getOptimalPnl", - "outputs": [ - { - "internalType": "uint256", - "name": "notionalPosition", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "unrealizedPnl", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - } - ], - "name": "getPendingFundingPayment", - "outputs": [ - { - "internalType": "int256", - "name": "takerFundingPayment", - "type": "int256" - }, - { - "internalType": "int256", - "name": "latestCumulativePremiumFraction", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "openNotional", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "size", - "type": "int256" - }, - { - "internalType": "int256", - "name": "margin", - "type": "int256" - } - ], - "name": "getPositionMetadata", - "outputs": [ - { - "internalType": "uint256", - "name": "notionalPos", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "uPnl", - "type": "int256" - }, - { - "internalType": "int256", - "name": "marginFraction", - "type": "int256" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [], - "name": "getUnderlyingPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "governance", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "impactMarginNotional", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "_name", - "type": "string" - }, - { - "internalType": "address", - "name": "_underlyingAsset", - "type": "address" - }, - { - "internalType": "address", - "name": "_oracle", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_minSizeRequirement", - "type": "uint256" - }, - { - "internalType": "address", - "name": "_governance", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_pricePrecision", - "type": "uint256" - }, - { - "internalType": "address", - "name": "_ticks", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "interestRate", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "lastPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "lastTradePrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "fillAmount", - "type": "int256" - } - ], - "name": "liquidatePosition", - "outputs": [ - { - "internalType": "int256", - "name": "realizedPnl", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "quoteAsset", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "size", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "openNotional", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "longOpenInterestNotional", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "maxFundingRate", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "maxLiquidationPriceSpread", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "maxLiquidationRatio", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "maxOracleSpreadRatio", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "minSizeRequirement", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "multiplier", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "nextFundingTime", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "openInterestNotional", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "internalType": "int256", - "name": "fillAmount", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "fulfillPrice", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "is2ndTrade", - "type": "bool" - } - ], - "name": "openPosition", - "outputs": [ - { - "internalType": "int256", - "name": "realizedPnl", - "type": "int256" - }, - { - "internalType": "bool", - "name": "isPositionIncreased", - "type": "bool" - }, - { - "internalType": "int256", - "name": "size", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "openNotional", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "openInterest", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "oracle", - "outputs": [ - { - "internalType": "contract IOracle", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "piData", - "outputs": [ - { - "internalType": "int256", - "name": "piTwap", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "accTime", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "piLast", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "lastTS", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "positions", - "outputs": [ - { - "internalType": "int256", - "name": "size", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "openNotional", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "lastPremiumFraction", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "liquidationThreshold", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "redStoneAdapter", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "redStoneFeedId", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "samplePI", - "outputs": [ - { - "internalType": "bool", - "name": "success", - "type": "bool" - }, - { - "internalType": "int256", - "name": "premiumIndex", - "type": "int256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_fundingPeriod", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "_maxFundingRate", - "type": "int256" - } - ], - "name": "setFundingParams", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "__governance", - "type": "address" - } - ], - "name": "setGovernace", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_impactMarginNotional", - "type": "uint256" - } - ], - "name": "setImpactMarginNotional", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "int256", - "name": "_interestRate", - "type": "int256" - } - ], - "name": "setInterestRate", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_maxLiquidationRatio", - "type": "uint256" - } - ], - "name": "setLiquidationSizeRatio", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_minSizeRequirement", - "type": "uint256" - } - ], - "name": "setMinSizeRequirement", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_oracle", - "type": "address" - }, - { - "internalType": "address", - "name": "_redStoneAdapter", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "_redStoneFeedId", - "type": "bytes32" - } - ], - "name": "setOracleConfig", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_pricePrecision", - "type": "uint256" - } - ], - "name": "setPricePrecision", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_maxOracleSpreadRatio", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_maxLiquidationPriceSpread", - "type": "uint256" - } - ], - "name": "setPriceSpreadParams", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_redStoneAdapter", - "type": "address" - } - ], - "name": "setRedStoneAdapterAddress", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_ticks", - "type": "address" - } - ], - "name": "setTicks", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "settleFunding", - "outputs": [ - { - "internalType": "int256", - "name": "premiumFraction", - "type": "int256" - }, - { - "internalType": "int256", - "name": "underlyingPrice", - "type": "int256" - }, - { - "internalType": "int256", - "name": "", - "type": "int256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "shortOpenInterestNotional", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tick", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "quantity", - "type": "int256" - } - ], - "name": "signalAddLiquidity", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tick", - "type": "uint256" - }, - { - "internalType": "int256", - "name": "quantity", - "type": "int256" - } - ], - "name": "signalRemoveLiquidity", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "startFunding", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "ticks", - "outputs": [ - { - "internalType": "contract ITicks", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_orderBook", - "type": "address" - } - ], - "name": "toggleWhitelistedOrderBook", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "underlyingAsset", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - } - ], - "name": "updatePosition", - "outputs": [ - { - "internalType": "bool", - "name": "isUpdated", - "type": "bool" - }, - { - "internalType": "int256", - "name": "fundingPayment", - "type": "int256" - }, - { - "internalType": "int256", - "name": "latestCumulativePremiumFraction", - "type": "int256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "whitelistedOrderBooks", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - } + { + "inputs": [ + { + "internalType": "address", + "name": "_clearingHouse", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "asks", + "outputs": [ + { + "internalType": "uint256", + "name": "nextTick", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "asksHead", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "bids", + "outputs": [ + { + "internalType": "uint256", + "name": "nextTick", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bidsHead", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "clearingHouse", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cumulativePremiumFraction", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "fundingPeriod", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAcceptableBounds", + "outputs": [ + { + "internalType": "uint256", + "name": "upperBound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lowerBound", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAcceptableBoundsForLiquidation", + "outputs": [ + { + "internalType": "uint256", + "name": "upperBound", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lowerBound", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + } + ], + "name": "getNotionalPositionAndUnrealizedPnl", + "outputs": [ + { + "internalType": "uint256", + "name": "notionalPosition", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "unrealizedPnl", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "positionSize", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "openNotional", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "unrealizedPnl", + "type": "int256" + }, + { + "internalType": "int256", + "name": "baseAssetQuantity", + "type": "int256" + } + ], + "name": "getOpenNotionalWhileReducingPosition", + "outputs": [ + { + "internalType": "uint256", + "name": "remainOpenNotional", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "realizedPnl", + "type": "int256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "int256", + "name": "margin", + "type": "int256" + }, + { + "internalType": "enum IClearingHouse.Mode", + "name": "mode", + "type": "uint8" + } + ], + "name": "getOptimalPnl", + "outputs": [ + { + "internalType": "uint256", + "name": "notionalPosition", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "unrealizedPnl", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + } + ], + "name": "getPendingFundingPayment", + "outputs": [ + { + "internalType": "int256", + "name": "takerFundingPayment", + "type": "int256" + }, + { + "internalType": "int256", + "name": "latestCumulativePremiumFraction", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "openNotional", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "size", + "type": "int256" + }, + { + "internalType": "int256", + "name": "margin", + "type": "int256" + } + ], + "name": "getPositionMetadata", + "outputs": [ + { + "internalType": "uint256", + "name": "notionalPos", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "uPnl", + "type": "int256" + }, + { + "internalType": "int256", + "name": "marginFraction", + "type": "int256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getUnderlyingPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governance", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "impactMarginNotional", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "address", + "name": "_underlyingAsset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_minSizeRequirement", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_governance", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_pricePrecision", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_ticks", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "interestRate", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lastPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "fillAmount", + "type": "int256" + } + ], + "name": "liquidatePosition", + "outputs": [ + { + "internalType": "int256", + "name": "realizedPnl", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "quoteAsset", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "size", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "openNotional", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "longOpenInterestNotional", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "marginAccount", + "outputs": [ + { + "internalType": "contract IMarginAccount", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxFundingRate", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxLiquidationPriceSpread", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxLiquidationRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxOracleSpreadRatio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "midPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minSizeRequirement", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "multiplier", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nextFundingTime", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "openInterestNotional", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "int256", + "name": "fillAmount", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "fulfillPrice", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "is2ndTrade", + "type": "bool" + } + ], + "name": "openPosition", + "outputs": [ + { + "internalType": "int256", + "name": "realizedPnl", + "type": "int256" + }, + { + "internalType": "bool", + "name": "isPositionIncreased", + "type": "bool" + }, + { + "internalType": "int256", + "name": "size", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "openNotional", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "openInterest", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "oracle", + "outputs": [ + { + "internalType": "contract IOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "piData", + "outputs": [ + { + "internalType": "int256", + "name": "piTwap", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "accTime", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "piLast", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "lastTS", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "int256", + "name": "size", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "openNotional", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "lastPremiumFraction", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "liquidationThreshold", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "samplePI", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + }, + { + "internalType": "int256", + "name": "premiumIndex", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_fundingPeriod", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "_maxFundingRate", + "type": "int256" + } + ], + "name": "setFundingParams", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "__governance", + "type": "address" + } + ], + "name": "setGovernace", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_impactMarginNotional", + "type": "uint256" + } + ], + "name": "setImpactMarginNotional", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int256", + "name": "_interestRate", + "type": "int256" + } + ], + "name": "setInterestRate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxLiquidationRatio", + "type": "uint256" + } + ], + "name": "setLiquidationSizeRatio", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_minSizeRequirement", + "type": "uint256" + } + ], + "name": "setMinSizeRequirement", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_pricePrecision", + "type": "uint256" + } + ], + "name": "setPricePrecision", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_maxOracleSpreadRatio", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxLiquidationPriceSpread", + "type": "uint256" + } + ], + "name": "setPriceSpreadParams", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_ticks", + "type": "address" + } + ], + "name": "setTicks", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "settleFunding", + "outputs": [ + { + "internalType": "int256", + "name": "premiumFraction", + "type": "int256" + }, + { + "internalType": "int256", + "name": "underlyingPrice", + "type": "int256" + }, + { + "internalType": "int256", + "name": "", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "shortOpenInterestNotional", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tick", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "quantity", + "type": "int256" + } + ], + "name": "signalAddLiquidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tick", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "quantity", + "type": "int256" + } + ], + "name": "signalRemoveLiquidity", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "startFunding", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "ticks", + "outputs": [ + { + "internalType": "contract ITicks", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_orderBook", + "type": "address" + } + ], + "name": "toggleWhitelistedOrderBook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "underlyingAsset", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + } + ], + "name": "updatePosition", + "outputs": [ + { + "internalType": "bool", + "name": "isUpdated", + "type": "bool" + }, + { + "internalType": "int256", + "name": "fundingPayment", + "type": "int256" + }, + { + "internalType": "int256", + "name": "latestCumulativePremiumFraction", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "whitelistedOrderBooks", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } ] diff --git a/tests/orderbook/abi/MarginAccount.json b/tests/orderbook/abi/MarginAccount.json index 7a8838bcdf..deda936e25 100644 --- a/tests/orderbook/abi/MarginAccount.json +++ b/tests/orderbook/abi/MarginAccount.json @@ -1,1093 +1,1119 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "_trustedForwarder", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "enum IMarginAccount.LiquidationStatus", - "name": "", - "type": "uint8" - } - ], - "name": "NOT_LIQUIDATABLE", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "idx", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "seizeAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "repayAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" - } - ], - "name": "MarginAccountLiquidated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "idx", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" - } - ], - "name": "MarginAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "MarginReleased", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "idx", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" - } - ], - "name": "MarginRemoved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "MarginReserved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Paused", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "indexed": false, - "internalType": "int256", - "name": "realizedPnl", - "type": "int256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" - } - ], - "name": "PnLRealized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "seized", - "type": "uint256[]" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "repayAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" - } - ], - "name": "SettledBadDebt", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Unpaused", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "idx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "addMargin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "idx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - } - ], - "name": "addMarginFor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "idx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_weight", - "type": "uint256" - } - ], - "name": "changeCollateralWeight", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "clearingHouse", - "outputs": [ - { - "internalType": "contract IClearingHouse", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "credit", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - } - ], - "name": "getAvailableMargin", - "outputs": [ - { - "internalType": "int256", - "name": "availableMargin", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "idx", - "type": "uint256" - } - ], - "name": "getCollateralToken", - "outputs": [ - { - "internalType": "contract IERC20", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - } - ], - "name": "getNormalizedMargin", - "outputs": [ - { - "internalType": "int256", - "name": "weighted", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - } - ], - "name": "getSpotCollateralValue", - "outputs": [ - { - "internalType": "int256", - "name": "spot", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "governance", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_governance", - "type": "address" - }, - { - "internalType": "address", - "name": "_vusd", - "type": "address" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "insuranceFund", - "outputs": [ - { - "internalType": "contract IInsuranceFund", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "internalType": "bool", - "name": "includeFunding", - "type": "bool" - } - ], - "name": "isLiquidatable", - "outputs": [ - { - "internalType": "enum IMarginAccount.LiquidationStatus", - "name": "_isLiquidatable", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "repayAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "incentivePerDollar", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "forwarder", - "type": "address" - } - ], - "name": "isTrustedForwarder", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "juror", - "outputs": [ - { - "internalType": "contract IJuror", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "internalType": "uint256", - "name": "repay", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "idx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minSeizeAmount", - "type": "uint256" - } - ], - "name": "liquidateExactRepay", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "internalType": "uint256", - "name": "maxRepay", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "idx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "seize", - "type": "uint256" - } - ], - "name": "liquidateExactSeize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "internalType": "uint256", - "name": "maxRepay", - "type": "uint256" - }, - { - "internalType": "uint256[]", - "name": "idxs", - "type": "uint256[]" - } - ], - "name": "liquidateFlexible", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "liquidationIncentive", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "margin", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "marginAccountHelper", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "minAllowableMargin", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "oracle", - "outputs": [ - { - "internalType": "contract IOracle", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "internalType": "int256", - "name": "realizedPnl", - "type": "int256" - } - ], - "name": "realizePnL", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "releaseMargin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "idx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "removeMargin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "idx", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "address", - "name": "trader", - "type": "address" - } - ], - "name": "removeMarginFor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "reserveMargin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "reservedMargin", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "__governance", - "type": "address" - } - ], - "name": "setGovernace", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_juror", - "type": "address" - } - ], - "name": "setJuror", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - } - ], - "name": "settleBadDebt", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "supportedAssets", - "outputs": [ - { - "components": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "weight", - "type": "uint256" - }, - { - "internalType": "uint8", - "name": "decimals", - "type": "uint8" - } - ], - "internalType": "struct IMarginAccount.Collateral[]", - "name": "", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "supportedAssetsLen", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "supportedCollateral", - "outputs": [ - { - "internalType": "contract IERC20", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "weight", - "type": "uint256" - }, - { - "internalType": "uint8", - "name": "decimals", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_registry", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_liquidationIncentive", - "type": "uint256" - } - ], - "name": "syncDeps", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_settler", - "type": "address" - } - ], - "name": "toggleTrustedSettler", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_orderBook", - "type": "address" - } - ], - "name": "toggleWhitelistedOrderBook", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "transferOutVusd", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "trustedSettlers", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "unpause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_minAllowableMargin", - "type": "uint256" - } - ], - "name": "updateParams", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "vusd", - "outputs": [ - { - "internalType": "contract IERC20FlexibleSupply", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "trader", - "type": "address" - } - ], - "name": "weightedAndSpotCollateral", - "outputs": [ - { - "internalType": "int256", - "name": "weighted", - "type": "int256" - }, - { - "internalType": "int256", - "name": "spot", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_coin", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_weight", - "type": "uint256" - } - ], - "name": "whitelistCollateral", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "whitelistedOrderBooks", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "stateMutability": "payable", - "type": "receive" - } + { + "inputs": [ + { + "internalType": "address", + "name": "_trustedForwarder", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "enum IMarginAccount.LiquidationStatus", + "name": "", + "type": "uint8" + } + ], + "name": "NOT_LIQUIDATABLE", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "idx", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "seizeAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "MarginAccountLiquidated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "idx", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "MarginAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "MarginReleased", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "idx", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "MarginRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "MarginReserved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "indexed": false, + "internalType": "int256", + "name": "realizedPnl", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "PnLRealized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "seized", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "SettledBadDebt", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "idx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "addMargin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "idx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "addMarginFor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "idx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_weight", + "type": "uint256" + } + ], + "name": "changeCollateralWeight", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "clearingHouse", + "outputs": [ + { + "internalType": "contract IClearingHouse", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "credit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + } + ], + "name": "getAvailableMargin", + "outputs": [ + { + "internalType": "int256", + "name": "availableMargin", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "idx", + "type": "uint256" + } + ], + "name": "getCollateralToken", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + } + ], + "name": "getNormalizedMargin", + "outputs": [ + { + "internalType": "int256", + "name": "weighted", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + } + ], + "name": "getSpotCollateralValue", + "outputs": [ + { + "internalType": "int256", + "name": "spot", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governance", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_governance", + "type": "address" + }, + { + "internalType": "address", + "name": "_vusd", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "insuranceFund", + "outputs": [ + { + "internalType": "contract IInsuranceFund", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "bool", + "name": "includeFunding", + "type": "bool" + } + ], + "name": "isLiquidatable", + "outputs": [ + { + "internalType": "enum IMarginAccount.LiquidationStatus", + "name": "_isLiquidatable", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "repayAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "incentivePerDollar", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "forwarder", + "type": "address" + } + ], + "name": "isTrustedForwarder", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "juror", + "outputs": [ + { + "internalType": "contract IJuror", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "repay", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "idx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minSeizeAmount", + "type": "uint256" + } + ], + "name": "liquidateExactRepay", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxRepay", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "idx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "seize", + "type": "uint256" + } + ], + "name": "liquidateExactSeize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "maxRepay", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "idxs", + "type": "uint256[]" + } + ], + "name": "liquidateFlexible", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "liquidationIncentive", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "margin", + "outputs": [ + { + "internalType": "int256", + "name": "", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "marginAccountHelper", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minAllowableMargin", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "oracle", + "outputs": [ + { + "internalType": "contract IOracle", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "int256", + "name": "realizedPnl", + "type": "int256" + } + ], + "name": "realizePnL", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "releaseMargin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "idx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "removeMargin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "idx", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "trader", + "type": "address" + } + ], + "name": "removeMarginFor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "reserveMargin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "reservedMargin", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "__governance", + "type": "address" + } + ], + "name": "setGovernace", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_juror", + "type": "address" + } + ], + "name": "setJuror", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IOracle", + "name": "_oracle", + "type": "address" + } + ], + "name": "setOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + } + ], + "name": "settleBadDebt", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "supportedAssets", + "outputs": [ + { + "components": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "weight", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + } + ], + "internalType": "struct IMarginAccount.Collateral[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "supportedAssetsLen", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "supportedCollateral", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "weight", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_registry", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_liquidationIncentive", + "type": "uint256" + } + ], + "name": "syncDeps", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_settler", + "type": "address" + } + ], + "name": "toggleTrustedSettler", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_orderBook", + "type": "address" + } + ], + "name": "toggleWhitelistedOrderBook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferOutVusd", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "trustedSettlers", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_minAllowableMargin", + "type": "uint256" + } + ], + "name": "updateParams", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vusd", + "outputs": [ + { + "internalType": "contract IERC20FlexibleSupply", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "trader", + "type": "address" + } + ], + "name": "weightedAndSpotCollateral", + "outputs": [ + { + "internalType": "int256", + "name": "weighted", + "type": "int256" + }, + { + "internalType": "int256", + "name": "spot", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_coin", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_weight", + "type": "uint256" + } + ], + "name": "whitelistCollateral", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "whitelistedOrderBooks", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } ] diff --git a/tests/orderbook/abi/Oracle.json b/tests/orderbook/abi/Oracle.json index 42f91a8337..f51ba1e0e9 100644 --- a/tests/orderbook/abi/Oracle.json +++ b/tests/orderbook/abi/Oracle.json @@ -1,142 +1,146 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "_governance", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "chainLinkAggregatorMap", - "outputs": [ - { - "internalType": "address", - "name": "aggregator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "heartbeat", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "underlying", - "type": "address" - } - ], - "name": "getUnderlyingPrice", - "outputs": [ - { - "internalType": "int256", - "name": "answer", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "governance", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "underlying", - "type": "address" - }, - { - "internalType": "address", - "name": "aggregator", - "type": "address" - }, - { - "internalType": "uint256", - "name": "hearbeat", - "type": "uint256" - } - ], - "name": "setAggregator", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "__governance", - "type": "address" - } - ], - "name": "setGovernace", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "underlying", - "type": "address" - }, - { - "internalType": "uint256", - "name": "price", - "type": "uint256" - } - ], - "name": "setStablePrice", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "stablePrice", - "outputs": [ - { - "internalType": "int256", - "name": "", - "type": "int256" - } - ], - "stateMutability": "view", - "type": "function" - } + { + "inputs": [ + { + "internalType": "address", + "name": "_governance", + "type": "address" + }, + { + "internalType": "address", + "name": "_redStoneAdapter", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "aggregatorMap", + "outputs": [ + { + "internalType": "bytes32", + "name": "redstoneFeedId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "aggregator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "heartbeat", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "underlying", + "type": "address" + } + ], + "name": "getUnderlyingPrice", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governance", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "redStoneAdapter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "underlying", + "type": "address" + }, + { + "internalType": "address", + "name": "aggregator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "heartbeat", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "redstoneFeedId", + "type": "bytes32" + } + ], + "name": "setAggregator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "__governance", + "type": "address" + } + ], + "name": "setGovernace", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_redStoneAdapter", + "type": "address" + } + ], + "name": "setRedStoneAdapterAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } ] diff --git a/tests/orderbook/bibliophile/variablesReadFromSlotTests.js b/tests/orderbook/bibliophile/variablesReadFromSlotTests.js index dac41053c1..79a43c226c 100644 --- a/tests/orderbook/bibliophile/variablesReadFromSlotTests.js +++ b/tests/orderbook/bibliophile/variablesReadFromSlotTests.js @@ -127,7 +127,7 @@ describe('Testing variables read from slots by precompile', function () { }) context("AMM contract variables", function () { // vars read from slot - // positions, cumulativePremiumFraction, maxOracleSpreadRatio, maxLiquidationRatio, minSizeRequirement, oracle, underlyingAsset, + // positions, cumulativePremiumFraction, maxOracleSpreadRatio, maxLiquidationRatio, minSizeRequirement, oracle, underlyingAsset, // maxLiquidationPriceSpread, redStoneAdapter, redStoneFeedId, impactMarginNotional, lastTradePrice, bids, asks, bidsHead, asksHead let ammIndex = 0 let method ="testing_getAMMVars" @@ -163,28 +163,28 @@ describe('Testing variables read from slots by precompile', function () { context("when variables dont have default value after setup", async function () { // positions, cumulativePremiumFraction, redStoneAdapter, redStoneFeedId, impactMarginNotional, lastTradePrice, bids, asks, bidsHead, asksHead context("variables which need set config before reading", async function () { - // redStoneAdapter, redStoneFeedId, impactMarginNotional - // impactMarginNotional - let amm, oracleAddress, redStoneAdapterAddress, redStoneFeedId, impactMarginNotional + let amm, oracleAddress, redStoneAdapterAddress, impactMarginNotional this.beforeAll(async function () { amm = await getAMMContract(ammIndex) oracleAddress = await amm.oracle() - redStoneAdapterAddress = await amm.redStoneAdapter() - redStoneFeedId = await amm.redStoneFeedId() + oracle = new ethers.Contract(oracleAddress, require("../abi/Oracle.json"), provider); + marginAccount = new ethers.Contract(await amm.marginAccount(), require("../abi/MarginAccount.json"), provider); + + redStoneAdapterAddress = await oracle.redStoneAdapter() + console.log("redStoneAdapterAddress", redStoneAdapterAddress) impactMarginNotional = await amm.impactMarginNotional() }) this.afterAll(async function () { - await amm.connect(governance).setOracleConfig(oracleAddress, redStoneAdapterAddress, redStoneFeedId) + await oracle.connect(governance).setRedStoneAdapterAddress(redStoneAdapterAddress) + await marginAccount.connect(governance).setOracle(oracleAddress) await amm.connect(governance).setImpactMarginNotional(impactMarginNotional) }) it("should read the correct value from contracts", async function () { newOracleAddress = alice.address newRedStoneAdapterAddress = bob.address - newRedStoneFeedId = ethers.utils.formatBytes32String("redStoneFeedId") - tx = await amm.connect(governance).setOracleConfig(newOracleAddress, newRedStoneAdapterAddress, newRedStoneFeedId) - await tx.wait() - newImpactMarginNotional = BigNumber.from(100000) + + tx = await oracle.connect(governance).setRedStoneAdapterAddress(newRedStoneAdapterAddress) tx = await amm.connect(governance).setImpactMarginNotional(newImpactMarginNotional) await tx.wait() @@ -192,9 +192,16 @@ describe('Testing variables read from slots by precompile', function () { response = await makehttpCall(method, params) result = response.body.result - expect(result.oracle_address.toLowerCase()).to.equal(newOracleAddress.toLowerCase()) expect(result.red_stone_adapter_address.toLowerCase()).to.equal(newRedStoneAdapterAddress.toLowerCase()) - expect(result.red_stone_feed_id).to.equal(newRedStoneFeedId) + expect(result.impact_margin_notional).to.equal(newImpactMarginNotional.toNumber()) + + // setOracle + tx = await marginAccount.connect(governance).setOracle(newOracleAddress) + await tx.wait() + response = await makehttpCall(method, params) + result = response.body.result + expect(result.oracle_address.toLowerCase()).to.equal(newOracleAddress.toLowerCase()) + expect(result.red_stone_adapter_address.toLowerCase()).to.equal('0x' + '0'.repeat(40)) // red stone adapter should be zero in new oracle expect(result.impact_margin_notional).to.equal(newImpactMarginNotional.toNumber()) }) }) @@ -287,7 +294,7 @@ describe('Testing variables read from slots by precompile', function () { //ioc expiration cap it("should read the correct value from contracts", async function () { params = [ "0xe97a0702264091714ea19b481c1fd12d9686cb4602efbfbec41ec5ea5410da84"] - + result = (await makehttpCall(method, params)).body.result actualExpirationCap = await ioc.expirationCap() expect(result.ioc_expiration_cap).to.eq(actualExpirationCap.toNumber()) @@ -319,7 +326,7 @@ describe('Testing variables read from slots by precompile', function () { longIOCOrder = getIOCOrder(expireAt, market, charlie.address, longOrderBaseAssetQuantity, orderPrice, getRandomSalt(), false) orderHash = await ioc.getOrderHash(longIOCOrder) params = [ orderHash ] - txDetails = await placeIOCOrder(longIOCOrder, charlie) + txDetails = await placeIOCOrder(longIOCOrder, charlie) result = (await makehttpCall(method, params)).body.result //cleanup diff --git a/tests/orderbook/juror/JurorTests.js b/tests/orderbook/juror/JurorTests.js index 37d01eb2a8..22d319e7d8 100644 --- a/tests/orderbook/juror/JurorTests.js +++ b/tests/orderbook/juror/JurorTests.js @@ -38,7 +38,7 @@ describe("Juror tests", async function() { let longOrderBaseAssetQuantity = multiplySize(0.1) // 0.1 ether let orderPrice = multiplyPrice(1800) let market = BigNumber.from(0) - let longOrder = getOrderV2(market, alice.address, longOrderBaseAssetQuantity, orderPrice, getRandomSalt()) + let longOrder = getOrderV2(market, alice.address, longOrderBaseAssetQuantity, orderPrice, getRandomSalt()) it("should fail as trader has not margin", async function() { await removeAllAvailableMargin(alice) @@ -132,7 +132,7 @@ describe("Juror tests", async function() { let shortOrderBaseAssetQuantity = multiplySize(-0.1) // 0.1 ether let orderPrice = multiplyPrice(1800) let market = BigNumber.from(0) - let shortOrder = getOrderV2(market, bob.address, shortOrderBaseAssetQuantity, orderPrice, getRandomSalt()) + let shortOrder = getOrderV2(market, bob.address, shortOrderBaseAssetQuantity, orderPrice, getRandomSalt()) let tradingAuthority = charlie it("should fail as trader has no margin", async function() { @@ -202,7 +202,7 @@ describe("Juror tests", async function() { expect(output.orderHash).to.equal(expectedOrderHash) expect(output.res.unfilledAmount.toString()).to.equal(shortOrder.baseAssetQuantity.toString()) expect(output.res.amm).to.equal(await clearingHouse.amms(market)) - + await cancelOrderFromLimitOrderV2(shortOrder, tradingAuthority) orderStatus = await limitOrderBook.orderStatus(expectedOrderHash) expect(orderStatus.status).to.equal(3) @@ -408,7 +408,7 @@ describe("Juror tests", async function() { totalRequiredMarginForShortOrder3 = await getRequiredMarginForShortOrder(shortOrder3) expect(output.res.reserveAmount.toNumber()).to.equal(totalRequiredMarginForShortOrder3.toNumber()) expectedAmmAddress = await clearingHouse.amms(market) - + // place the order output = await placeOrderFromLimitOrderV2(shortOrder3, marketMaker) limitOrderBookLogWithEvent = (await getEventsFromLimitOrderBookTx(output.txReceipt.transactionHash))[0] @@ -469,7 +469,7 @@ describe("Juror tests", async function() { expect(output.orderHash).to.equal(expectedOrderHash) expect(output.res.unfilledAmount.toString()).to.equal(longOrder.baseAssetQuantity.toString()) expect(output.res.amm).to.equal(await clearingHouse.amms(market)) - + await cancelOrderFromLimitOrderV2(longOrder, marketMaker) orderStatus = await limitOrderBook.orderStatus(expectedOrderHash) expect(orderStatus.status).to.equal(3) @@ -563,7 +563,7 @@ describe("Juror tests", async function() { // Alice has long Position and bob has short position // If reduceOnly order is longOrder - it should fail // Alice tries to place a short reduceOnly order when she has an open shortOrder - it should fail - // when there is no open shortOrder for alice and alice tries to place a short reduceOnly order - it should succeed + // when there is no open shortOrder for alice and alice tries to place a short reduceOnly order - it should succeed // after placing short reduceOnly order, alice tries to place a normal shortOrder - it should fail // if currentOrder size + (sum of size of all reduceOnly orders) > posSize of alice - it should fail // if currentOrder size + (sum of size of all reduceOnly orders) < posSize of alice - it should succeed diff --git a/tests/orderbook/tests/test.js b/tests/orderbook/tests/test.js index 9579d36564..93e9e404dc 100644 --- a/tests/orderbook/tests/test.js +++ b/tests/orderbook/tests/test.js @@ -664,7 +664,7 @@ async function setOraclePrice(market, price) { const oracleAddress = await marginAccount.oracle() const oracle = new ethers.Contract(oracleAddress, require('../abi/Oracle.json'), provider); - await oracle.connect(governance).setStablePrice(underlying, price) + await oracle.connect(governance).setUnderlyingPrice(underlying, price) } async function getOraclePrice(market) { @@ -713,4 +713,4 @@ module.exports = { sleep, getOraclePrice, getLastPrice -} \ No newline at end of file +} diff --git a/tests/orderbook/utils.js b/tests/orderbook/utils.js index 774159aba4..f414f6bf32 100644 --- a/tests/orderbook/utils.js +++ b/tests/orderbook/utils.js @@ -18,7 +18,7 @@ const OrderBookContractAddress = "0x0300000000000000000000000000000000000000" const MarginAccountContractAddress = "0x0300000000000000000000000000000000000001" const ClearingHouseContractAddress = "0x0300000000000000000000000000000000000002" const JurorPrecompileAddress = "0x0300000000000000000000000000000000000003" -const TicksPrecompileAddress = "0x0300000000000000000000000000000000000004" +const TicksPrecompileAddress = "0x0300000000000000000000000000000000000004" const LimitOrderBookContractAddress = "0x0300000000000000000000000000000000000005" const IOCContractAddress = "0x0300000000000000000000000000000000000006"