diff --git a/plugin/evm/limit_order.go b/plugin/evm/limit_order.go index 778b00410b..8436cd6f10 100644 --- a/plugin/evm/limit_order.go +++ b/plugin/evm/limit_order.go @@ -77,7 +77,7 @@ func NewLimitOrderProcesser(ctx *snow.Context, txPool *txpool.TxPool, shutdownCh MinAllowableMargin: configService.GetMinAllowableMargin(), MaintenanceMargin: configService.GetMaintenanceMargin(), TakerFee: configService.GetTakerFee(), - UpgradeVersion: configService.GetUpgradeVersion(), + UpgradeVersion: hu.V2, } hu.SetHubbleState(hState) hu.SetChainIdAndVerifyingSignedOrdersContract(backend.ChainConfig().ChainID.Int64(), signedObAddy.String()) diff --git a/plugin/evm/orderbook/config_service.go b/plugin/evm/orderbook/config_service.go index ee71a82471..9411e2fae5 100644 --- a/plugin/evm/orderbook/config_service.go +++ b/plugin/evm/orderbook/config_service.go @@ -31,7 +31,6 @@ type IConfigService interface { GetSignedOrderStatus(orderHash common.Hash) int64 IsTradingAuthority(trader, signer common.Address) bool GetSignedOrderbookContract() common.Address - GetUpgradeVersion() hu.UpgradeVersion GetMarketAddressFromMarketID(marketId int64) common.Address GetImpactMarginNotional(ammAddress common.Address) *big.Int @@ -132,14 +131,6 @@ func (cs *ConfigService) GetSignedOrderbookContract() common.Address { return bibliophile.GetSignedOrderBookAddress(cs.getStateAtCurrentBlock()) } -func (cs *ConfigService) GetUpgradeVersion() hu.UpgradeVersion { - jurorAddy := bibliophile.JurorAddress(cs.getStateAtCurrentBlock()) - if jurorAddy == common.HexToAddress("0x03000000000000000000000000000000000000a2") { - return hu.V2 - } - return hu.V1 -} - func (cs *ConfigService) GetMarketAddressFromMarketID(marketId int64) common.Address { return bibliophile.GetMarketAddressFromMarketID(marketId, cs.getStateAtCurrentBlock()) } diff --git a/plugin/evm/orderbook/hubbleutils/config.go b/plugin/evm/orderbook/hubbleutils/config.go index 5d5c885716..17e4931e78 100644 --- a/plugin/evm/orderbook/hubbleutils/config.go +++ b/plugin/evm/orderbook/hubbleutils/config.go @@ -1,9 +1,11 @@ package hubbleutils +import "math/big" + var ( ChainId int64 VerifyingContract string - HState *HubbleState + hState *HubbleState ) func SetChainIdAndVerifyingSignedOrdersContract(chainId int64, verifyingContract string) { @@ -11,6 +13,23 @@ func SetChainIdAndVerifyingSignedOrdersContract(chainId int64, verifyingContract VerifyingContract = verifyingContract } -func SetHubbleState(hState *HubbleState) { - HState = hState +func SetHubbleState(_hState *HubbleState) { + hState = _hState +} + +func GetHubbleState() *HubbleState { + assets := make([]Collateral, len(hState.Assets)) + copy(assets, hState.Assets) + + activeMarkets := make([]Market, len(hState.ActiveMarkets)) + copy(activeMarkets, hState.ActiveMarkets) + + return &HubbleState{ + Assets: assets, + ActiveMarkets: activeMarkets, + MinAllowableMargin: new(big.Int).Set(hState.MinAllowableMargin), + MaintenanceMargin: new(big.Int).Set(hState.MaintenanceMargin), + TakerFee: new(big.Int).Set(hState.TakerFee), + UpgradeVersion: hState.UpgradeVersion, + } } diff --git a/plugin/evm/orderbook/hubbleutils/margin_math.go b/plugin/evm/orderbook/hubbleutils/margin_math.go index 39e6e2dbc9..ce440ed6e5 100644 --- a/plugin/evm/orderbook/hubbleutils/margin_math.go +++ b/plugin/evm/orderbook/hubbleutils/margin_math.go @@ -160,3 +160,11 @@ func GetRequiredMargin(price, fillAmount, minAllowableMargin, takerFee *big.Int) quoteAsset := Div1e18(Mul(fillAmount, price)) return Add(Div1e6(Mul(quoteAsset, minAllowableMargin)), Div1e6(Mul(quoteAsset, takerFee))) } + +func ArrayToMap(prices []*big.Int) map[Market]*big.Int { + underlyingPrices := make(map[Market]*big.Int) + for market, price := range prices { + underlyingPrices[Market(market)] = price + } + return underlyingPrices +} diff --git a/plugin/evm/orderbook/hubbleutils/margin_math_test.go b/plugin/evm/orderbook/hubbleutils/margin_math_test.go index 7d4fe1d40f..cbbe9471a9 100644 --- a/plugin/evm/orderbook/hubbleutils/margin_math_test.go +++ b/plugin/evm/orderbook/hubbleutils/margin_math_test.go @@ -8,7 +8,7 @@ import ( "github.com/stretchr/testify/assert" ) -var hState = &HubbleState{ +var _hState = &HubbleState{ Assets: []Collateral{ { Price: big.NewInt(1.01 * 1e6), // 1.01 @@ -57,7 +57,7 @@ var userState = &UserState{ } func TestWeightedAndSpotCollateral(t *testing.T) { - assets := hState.Assets + assets := _hState.Assets margins := userState.Margins expectedWeighted := Unscale(Mul(Mul(margins[0], assets[0].Price), assets[0].Weight), assets[0].Decimals+6) expectedWeighted.Add(expectedWeighted, Unscale(Mul(Mul(margins[1], assets[1].Price), assets[1].Weight), assets[1].Decimals+6)) @@ -122,10 +122,10 @@ func TestGetOptimalPnlV2(t *testing.T) { position := userState.Positions[market] marginMode := Maintenance_Margin - notionalPosition, uPnL := getOptimalPnl(hState, position, margin, market, marginMode) + notionalPosition, uPnL := getOptimalPnl(_hState, position, margin, market, marginMode) // mid price pnl is more than oracle price pnl - expectedNotionalPosition := Unscale(Mul(position.Size, hState.MidPrices[market]), 18) + expectedNotionalPosition := Unscale(Mul(position.Size, _hState.MidPrices[market]), 18) expectedUPnL := Sub(expectedNotionalPosition, position.OpenNotional) fmt.Println("Maintenace_Margin_Mode", "notionalPosition", notionalPosition, "uPnL", uPnL) @@ -135,9 +135,9 @@ func TestGetOptimalPnlV2(t *testing.T) { // ------ when marginMode is Min_Allowable_Margin ------ marginMode = Min_Allowable_Margin - notionalPosition, uPnL = getOptimalPnl(hState, position, margin, market, marginMode) + notionalPosition, uPnL = getOptimalPnl(_hState, position, margin, market, marginMode) - expectedNotionalPosition = Unscale(Mul(position.Size, hState.OraclePrices[market]), 18) + expectedNotionalPosition = Unscale(Mul(position.Size, _hState.OraclePrices[market]), 18) expectedUPnL = Sub(expectedNotionalPosition, position.OpenNotional) fmt.Println("Min_Allowable_Margin_Mode", "notionalPosition", notionalPosition, "uPnL", uPnL) @@ -151,10 +151,10 @@ func TestGetOptimalPnlV1(t *testing.T) { position := userState.Positions[market] marginMode := Maintenance_Margin - notionalPosition, uPnL := getOptimalPnl(hState, position, margin, market, marginMode) + notionalPosition, uPnL := getOptimalPnl(_hState, position, margin, market, marginMode) // mid price pnl is more than oracle price pnl - expectedNotionalPosition := Unscale(Mul(position.Size, hState.MidPrices[market]), 18) + expectedNotionalPosition := Unscale(Mul(position.Size, _hState.MidPrices[market]), 18) expectedUPnL := Sub(expectedNotionalPosition, position.OpenNotional) fmt.Println("Maintenace_Margin_Mode", "notionalPosition", notionalPosition, "uPnL", uPnL) @@ -164,9 +164,9 @@ func TestGetOptimalPnlV1(t *testing.T) { // ------ when marginMode is Min_Allowable_Margin ------ marginMode = Min_Allowable_Margin - notionalPosition, uPnL = getOptimalPnl(hState, position, margin, market, marginMode) + notionalPosition, uPnL = getOptimalPnl(_hState, position, margin, market, marginMode) - expectedNotionalPosition = Unscale(Mul(position.Size, hState.OraclePrices[market]), 18) + expectedNotionalPosition = Unscale(Mul(position.Size, _hState.OraclePrices[market]), 18) expectedUPnL = Sub(expectedNotionalPosition, position.OpenNotional) fmt.Println("Min_Allowable_Margin_Mode", "notionalPosition", notionalPosition, "uPnL", uPnL) @@ -175,15 +175,15 @@ func TestGetOptimalPnlV1(t *testing.T) { } func TestGetTotalNotionalPositionAndUnrealizedPnlV2(t *testing.T) { - margin := GetNormalizedMargin(hState.Assets, userState.Margins) + margin := GetNormalizedMargin(_hState.Assets, userState.Margins) marginMode := Maintenance_Margin - notionalPosition, uPnL := GetTotalNotionalPositionAndUnrealizedPnl(hState, userState, margin, marginMode) + notionalPosition, uPnL := GetTotalNotionalPositionAndUnrealizedPnl(_hState, userState, margin, marginMode) // mid price pnl is more than oracle price pnl for long position - expectedNotionalPosition := Unscale(Mul(userState.Positions[0].Size, hState.MidPrices[0]), 18) + expectedNotionalPosition := Unscale(Mul(userState.Positions[0].Size, _hState.MidPrices[0]), 18) expectedUPnL := Sub(expectedNotionalPosition, userState.Positions[0].OpenNotional) // oracle price pnl is more than mid price pnl for short position - expectedNotional2 := Abs(Unscale(Mul(userState.Positions[1].Size, hState.OraclePrices[1]), 18)) + expectedNotional2 := Abs(Unscale(Mul(userState.Positions[1].Size, _hState.OraclePrices[1]), 18)) expectedNotionalPosition.Add(expectedNotionalPosition, expectedNotional2) expectedUPnL.Add(expectedUPnL, Sub(userState.Positions[1].OpenNotional, expectedNotional2)) @@ -193,12 +193,12 @@ func TestGetTotalNotionalPositionAndUnrealizedPnlV2(t *testing.T) { // ------ when marginMode is Min_Allowable_Margin ------ marginMode = Min_Allowable_Margin - notionalPosition, uPnL = GetTotalNotionalPositionAndUnrealizedPnl(hState, userState, margin, marginMode) + notionalPosition, uPnL = GetTotalNotionalPositionAndUnrealizedPnl(_hState, userState, margin, marginMode) fmt.Println("Min_Allowable_Margin_Mode ", "notionalPosition = ", notionalPosition, "uPnL = ", uPnL) - expectedNotionalPosition = Unscale(Mul(userState.Positions[0].Size, hState.OraclePrices[0]), 18) + expectedNotionalPosition = Unscale(Mul(userState.Positions[0].Size, _hState.OraclePrices[0]), 18) expectedUPnL = Sub(expectedNotionalPosition, userState.Positions[0].OpenNotional) - expectedNotional2 = Abs(Unscale(Mul(userState.Positions[1].Size, hState.MidPrices[1]), 18)) + expectedNotional2 = Abs(Unscale(Mul(userState.Positions[1].Size, _hState.MidPrices[1]), 18)) expectedNotionalPosition.Add(expectedNotionalPosition, expectedNotional2) expectedUPnL.Add(expectedUPnL, Sub(userState.Positions[1].OpenNotional, expectedNotional2)) @@ -207,16 +207,16 @@ func TestGetTotalNotionalPositionAndUnrealizedPnlV2(t *testing.T) { } func TestGetTotalNotionalPositionAndUnrealizedPnl(t *testing.T) { - margin := GetNormalizedMargin(hState.Assets, userState.Margins) + margin := GetNormalizedMargin(_hState.Assets, userState.Margins) marginMode := Maintenance_Margin - hState.UpgradeVersion = V2 - notionalPosition, uPnL := GetTotalNotionalPositionAndUnrealizedPnl(hState, userState, margin, marginMode) + _hState.UpgradeVersion = V2 + notionalPosition, uPnL := GetTotalNotionalPositionAndUnrealizedPnl(_hState, userState, margin, marginMode) // mid price pnl is more than oracle price pnl for long position - expectedNotionalPosition := Unscale(Mul(userState.Positions[0].Size, hState.OraclePrices[0]), 18) + expectedNotionalPosition := Unscale(Mul(userState.Positions[0].Size, _hState.OraclePrices[0]), 18) expectedUPnL := Sub(expectedNotionalPosition, userState.Positions[0].OpenNotional) // oracle price pnl is more than mid price pnl for short position - expectedNotional2 := Abs(Unscale(Mul(userState.Positions[1].Size, hState.OraclePrices[1]), 18)) + expectedNotional2 := Abs(Unscale(Mul(userState.Positions[1].Size, _hState.OraclePrices[1]), 18)) expectedNotionalPosition.Add(expectedNotionalPosition, expectedNotional2) expectedUPnL.Add(expectedUPnL, Sub(userState.Positions[1].OpenNotional, expectedNotional2)) @@ -226,12 +226,12 @@ func TestGetTotalNotionalPositionAndUnrealizedPnl(t *testing.T) { // ------ when marginMode is Min_Allowable_Margin ------ marginMode = Min_Allowable_Margin - notionalPosition, uPnL = GetTotalNotionalPositionAndUnrealizedPnl(hState, userState, margin, marginMode) + notionalPosition, uPnL = GetTotalNotionalPositionAndUnrealizedPnl(_hState, userState, margin, marginMode) fmt.Println("Min_Allowable_Margin_Mode ", "notionalPosition = ", notionalPosition, "uPnL = ", uPnL) - expectedNotionalPosition = Unscale(Mul(userState.Positions[0].Size, hState.OraclePrices[0]), 18) + expectedNotionalPosition = Unscale(Mul(userState.Positions[0].Size, _hState.OraclePrices[0]), 18) expectedUPnL = Sub(expectedNotionalPosition, userState.Positions[0].OpenNotional) - expectedNotional2 = Abs(Unscale(Mul(userState.Positions[1].Size, hState.OraclePrices[1]), 18)) + expectedNotional2 = Abs(Unscale(Mul(userState.Positions[1].Size, _hState.OraclePrices[1]), 18)) expectedNotionalPosition.Add(expectedNotionalPosition, expectedNotional2) expectedUPnL.Add(expectedUPnL, Sub(userState.Positions[1].OpenNotional, expectedNotional2)) diff --git a/plugin/evm/orderbook/matching_pipeline.go b/plugin/evm/orderbook/matching_pipeline.go index 13cf5016c1..546081da99 100644 --- a/plugin/evm/orderbook/matching_pipeline.go +++ b/plugin/evm/orderbook/matching_pipeline.go @@ -78,10 +78,9 @@ func (pipeline *MatchingPipeline) Run(blockNumber *big.Int) bool { } } - // fetch the underlying price and run the matching engine - hState := hu.HState - hState.OraclePrices = pipeline.GetUnderlyingPrices() - hState.MidPrices = pipeline.GetMidPrices() + // fetch various hubble market params and run the matching engine + hState := hu.GetHubbleState() + hState.OraclePrices = hu.ArrayToMap(pipeline.configService.GetUnderlyingPrices()) // build trader map liquidablePositions, ordersToCancel, marginMap := pipeline.db.GetNaughtyTraders(hState) @@ -120,26 +119,6 @@ func (pipeline *MatchingPipeline) GetActiveMarkets() []Market { return markets } -func (pipeline *MatchingPipeline) GetUnderlyingPrices() map[Market]*big.Int { - prices := pipeline.configService.GetUnderlyingPrices() - // log.Info("GetUnderlyingPrices", "prices", prices) - underlyingPrices := make(map[Market]*big.Int) - for market, price := range prices { - underlyingPrices[Market(market)] = price - } - return underlyingPrices -} - -func (pipeline *MatchingPipeline) GetMidPrices() map[Market]*big.Int { - prices := pipeline.configService.GetMidPrices() - // log.Info("GetMidPrices", "prices", prices) - midPrices := make(map[Market]*big.Int) - for market, price := range prices { - midPrices[Market(market)] = price - } - return midPrices -} - func (pipeline *MatchingPipeline) GetCollaterals() []hu.Collateral { return pipeline.configService.GetCollaterals() } diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index 3ff20ec5fb..f092ceee33 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -258,6 +258,7 @@ type LimitOrderDatabase interface { GetOrderValidationFields(orderId common.Hash, order *hu.SignedOrder) OrderValidationFields SampleImpactPrice() (impactBids, impactAsks, midPrices []*big.Int) RemoveExpiredSignedOrders() + GetMarginAvailableForMakerbook(trader common.Address, prices map[int]*big.Int) *big.Int } type Snapshot struct { @@ -1290,11 +1291,10 @@ func getOrderIdx(orders []*Order, orderId common.Hash) int { } type OrderValidationFields struct { - Exists bool - PosSize *big.Int - AvailableMargin *big.Int - AsksHead *big.Int - BidsHead *big.Int + Exists bool + PosSize *big.Int + AsksHead *big.Int + BidsHead *big.Int } func (db *InMemoryDatabase) GetOrderValidationFields(orderId common.Hash, order *hu.SignedOrder) OrderValidationFields { @@ -1323,28 +1323,35 @@ func (db *InMemoryDatabase) GetOrderValidationFields(orderId common.Hash, order bidsHead = db.LongOrders[marketId][0].Price } - availableMargin := big.NewInt(0) + return OrderValidationFields{ + Exists: false, + PosSize: posSize, + AsksHead: asksHead, + BidsHead: bidsHead, + } +} + +func (db *InMemoryDatabase) GetMarginAvailableForMakerbook(trader common.Address, prices map[int]*big.Int) *big.Int { + db.mu.RLock() + defer db.mu.RUnlock() + _trader := db.TraderMap[trader] - if _trader != nil { - hState := hu.HState - userState := &hu.UserState{ - Positions: translatePositions(_trader.Positions), - Margins: getMargins(_trader, len(hState.Assets)), - PendingFunding: getTotalFunding(_trader, hState.ActiveMarkets), - ReservedMargin: new(big.Int).Set(_trader.Margin.Reserved), - } - if _trader.Margin.VirtualReserved == nil { - _trader.Margin.VirtualReserved = big.NewInt(0) - } - availableMargin = hu.Sub(hu.GetAvailableMargin(hState, userState), _trader.Margin.VirtualReserved) + if _trader == nil { + return big.NewInt(0) } - return OrderValidationFields{ - Exists: false, - PosSize: posSize, - AvailableMargin: availableMargin, - AsksHead: asksHead, - BidsHead: bidsHead, + + hState := hu.GetHubbleState() + hState.OraclePrices = prices + userState := &hu.UserState{ + Positions: translatePositions(_trader.Positions), + Margins: getMargins(_trader, len(hState.Assets)), + PendingFunding: getTotalFunding(_trader, hState.ActiveMarkets), + ReservedMargin: new(big.Int).Set(_trader.Margin.Reserved), + } + if _trader.Margin.VirtualReserved == nil { + _trader.Margin.VirtualReserved = big.NewInt(0) } + return hu.Sub(hu.GetAvailableMargin(hState, userState), _trader.Margin.VirtualReserved) } func (db *InMemoryDatabase) SampleImpactPrice() (impactBids, impactAsks, midPrices []*big.Int) { diff --git a/plugin/evm/orderbook/mocks.go b/plugin/evm/orderbook/mocks.go index 1230e250d0..9ff2bdd61b 100644 --- a/plugin/evm/orderbook/mocks.go +++ b/plugin/evm/orderbook/mocks.go @@ -170,6 +170,10 @@ func (db *MockLimitOrderDatabase) SampleImpactPrice() (impactBids, impactAsks, m func (db *MockLimitOrderDatabase) RemoveExpiredSignedOrders() {} +func (db *MockLimitOrderDatabase) GetMarginAvailableForMakerbook(trader common.Address, prices map[int]*big.Int) *big.Int { + return big.NewInt(0) +} + type MockLimitOrderTxProcessor struct { mock.Mock } @@ -328,10 +332,6 @@ func (cs *MockConfigService) GetSignedOrderbookContract() common.Address { return common.Address{} } -func (cs *MockConfigService) GetUpgradeVersion() hu.UpgradeVersion { - return hu.V2 -} - func (cs *MockConfigService) GetMarketAddressFromMarketID(marketId int64) common.Address { return common.Address{} } diff --git a/plugin/evm/orderbook/trading_apis.go b/plugin/evm/orderbook/trading_apis.go index 8f77b9f21e..04ef4c5d97 100644 --- a/plugin/evm/orderbook/trading_apis.go +++ b/plugin/evm/orderbook/trading_apis.go @@ -349,7 +349,8 @@ func (api *TradingAPI) PlaceOrder(order *hu.SignedOrder) (common.Hash, error) { minAllowableMargin := api.configService.GetMinAllowableMargin() // even tho order might be matched at a different price, we reserve margin at the price the order was placed at to keep it simple requiredMargin = hu.GetRequiredMargin(order.Price, hu.Abs(order.BaseAssetQuantity), minAllowableMargin, big.NewInt(0)) - if fields.AvailableMargin.Cmp(requiredMargin) == -1 { + availableMargin := api.db.GetMarginAvailableForMakerbook(trader, hu.ArrayToMap(api.configService.GetUnderlyingPrices())) + if availableMargin.Cmp(requiredMargin) == -1 { return orderId, hu.ErrInsufficientMargin } } else {