From 0a8ab9eca367b074209ec1897dab96e7c8d06273 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Mon, 4 Dec 2023 08:49:40 +0000 Subject: [PATCH 01/12] tradingApi.PostOrder --- plugin/evm/orderbook/config_service.go | 8 + .../orderbook/hubbleutils/data_structures.go | 20 +++ .../evm/orderbook/hubbleutils/hubble_math.go | 18 ++ plugin/evm/orderbook/memory_database.go | 29 ++++ plugin/evm/orderbook/order_types.go | 87 +++++++++- plugin/evm/orderbook/trading_apis.go | 154 ++++++++++++++++++ precompile/contracts/bibliophile/client.go | 5 + 7 files changed, 317 insertions(+), 4 deletions(-) diff --git a/plugin/evm/orderbook/config_service.go b/plugin/evm/orderbook/config_service.go index 85c686df23..75ca4626ce 100644 --- a/plugin/evm/orderbook/config_service.go +++ b/plugin/evm/orderbook/config_service.go @@ -16,6 +16,7 @@ type IConfigService interface { getMinAllowableMargin() *big.Int getMaintenanceMargin() *big.Int getMinSizeRequirement(market Market) *big.Int + GetActiveMarkets() []Market GetActiveMarketsCount() int64 GetUnderlyingPrices() []*big.Int GetMidPrices() []*big.Int @@ -24,6 +25,8 @@ type IConfigService interface { GetCumulativePremiumFraction(market Market) *big.Int GetAcceptableBounds(market Market) (*big.Int, *big.Int) GetAcceptableBoundsForLiquidation(market Market) (*big.Int, *big.Int) + + GetSignedOrderStatus(orderHash [32]byte) int64 } type ConfigService struct { @@ -102,3 +105,8 @@ func (cs *ConfigService) GetCumulativePremiumFraction(market Market) *big.Int { markets := bibliophile.GetMarkets(cs.getStateAtCurrentBlock()) return bibliophile.GetCumulativePremiumFraction(cs.getStateAtCurrentBlock(), markets[market]) } + +func (cs *ConfigService) GetSignedOrderStatus(orderHash common.Hash) int64 { + return 0 // @todo + // return bibliophile.GetSignedOrderStatus(cs.getStateAtCurrentBlock(), orderHash) +} diff --git a/plugin/evm/orderbook/hubbleutils/data_structures.go b/plugin/evm/orderbook/hubbleutils/data_structures.go index 8bca2d54a0..b48e057bef 100644 --- a/plugin/evm/orderbook/hubbleutils/data_structures.go +++ b/plugin/evm/orderbook/hubbleutils/data_structures.go @@ -3,6 +3,8 @@ package hubbleutils import ( // "encoding/json" "math/big" + + "github.com/ethereum/go-ethereum/common" ) type MarginMode = uint8 @@ -34,3 +36,21 @@ type Margin struct { Reserved *big.Int `json:"reserved"` Deposited map[Collateral]*big.Int `json:"deposited"` } + +type Side uint8 + +const ( + Long Side = iota + Short + Liquidation +) + +type OrderStatus uint8 + +// has to be exact same as IOrderHandler +const ( + Invalid OrderStatus = iota + Placed + Filled + Cancelled +) diff --git a/plugin/evm/orderbook/hubbleutils/hubble_math.go b/plugin/evm/orderbook/hubbleutils/hubble_math.go index 5bf10d7d7f..ce3f4d9ffc 100644 --- a/plugin/evm/orderbook/hubbleutils/hubble_math.go +++ b/plugin/evm/orderbook/hubbleutils/hubble_math.go @@ -2,6 +2,8 @@ package hubbleutils import ( "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" ) var ( @@ -65,3 +67,19 @@ func Scale(a *big.Int, decimals uint8) *big.Int { func Unscale(a *big.Int, decimals uint8) *big.Int { return Div(a, new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals)), nil)) } + +func ECRecover(data, sig hexutil.Bytes) (common.Address, error) { + if len(sig) != crypto.SignatureLength { + return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength) + } + if sig[crypto.RecoveryIDOffset] != 27 && sig[crypto.RecoveryIDOffset] != 28 { + return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)") + } + sig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1 + + rpk, err := crypto.SigToPub(accounts.TextHash(data), sig) + if err != nil { + return common.Address{}, err + } + return crypto.PubkeyToAddress(*rpk), nil +} diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index 9def549b3f..74b3c5d397 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -84,6 +84,7 @@ type OrderType uint8 const ( Limit OrderType = iota IOC + Signed ) func (o OrderType) String() string { @@ -246,6 +247,11 @@ type LimitOrderDatabase interface { UpdateLastPremiumFraction(market Market, trader common.Address, lastPremiumFraction *big.Int, cumlastPremiumFraction *big.Int) GetOrderById(orderId common.Hash) *Order GetTraderInfo(trader common.Address) *Trader + GetOrderValidationFields( + orderId common.Hash, + trader common.Address, + marketId int, + ) OrderValidationFields } type Snapshot struct { @@ -1171,3 +1177,26 @@ func getOrderIdx(orders []*Order, orderId common.Hash) int { } return -1 } + +type OrderValidationFields struct { + Exists bool + PosSize *big.Int + AsksHead *big.Int + BidsHead *big.Int +} + +func (db *InMemoryDatabase) GetOrderValidationFields( + orderId common.Hash, + trader common.Address, + marketId int, +) OrderValidationFields { + fields := OrderValidationFields{ + PosSize: new(big.Int).Set(db.TraderMap[trader].Positions[marketId].Size), + AsksHead: db.ShortOrders[marketId][0].Price, + BidsHead: db.LongOrders[marketId][0].Price, + } + if db.Orders[orderId] != nil { + fields.Exists = true + } + return fields +} diff --git a/plugin/evm/orderbook/order_types.go b/plugin/evm/orderbook/order_types.go index aaeb9d7f79..f18daf59ef 100644 --- a/plugin/evm/orderbook/order_types.go +++ b/plugin/evm/orderbook/order_types.go @@ -41,6 +41,14 @@ type IOCOrder struct { ExpireAt *big.Int `json:"expireAt"` } +// IOCOrder type is copy of IOCOrder struct defined in Orderbook contract +type SignedOrder struct { + LimitOrder + OrderType uint8 `json:"orderType"` + ExpireAt *big.Int `json:"expireAt"` + Sig []byte `json:"sig"` +} + // LimitOrder func (order *LimitOrder) EncodeToABIWithoutType() ([]byte, error) { limitOrderType, err := getOrderType("limit") @@ -116,11 +124,11 @@ func (order *IOCOrder) EncodeToABIWithoutType() ([]byte, error) { if err != nil { return nil, err } - encodedIOCOrder, err := abi.Arguments{{Type: iocOrderType}}.Pack(order) + encodedOrder, err := abi.Arguments{{Type: iocOrderType}}.Pack(order) if err != nil { return nil, err } - return encodedIOCOrder, nil + return encodedOrder, nil } func (order *IOCOrder) EncodeToABI() ([]byte, error) { @@ -128,7 +136,7 @@ func (order *IOCOrder) EncodeToABI() ([]byte, error) { if err != nil { return nil, fmt.Errorf("failed getting abi type: %w", err) } - encodedIOCOrder, err := abi.Arguments{{Type: iocOrderType}}.Pack(order) + encodedOrder, err := abi.Arguments{{Type: iocOrderType}}.Pack(order) if err != nil { return nil, fmt.Errorf("limit order packing failed: %w", err) } @@ -136,7 +144,7 @@ func (order *IOCOrder) EncodeToABI() ([]byte, error) { orderType, _ := abi.NewType("uint8", "uint8", nil) orderBytesType, _ := abi.NewType("bytes", "bytes", nil) // 1 means ordertype = IOC/market order - encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(1), encodedIOCOrder) + encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(IOC), encodedOrder) if err != nil { return nil, fmt.Errorf("order encoding failed: %w", err) } @@ -184,6 +192,64 @@ func (order *IOCOrder) Hash() (hash common.Hash, err error) { return common.BytesToHash(crypto.Keccak256(data)), nil } +// ---------------------------------------------------------------------------- +// SignedOrder + +func (order *SignedOrder) EncodeToABIWithoutType() ([]byte, error) { + signedOrderType, err := getOrderType("signed") + if err != nil { + return nil, err + } + encodedOrder, err := abi.Arguments{{Type: signedOrderType}}.Pack(order) + if err != nil { + return nil, err + } + return encodedOrder, nil +} + +func (order *SignedOrder) EncodeToABI() ([]byte, error) { + encodedSignedOrder, err := order.EncodeToABIWithoutType() + if err != nil { + return nil, fmt.Errorf("failed getting abi type: %w", err) + } + + orderType, _ := abi.NewType("uint8", "uint8", nil) + orderBytesType, _ := abi.NewType("bytes", "bytes", nil) + // 1 means ordertype = IOC/market order + encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(Signed), encodedSignedOrder) + if err != nil { + return nil, fmt.Errorf("order encoding failed: %w", err) + } + + return encodedOrder, nil +} + +func (order *SignedOrder) DecodeFromRawOrder(rawOrder interface{}) { + marshalledOrder, _ := json.Marshal(rawOrder) + json.Unmarshal(marshalledOrder, &order) +} + +// func (order *SignedOrder) Map() map[string]interface{} { +// return map[string]interface{}{ +// "ammIndex": order.AmmIndex, +// "trader": order.Trader, +// "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), +// "price": utils.BigIntToFloat(order.Price, 6), +// "reduceOnly": order.ReduceOnly, +// "salt": order.Salt, +// "orderType": order.OrderType, +// "expireAt": order.ExpireAt, +// } +// } + +func (order *SignedOrder) Hash() (hash common.Hash, err error) { + data, err := order.EncodeToABIWithoutType() + if err != nil { + return common.Hash{}, err + } + return common.BytesToHash(crypto.Keccak256(data)), nil +} + // ---------------------------------------------------------------------------- // Helper functions type DecodeStep struct { @@ -228,5 +294,18 @@ func getOrderType(orderType string) (abi.Type, error) { {Name: "reduceOnly", Type: "bool"}, }) } + if orderType == "signed" { + return abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + {Name: "orderType", Type: "uint8"}, + {Name: "expireAt", Type: "uint256"}, + {Name: "ammIndex", Type: "uint256"}, + {Name: "trader", Type: "address"}, + {Name: "baseAssetQuantity", Type: "int256"}, + {Name: "price", Type: "uint256"}, + {Name: "salt", Type: "uint256"}, + {Name: "reduceOnly", Type: "bool"}, + {Name: "postOnly", Type: "bool"}, + }) + } return abi.Type{}, fmt.Errorf("invalid order type") } diff --git a/plugin/evm/orderbook/trading_apis.go b/plugin/evm/orderbook/trading_apis.go index c07d62b040..62b8cb10fa 100644 --- a/plugin/evm/orderbook/trading_apis.go +++ b/plugin/evm/orderbook/trading_apis.go @@ -5,12 +5,14 @@ package orderbook import ( "context" + "errors" "fmt" "math/big" "strings" "time" "github.com/ava-labs/subnet-evm/eth" + hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" "github.com/ava-labs/subnet-evm/rpc" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" @@ -310,3 +312,155 @@ func (api *TradingAPI) StreamMarketTrades(ctx context.Context, market Market, bl return rpcSub, nil } + +type PlaceOrderResponse struct { + err error +} + +func (api *TradingAPI) PostOrder(ctx context.Context, order_ interface{}) PlaceOrderResponse { + stateDB, _, _ := api.backend.StateAndHeaderByNumber(ctx, rpc.BlockNumber(getCurrentBlockNumber(api.backend))) + + // func (api *TradingAPI) PostOrder(ctx context.Context, order_ hexutil.Bytes) PlaceOrderResponse { + order := order_.(*SignedOrder) + var response PlaceOrderResponse + + // @todo validations + if order.OrderType != uint8(Signed) { + response.err = fmt.Errorf("order type not supported") + return response + } + + if order.ExpireAt.Int64() <= time.Now().Unix() { + response.err = fmt.Errorf("order expired") + return response + } + + hubbleutils.EcRecover(order.Sig, order.Sig) + // @todo gossip order + + // add to db + limitOrder := Order{ + Id: orderId, + Market: Market(order.AmmIndex.Int64()), + PositionType: getPositionTypeBasedOnBaseAssetQuantity(order.BaseAssetQuantity), + Trader: getAddressFromTopicHash(event.Topics[1]), + BaseAssetQuantity: order.BaseAssetQuantity, + FilledBaseAssetQuantity: big.NewInt(0), + Price: order.Price, + RawOrder: &order, + Salt: order.Salt, + ReduceOnly: order.ReduceOnly, + BlockNumber: big.NewInt(int64(event.BlockNumber)), + OrderType: Limit, + } + orderHash, err := api.db.Add(&limitOrder) + if err != nil { + return common.Hash{}, err + } + + return orderHash, nil +} + +var ( + ErrTwoOrders = errors.New("need 2 orders") + ErrInvalidFillAmount = errors.New("invalid fillAmount") + ErrNotLongOrder = errors.New("not long") + ErrNotShortOrder = errors.New("not short") + ErrNotSameAMM = errors.New("OB_orders_for_different_amms") + ErrNoMatch = errors.New("OB_orders_do_not_match") + ErrNotMultiple = errors.New("not multiple") + + ErrInvalidOrder = errors.New("invalid order") + ErrNotIOCOrder = errors.New("not_ioc_order") + ErrInvalidPrice = errors.New("invalid price") + ErrPricePrecision = errors.New("invalid price precision") + ErrInvalidMarket = errors.New("invalid market") + ErrCancelledOrder = errors.New("cancelled order") + ErrFilledOrder = errors.New("filled order") + ErrOrderAlreadyExists = errors.New("order already exists") + ErrTooLow = errors.New("long price below lower bound") + ErrTooHigh = errors.New("short price above upper bound") + ErrOverFill = errors.New("overfill") + ErrReduceOnlyAmountExceeded = errors.New("not reducing pos") + ErrBaseAssetQuantityZero = errors.New("baseAssetQuantity is zero") + ErrReduceOnlyBaseAssetQuantityInvalid = errors.New("reduce only order must reduce position") + ErrNetReduceOnlyAmountExceeded = errors.New("net reduce only amount exceeded") + ErrStaleReduceOnlyOrders = errors.New("cancel stale reduce only orders") + ErrInsufficientMargin = errors.New("insufficient margin") + ErrCrossingMarket = errors.New("crossing market") + ErrIOCOrderExpired = errors.New("IOC order expired") + ErrOpenOrders = errors.New("open orders") + ErrOpenReduceOnlyOrders = errors.New("open reduce only orders") + ErrNoTradingAuthority = errors.New("no trading authority") + ErrNoReferrer = errors.New("no referrer") +) + +func (api *TradingAPI) ValidatePlaceLimitOrder(order *SignedOrder) error { + if order.Price.Sign() != 1 { + return ErrInvalidPrice + } + + if order.BaseAssetQuantity.Sign() == 0 { + return ErrBaseAssetQuantityZero + } + + orderHash, err := order.Hash() + if err != nil { + return err + } + + signer, err := hu.ECRecover(order.Sig, order.Sig) + trader := order.Trader + if trader != signer && !api.configService.IsTradingAuthority(stateDB, trader, signer) { + return ErrNoTradingAuthority + } + + markets := api.configService.GetActiveMarketsCount() + // assumes all markets are active and in sequential order + if order.AmmIndex.Int64() >= markets { + return ErrInvalidMarket + } + // ammAddress := getMarketAddressFromMarketID(order.AmmIndex.Int64(), stateDB) + + market := Market(order.AmmIndex.Int64()) + minSize := api.configService.getMinSizeRequirement(market) + if new(big.Int).Mod(order.BaseAssetQuantity, minSize).Sign() != 0 { + return ErrNotMultiple + } + + // order status + // should not already be submitted + fields := api.db.GetOrderValidationFields() + if fields.Exists { + return ErrOrderAlreadyExists + } + // should not be filled or cancelled + status := hu.OrderStatus(api.configService.GetSignedOrderStatus(orderHash)) + if status != hu.Invalid { + return ErrOrderAlreadyExists + } + + if order.PostOnly { + orderSide := hu.Side(hu.Long) + if order.BaseAssetQuantity.Sign() == -1 { + orderSide = hu.Side(hu.Short) + } + asksHead := fields.AsksHead + bidsHead := fields.BidsHead + if (orderSide == hu.Side(hu.Short) && bidsHead.Sign() != 0 && order.Price.Cmp(bidsHead) != 1) || (orderSide == hu.Side(hu.Long) && asksHead.Sign() != 0 && order.Price.Cmp(asksHead) != -1) { + return ErrCrossingMarket + } + } + + // @todo + // if !bibliophile.HasReferrer(order.Trader) { + // response.Err = ErrNoReferrer.Error() + // } + + // if hu.Mod(order.Price, bibliophile.GetPriceMultiplier(ammAddress)).Sign() != 0 { + // response.Err = ErrPricePrecision.Error() + // return + // } + + return response +} diff --git a/precompile/contracts/bibliophile/client.go b/precompile/contracts/bibliophile/client.go index 7787e1dad2..490805167a 100644 --- a/precompile/contracts/bibliophile/client.go +++ b/precompile/contracts/bibliophile/client.go @@ -21,16 +21,21 @@ type BibliophileClient interface { GetReduceOnlyAmount(trader common.Address, ammIndex *big.Int) *big.Int IsTradingAuthority(trader, senderOrSigner common.Address) bool IsValidator(senderOrSigner common.Address) bool + // Limit Order GetBlockPlaced(orderHash [32]byte) *big.Int GetOrderFilledAmount(orderHash [32]byte) *big.Int GetOrderStatus(orderHash [32]byte) int64 + // IOC Order IOC_GetBlockPlaced(orderHash [32]byte) *big.Int IOC_GetOrderFilledAmount(orderHash [32]byte) *big.Int IOC_GetOrderStatus(orderHash [32]byte) int64 IOC_GetExpirationCap() *big.Int + // Signed Order + GetSignedOrderStatus(orderHash [32]byte) int64 + // AMM GetMinSizeRequirement(marketId int64) *big.Int GetLastPrice(ammAddress common.Address) *big.Int From 4be6b0d40d92c12979c06fb5bac45117355ac8b7 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:50:49 +0000 Subject: [PATCH 02/12] start modifying precompile --- plugin/evm/orderbook/order_types.go | 14 ++++++ plugin/evm/orderbook/trading_apis.go | 18 +++++++- precompile/contracts/bibliophile/client.go | 1 + .../contracts/jurorv2/matching_validation.go | 46 +++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/plugin/evm/orderbook/order_types.go b/plugin/evm/orderbook/order_types.go index f18daf59ef..1d34763296 100644 --- a/plugin/evm/orderbook/order_types.go +++ b/plugin/evm/orderbook/order_types.go @@ -242,6 +242,20 @@ func (order *SignedOrder) DecodeFromRawOrder(rawOrder interface{}) { // } // } +func DecodeSignedOrder(encodedOrder []byte) (*SignedOrder, error) { + orderType, err := getOrderType("signed") + if err != nil { + return nil, fmt.Errorf("failed getting abi type: %w", err) + } + order, err := abi.Arguments{{Type: orderType}}.Unpack(encodedOrder) + if err != nil { + return nil, err + } + signedOrder := &SignedOrder{} + signedOrder.DecodeFromRawOrder(order[0]) + return signedOrder, nil +} + func (order *SignedOrder) Hash() (hash common.Hash, err error) { data, err := order.EncodeToABIWithoutType() if err != nil { diff --git a/plugin/evm/orderbook/trading_apis.go b/plugin/evm/orderbook/trading_apis.go index 62b8cb10fa..b8228a7772 100644 --- a/plugin/evm/orderbook/trading_apis.go +++ b/plugin/evm/orderbook/trading_apis.go @@ -395,7 +395,23 @@ var ( ErrNoReferrer = errors.New("no referrer") ) -func (api *TradingAPI) ValidatePlaceLimitOrder(order *SignedOrder) error { +// Place Order Checks +// 1. price >= 0 +// 2. signer is valid trading authority +// 3. market is valid +// 4. baseAssetQuantity is not 0 and multiple of minSize +// 5. order is not already placed, filled or cancelled +// 6. reduce only amount check OR margin availablity check (not in state, simply compared to other active orders) +// 7. post only order shouldn't cross the market +// 8. HasReferrer +// 9. price precision check +// +// Matching Order Checks +// Since all the above checks are in memory and not guaranteed by consensus, we need to perform them at the time of matching too. In addition: +// (note) 6. Not required in the precompile +// 10. Order is not being overfilled +// 11. Not both post only orders are being matched +func (api *TradingAPI) ValidatePlaceSignedOrder(order *SignedOrder) error { if order.Price.Sign() != 1 { return ErrInvalidPrice } diff --git a/precompile/contracts/bibliophile/client.go b/precompile/contracts/bibliophile/client.go index 490805167a..d3b882a530 100644 --- a/precompile/contracts/bibliophile/client.go +++ b/precompile/contracts/bibliophile/client.go @@ -34,6 +34,7 @@ type BibliophileClient interface { IOC_GetExpirationCap() *big.Int // Signed Order + GetSignedOrderFilledAmount(orderHash [32]byte) *big.Int GetSignedOrderStatus(orderHash [32]byte) int64 // AMM diff --git a/precompile/contracts/jurorv2/matching_validation.go b/precompile/contracts/jurorv2/matching_validation.go index 8b3e9d1890..dc8c30ecf6 100644 --- a/precompile/contracts/jurorv2/matching_validation.go +++ b/precompile/contracts/jurorv2/matching_validation.go @@ -46,6 +46,7 @@ var ( ErrNotShortOrder = errors.New("not short") ErrNotSameAMM = errors.New("OB_orders_for_different_amms") ErrNoMatch = errors.New("OB_orders_do_not_match") + ErrBothPostOnly = errors.New("both orders are post only") ErrNotMultiple = errors.New("not multiple") ErrInvalidOrder = errors.New("invalid order") @@ -119,6 +120,10 @@ func ValidateOrdersAndDetermineFillPrice(bibliophile b.BibliophileClient, inputS return getValidateOrdersAndDetermineFillPriceErrorOutput(ErrNoMatch, Generic, common.Hash{}) } + if m0.PostOnly && m1.PostOnly { + return getValidateOrdersAndDetermineFillPriceErrorOutput(ErrBothPostOnly, Generic, common.Hash{}) + } + minSize := bibliophile.GetMinSizeRequirement(m0.AmmIndex.Int64()) if new(big.Int).Mod(inputStruct.FillAmount, minSize).Cmp(big.NewInt(0)) != 0 { return getValidateOrdersAndDetermineFillPriceErrorOutput(ErrNotMultiple, Generic, common.Hash{}) @@ -314,6 +319,13 @@ func validateOrder(bibliophile b.BibliophileClient, orderType ob.OrderType, enco } return validateExecuteIOCOrder(bibliophile, order, side, fillAmount) } + if orderType == ob.Signed { + order, err := ob.DecodeSignedOrder(encodedOrder) + if err != nil { + return nil, err + } + return validateExecuteSignedOrder(bibliophile, order, side, fillAmount) + } return nil, errors.New("invalid order type") } @@ -363,6 +375,40 @@ func validateExecuteIOCOrder(bibliophile b.BibliophileClient, order *ob.IOCOrder }, nil } +func validateExecuteSignedOrder(bibliophile b.BibliophileClient, order *ob.SignedOrder, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { + orderHash, err := order.Hash() + if err != nil { + return nil, err + } + if ob.OrderType(order.OrderType) != ob.Signed { + return &Metadata{OrderHash: orderHash}, errors.New("not signed order") + } + if order.ExpireAt.Uint64() < bibliophile.GetTimeStamp() { + return &Metadata{OrderHash: orderHash}, errors.New("order expired") + } + if !order.PostOnly { + return &Metadata{OrderHash: orderHash}, errors.New("not post only") + } + orderStatus := OrderStatus(bibliophile.GetSignedOrderStatus(orderHash)) + if orderStatus == Invalid { + // signed orders don't get placed in the contract, so we consider them placed by default + orderStatus = Placed + } + if err := validateLimitOrderLike(bibliophile, &order.BaseOrder, bibliophile.GetSignedOrderFilledAmount(orderHash), OrderStatus(bibliophile.GetSignedOrderStatus(orderHash)), side, fillAmount); err != nil { + return &Metadata{OrderHash: orderHash}, err + } + return &Metadata{ + AmmIndex: order.AmmIndex, + Trader: order.Trader, + BaseAssetQuantity: order.BaseAssetQuantity, + BlockPlaced: big.NewInt(0), // will always be treated as a maker order + Price: order.Price, + OrderHash: orderHash, + OrderType: ob.Signed, + PostOnly: true, + }, nil +} + func validateLimitOrderLike(bibliophile b.BibliophileClient, order *ob.BaseOrder, filledAmount *big.Int, status OrderStatus, side Side, fillAmount *big.Int) error { if status != Placed { return ErrInvalidOrder From e8d1919517f62073365e06ac45b46f7448b23d7d Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Tue, 12 Dec 2023 19:50:10 +0000 Subject: [PATCH 03/12] all validations! --- plugin/evm/orderbook/config_service.go | 16 +- .../orderbook/hubbleutils/data_structures.go | 318 +++++++++++++++++- .../evm/orderbook/hubbleutils/hubble_math.go | 4 + .../evm/orderbook/hubbleutils/validations.go | 105 ++++++ plugin/evm/orderbook/memory_database.go | 14 +- plugin/evm/orderbook/order_types.go | 316 +---------------- plugin/evm/orderbook/trading_apis.go | 183 +++------- precompile/contracts/bibliophile/amm.go | 4 + precompile/contracts/bibliophile/client.go | 13 + .../bibliophile/signed_order_book.go | 32 ++ precompile/contracts/juror/ioc_orders.go | 2 +- precompile/contracts/juror/limit_orders.go | 2 +- .../contracts/juror/matching_validation.go | 52 +-- .../contracts/jurorv2/matching_validation.go | 56 +-- 14 files changed, 601 insertions(+), 516 deletions(-) create mode 100644 plugin/evm/orderbook/hubbleutils/validations.go create mode 100644 precompile/contracts/bibliophile/signed_order_book.go diff --git a/plugin/evm/orderbook/config_service.go b/plugin/evm/orderbook/config_service.go index 75ca4626ce..0e85abd9dc 100644 --- a/plugin/evm/orderbook/config_service.go +++ b/plugin/evm/orderbook/config_service.go @@ -16,7 +16,7 @@ type IConfigService interface { getMinAllowableMargin() *big.Int getMaintenanceMargin() *big.Int getMinSizeRequirement(market Market) *big.Int - GetActiveMarkets() []Market + GetPriceMultiplier(market Market) *big.Int GetActiveMarketsCount() int64 GetUnderlyingPrices() []*big.Int GetMidPrices() []*big.Int @@ -26,7 +26,8 @@ type IConfigService interface { GetAcceptableBounds(market Market) (*big.Int, *big.Int) GetAcceptableBoundsForLiquidation(market Market) (*big.Int, *big.Int) - GetSignedOrderStatus(orderHash [32]byte) int64 + GetSignedOrderStatus(orderHash common.Hash) int64 + IsTradingAuthority(trader, signer common.Address) bool } type ConfigService struct { @@ -67,6 +68,10 @@ func (cs *ConfigService) getMinSizeRequirement(market Market) *big.Int { return bibliophile.GetMinSizeRequirement(cs.getStateAtCurrentBlock(), int64(market)) } +func (cs *ConfigService) GetPriceMultiplier(market Market) *big.Int { + return bibliophile.GetMultiplier(cs.getStateAtCurrentBlock(), int64(market)) +} + func (cs *ConfigService) getStateAtCurrentBlock() *state.StateDB { stateDB, _ := cs.blockChain.StateAt(cs.blockChain.CurrentBlock().Root) return stateDB @@ -107,6 +112,9 @@ func (cs *ConfigService) GetCumulativePremiumFraction(market Market) *big.Int { } func (cs *ConfigService) GetSignedOrderStatus(orderHash common.Hash) int64 { - return 0 // @todo - // return bibliophile.GetSignedOrderStatus(cs.getStateAtCurrentBlock(), orderHash) + return bibliophile.GetSignedOrderStatus(cs.getStateAtCurrentBlock(), orderHash) +} + +func (cs *ConfigService) IsTradingAuthority(trader, signer common.Address) bool { + return bibliophile.IsTradingAuthority(cs.getStateAtCurrentBlock(), trader, signer) } diff --git a/plugin/evm/orderbook/hubbleutils/data_structures.go b/plugin/evm/orderbook/hubbleutils/data_structures.go index b48e057bef..f6c41a949e 100644 --- a/plugin/evm/orderbook/hubbleutils/data_structures.go +++ b/plugin/evm/orderbook/hubbleutils/data_structures.go @@ -1,10 +1,14 @@ package hubbleutils import ( - // "encoding/json" + "encoding/json" + "fmt" "math/big" + "github.com/ava-labs/subnet-evm/accounts/abi" + "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" ) type MarginMode = uint8 @@ -54,3 +58,315 @@ const ( Filled Cancelled ) + +type OrderType uint8 + +const ( + Limit OrderType = iota + IOC + Signed +) + +func (o OrderType) String() string { + return [...]string{"limit", "ioc"}[o] +} + +type BaseOrder struct { + AmmIndex *big.Int `json:"ammIndex"` + Trader common.Address `json:"trader"` + BaseAssetQuantity *big.Int `json:"baseAssetQuantity"` + Price *big.Int `json:"price"` + Salt *big.Int `json:"salt"` + ReduceOnly bool `json:"reduceOnly"` +} + +// LimitOrder type is copy of Order struct defined in LimitOrderbook contract +type LimitOrder struct { + BaseOrder + PostOnly bool `json:"postOnly"` +} + +// IOCOrder type is copy of IOCOrder struct defined in Orderbook contract +type IOCOrder struct { + BaseOrder + OrderType uint8 `json:"orderType"` + ExpireAt *big.Int `json:"expireAt"` +} + +// IOCOrder type is copy of IOCOrder struct defined in Orderbook contract +type SignedOrder struct { + LimitOrder + OrderType uint8 `json:"orderType"` + ExpireAt *big.Int `json:"expireAt"` + Sig []byte `json:"sig"` +} + +// LimitOrder +func (order *LimitOrder) EncodeToABIWithoutType() ([]byte, error) { + limitOrderType, err := getOrderType("limit") + if err != nil { + return nil, err + } + encodedLimitOrder, err := abi.Arguments{{Type: limitOrderType}}.Pack(order) + if err != nil { + return nil, err + } + return encodedLimitOrder, nil +} + +func (order *LimitOrder) EncodeToABI() ([]byte, error) { + encodedLimitOrder, err := order.EncodeToABIWithoutType() + if err != nil { + return nil, fmt.Errorf("limit order packing failed: %w", err) + } + orderType, _ := abi.NewType("uint8", "uint8", nil) + orderBytesType, _ := abi.NewType("bytes", "bytes", nil) + // 0 means ordertype = limit order + encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(0) /* Limit Order */, encodedLimitOrder) + if err != nil { + return nil, fmt.Errorf("order encoding failed: %w", err) + } + return encodedOrder, nil +} + +func (order *LimitOrder) DecodeFromRawOrder(rawOrder interface{}) { + marshalledOrder, _ := json.Marshal(rawOrder) + json.Unmarshal(marshalledOrder, &order) +} + +func (order *LimitOrder) Map() map[string]interface{} { + return map[string]interface{}{ + "ammIndex": order.AmmIndex, + "trader": order.Trader, + "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), + "price": utils.BigIntToFloat(order.Price, 6), + "reduceOnly": order.ReduceOnly, + "postOnly": order.PostOnly, + "salt": order.Salt, + } +} + +func DecodeLimitOrder(encodedOrder []byte) (*LimitOrder, error) { + limitOrderType, err := getOrderType("limit") + if err != nil { + return nil, fmt.Errorf("failed getting abi type: %w", err) + } + order, err := abi.Arguments{{Type: limitOrderType}}.Unpack(encodedOrder) + if err != nil { + return nil, err + } + limitOrder := &LimitOrder{} + limitOrder.DecodeFromRawOrder(order[0]) + return limitOrder, nil +} + +func (order *LimitOrder) Hash() (common.Hash, error) { + data, err := order.EncodeToABIWithoutType() + if err != nil { + return common.Hash{}, err + } + return common.BytesToHash(crypto.Keccak256(data)), nil +} + +// ---------------------------------------------------------------------------- +// IOCOrder + +func (order *IOCOrder) EncodeToABIWithoutType() ([]byte, error) { + iocOrderType, err := getOrderType("ioc") + if err != nil { + return nil, err + } + encodedOrder, err := abi.Arguments{{Type: iocOrderType}}.Pack(order) + if err != nil { + return nil, err + } + return encodedOrder, nil +} + +func (order *IOCOrder) EncodeToABI() ([]byte, error) { + encodedIOCOrder, err := order.EncodeToABIWithoutType() + if err != nil { + return nil, fmt.Errorf("limit order packing failed: %w", err) + } + + orderType, _ := abi.NewType("uint8", "uint8", nil) + orderBytesType, _ := abi.NewType("bytes", "bytes", nil) + // 1 means ordertype = IOC/market order + encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(IOC), encodedIOCOrder) + if err != nil { + return nil, fmt.Errorf("order encoding failed: %w", err) + } + return encodedOrder, nil +} + +func (order *IOCOrder) DecodeFromRawOrder(rawOrder interface{}) { + marshalledOrder, _ := json.Marshal(rawOrder) + json.Unmarshal(marshalledOrder, &order) +} + +func (order *IOCOrder) Map() map[string]interface{} { + return map[string]interface{}{ + "ammIndex": order.AmmIndex, + "trader": order.Trader, + "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), + "price": utils.BigIntToFloat(order.Price, 6), + "reduceOnly": order.ReduceOnly, + "salt": order.Salt, + "orderType": order.OrderType, + "expireAt": order.ExpireAt, + } +} + +func DecodeIOCOrder(encodedOrder []byte) (*IOCOrder, error) { + iocOrderType, err := getOrderType("ioc") + if err != nil { + return nil, fmt.Errorf("failed getting abi type: %w", err) + } + order, err := abi.Arguments{{Type: iocOrderType}}.Unpack(encodedOrder) + if err != nil { + return nil, err + } + iocOrder := &IOCOrder{} + iocOrder.DecodeFromRawOrder(order[0]) + return iocOrder, nil +} + +func (order *IOCOrder) Hash() (hash common.Hash, err error) { + data, err := order.EncodeToABIWithoutType() + if err != nil { + return common.Hash{}, err + } + return common.BytesToHash(crypto.Keccak256(data)), nil +} + +// ---------------------------------------------------------------------------- +// SignedOrder + +func (order *SignedOrder) EncodeToABIWithoutType() ([]byte, error) { + signedOrderType, err := getOrderType("signed") + if err != nil { + return nil, err + } + encodedOrder, err := abi.Arguments{{Type: signedOrderType}}.Pack(order) + if err != nil { + return nil, err + } + return encodedOrder, nil +} + +func (order *SignedOrder) EncodeToABI() ([]byte, error) { + encodedSignedOrder, err := order.EncodeToABIWithoutType() + if err != nil { + return nil, fmt.Errorf("failed getting abi type: %w", err) + } + + orderType, _ := abi.NewType("uint8", "uint8", nil) + orderBytesType, _ := abi.NewType("bytes", "bytes", nil) + // 1 means ordertype = IOC/market order + encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(Signed), encodedSignedOrder) + if err != nil { + return nil, fmt.Errorf("order encoding failed: %w", err) + } + + return encodedOrder, nil +} + +func (order *SignedOrder) DecodeFromRawOrder(rawOrder interface{}) { + marshalledOrder, _ := json.Marshal(rawOrder) + json.Unmarshal(marshalledOrder, &order) +} + +// func (order *SignedOrder) Map() map[string]interface{} { +// return map[string]interface{}{ +// "ammIndex": order.AmmIndex, +// "trader": order.Trader, +// "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), +// "price": utils.BigIntToFloat(order.Price, 6), +// "reduceOnly": order.ReduceOnly, +// "salt": order.Salt, +// "orderType": order.OrderType, +// "expireAt": order.ExpireAt, +// } +// } + +func DecodeSignedOrder(encodedOrder []byte) (*SignedOrder, error) { + orderType, err := getOrderType("signed") + if err != nil { + return nil, fmt.Errorf("failed getting abi type: %w", err) + } + order, err := abi.Arguments{{Type: orderType}}.Unpack(encodedOrder) + if err != nil { + return nil, err + } + signedOrder := &SignedOrder{} + signedOrder.DecodeFromRawOrder(order[0]) + return signedOrder, nil +} + +func (order *SignedOrder) Hash() (hash common.Hash, err error) { + data, err := order.EncodeToABIWithoutType() + if err != nil { + return common.Hash{}, err + } + return common.BytesToHash(crypto.Keccak256(data)), nil +} + +// ---------------------------------------------------------------------------- +// Helper functions +type DecodeStep struct { + OrderType OrderType + EncodedOrder []byte +} + +func DecodeTypeAndEncodedOrder(data []byte) (*DecodeStep, error) { + orderType, _ := abi.NewType("uint8", "uint8", nil) + orderBytesType, _ := abi.NewType("bytes", "bytes", nil) + decodedValues, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Unpack(data) + if err != nil { + return nil, err + } + return &DecodeStep{ + OrderType: OrderType(decodedValues[0].(uint8)), + EncodedOrder: decodedValues[1].([]byte), + }, nil +} + +func getOrderType(orderType string) (abi.Type, error) { + if orderType == "limit" { + return abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + {Name: "ammIndex", Type: "uint256"}, + {Name: "trader", Type: "address"}, + {Name: "baseAssetQuantity", Type: "int256"}, + {Name: "price", Type: "uint256"}, + {Name: "salt", Type: "uint256"}, + {Name: "reduceOnly", Type: "bool"}, + {Name: "postOnly", Type: "bool"}, + }) + } + if orderType == "ioc" { + return abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + {Name: "orderType", Type: "uint8"}, + {Name: "expireAt", Type: "uint256"}, + {Name: "ammIndex", Type: "uint256"}, + {Name: "trader", Type: "address"}, + {Name: "baseAssetQuantity", Type: "int256"}, + {Name: "price", Type: "uint256"}, + {Name: "salt", Type: "uint256"}, + {Name: "reduceOnly", Type: "bool"}, + }) + } + if orderType == "signed" { + return abi.NewType("tuple", "", []abi.ArgumentMarshaling{ + {Name: "orderType", Type: "uint8"}, + {Name: "expireAt", Type: "uint256"}, + {Name: "ammIndex", Type: "uint256"}, + {Name: "trader", Type: "address"}, + {Name: "baseAssetQuantity", Type: "int256"}, + {Name: "price", Type: "uint256"}, + {Name: "salt", Type: "uint256"}, + {Name: "reduceOnly", Type: "bool"}, + {Name: "postOnly", Type: "bool"}, + }) + } + return abi.Type{}, fmt.Errorf("invalid order type") +} diff --git a/plugin/evm/orderbook/hubbleutils/hubble_math.go b/plugin/evm/orderbook/hubbleutils/hubble_math.go index ce3f4d9ffc..49c6472969 100644 --- a/plugin/evm/orderbook/hubbleutils/hubble_math.go +++ b/plugin/evm/orderbook/hubbleutils/hubble_math.go @@ -1,9 +1,13 @@ package hubbleutils import ( + "fmt" "math/big" + "github.com/ava-labs/subnet-evm/accounts" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" ) var ( diff --git a/plugin/evm/orderbook/hubbleutils/validations.go b/plugin/evm/orderbook/hubbleutils/validations.go new file mode 100644 index 0000000000..646fcaafa8 --- /dev/null +++ b/plugin/evm/orderbook/hubbleutils/validations.go @@ -0,0 +1,105 @@ +package hubbleutils + +import ( + "errors" + "math/big" + + "github.com/ethereum/go-ethereum/common" +) + +type SignedOrderValidationFields struct { + Now uint64 + ActiveMarketsCount int64 + MinSize *big.Int + PriceMultiplier *big.Int + Status int64 +} + +var ( + ErrNotSignedOrder = errors.New("not signed order") + ErrInvalidPrice = errors.New("invalid price") + ErrOrderExpired = errors.New("order expired") + ErrBaseAssetQuantityZero = errors.New("baseAssetQuantity is zero") + ErrNotPostOnly = errors.New("not post only") + ErrInvalidMarket = errors.New("invalid market") + ErrNotMultiple = errors.New("not multiple") + ErrPricePrecision = errors.New("invalid price precision") + ErrOrderAlreadyExists = errors.New("order already exists") + ErrCrossingMarket = errors.New("crossing market") + ErrNoTradingAuthority = errors.New("no trading authority") +) + +// Common Checks +// 1. orderType == Signed +// 2. Not expired +// 3. order should be post only +// 4. baseAssetQuantity is not 0 and multiple of minSize +// 5. price > 0 and price precision check +// 6. signer is valid trading authority +// 7. market is valid +// 8. order is not already filled or cancelled + +// Place Order Checks +// P1. Order is not already in memdb (placed) +// P2. Margin is available for non-reduce only orders +// P3. Sum of all reduce only orders should not exceed the total position size (not in state, simply compared to other active orders) and/or opposite direction validations +// P4. Post only order shouldn't cross the market +// P5. HasReferrer + +// Matching Order Checks +// M1. order is not being overfilled +// M2. reduce only order should reduce the position size +// M3. HasReferrer +// M4. Not both post only orders are being matched + +func ValidateSignedOrder(order *SignedOrder, fields SignedOrderValidationFields) (trader, signer common.Address, err error) { + if OrderType(order.OrderType) != Signed { // 1. + err = ErrNotSignedOrder + return trader, signer, err + } + + if order.ExpireAt.Uint64() < fields.Now { // 2. + err = ErrOrderExpired + return trader, signer, err + } + + if !order.PostOnly { // 3. + err = ErrNotPostOnly + return trader, signer, err + } + + // 4. + if order.BaseAssetQuantity.Sign() == 0 { + err = ErrBaseAssetQuantityZero + return trader, signer, err + } + if new(big.Int).Mod(order.BaseAssetQuantity, fields.MinSize).Sign() != 0 { + err = ErrNotMultiple + return trader, signer, err + } + + if order.Price.Sign() != 1 { // 5. + err = ErrInvalidPrice + return trader, signer, err + } + if Mod(order.Price, fields.PriceMultiplier).Sign() != 0 { + err = ErrPricePrecision + return trader, signer, err + } + + // 6. caller will perform the check + signer, err = ECRecover(order.Sig, order.Sig) // @todo construct data + trader = order.Trader + + // assumes all markets are active and in sequential order + if order.AmmIndex.Int64() >= fields.ActiveMarketsCount { // 7. + err = ErrInvalidMarket + return trader, signer, err + } + + if OrderStatus(fields.Status) != Invalid { // 8. + err = ErrOrderAlreadyExists + return trader, signer, err + } + return trader, signer, nil +} diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index 74b3c5d397..5d0c4765c1 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -79,17 +79,17 @@ const ( Execution_Failed ) -type OrderType uint8 +type OrderType = hu.OrderType const ( - Limit OrderType = iota - IOC - Signed + Limit = hu.Limit + IOC = hu.IOC + Signed = hu.Signed ) -func (o OrderType) String() string { - return [...]string{"limit", "ioc"}[o] -} +// func (o OrderType) String() string { +// return [...]string{"limit", "ioc"}[o] +// } type Lifecycle struct { BlockNumber uint64 diff --git a/plugin/evm/orderbook/order_types.go b/plugin/evm/orderbook/order_types.go index 1d34763296..6e05da6392 100644 --- a/plugin/evm/orderbook/order_types.go +++ b/plugin/evm/orderbook/order_types.go @@ -1,14 +1,7 @@ package orderbook import ( - "encoding/json" - "fmt" - "math/big" - - "github.com/ava-labs/subnet-evm/accounts/abi" - "github.com/ava-labs/subnet-evm/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" + hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" ) type ContractOrder interface { @@ -18,308 +11,5 @@ type ContractOrder interface { Map() map[string]interface{} } -// BaseOrder is the set of common fields among the order types -type BaseOrder struct { - AmmIndex *big.Int `json:"ammIndex"` - Trader common.Address `json:"trader"` - BaseAssetQuantity *big.Int `json:"baseAssetQuantity"` - Price *big.Int `json:"price"` - Salt *big.Int `json:"salt"` - ReduceOnly bool `json:"reduceOnly"` -} - -// LimitOrder type is copy of Order struct defined in LimitOrderbook contract -type LimitOrder struct { - BaseOrder - PostOnly bool `json:"postOnly"` -} - -// IOCOrder type is copy of IOCOrder struct defined in Orderbook contract -type IOCOrder struct { - BaseOrder - OrderType uint8 `json:"orderType"` - ExpireAt *big.Int `json:"expireAt"` -} - -// IOCOrder type is copy of IOCOrder struct defined in Orderbook contract -type SignedOrder struct { - LimitOrder - OrderType uint8 `json:"orderType"` - ExpireAt *big.Int `json:"expireAt"` - Sig []byte `json:"sig"` -} - -// LimitOrder -func (order *LimitOrder) EncodeToABIWithoutType() ([]byte, error) { - limitOrderType, err := getOrderType("limit") - if err != nil { - return nil, err - } - encodedLimitOrder, err := abi.Arguments{{Type: limitOrderType}}.Pack(order) - if err != nil { - return nil, err - } - return encodedLimitOrder, nil -} - -func (order *LimitOrder) EncodeToABI() ([]byte, error) { - encodedLimitOrder, err := order.EncodeToABIWithoutType() - if err != nil { - return nil, fmt.Errorf("limit order packing failed: %w", err) - } - orderType, _ := abi.NewType("uint8", "uint8", nil) - orderBytesType, _ := abi.NewType("bytes", "bytes", nil) - // 0 means ordertype = limit order - encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(0) /* Limit Order */, encodedLimitOrder) - if err != nil { - return nil, fmt.Errorf("order encoding failed: %w", err) - } - return encodedOrder, nil -} - -func (order *LimitOrder) DecodeFromRawOrder(rawOrder interface{}) { - marshalledOrder, _ := json.Marshal(rawOrder) - json.Unmarshal(marshalledOrder, &order) -} - -func (order *LimitOrder) Map() map[string]interface{} { - return map[string]interface{}{ - "ammIndex": order.AmmIndex, - "trader": order.Trader, - "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), - "price": utils.BigIntToFloat(order.Price, 6), - "reduceOnly": order.ReduceOnly, - "postOnly": order.PostOnly, - "salt": order.Salt, - } -} - -func DecodeLimitOrder(encodedOrder []byte) (*LimitOrder, error) { - limitOrderType, err := getOrderType("limit") - if err != nil { - return nil, fmt.Errorf("failed getting abi type: %w", err) - } - order, err := abi.Arguments{{Type: limitOrderType}}.Unpack(encodedOrder) - if err != nil { - return nil, err - } - limitOrder := &LimitOrder{} - limitOrder.DecodeFromRawOrder(order[0]) - return limitOrder, nil -} - -func (order *LimitOrder) Hash() (common.Hash, error) { - data, err := order.EncodeToABIWithoutType() - if err != nil { - return common.Hash{}, err - } - return common.BytesToHash(crypto.Keccak256(data)), nil -} - -// ---------------------------------------------------------------------------- -// IOCOrder - -func (order *IOCOrder) EncodeToABIWithoutType() ([]byte, error) { - iocOrderType, err := getOrderType("ioc") - if err != nil { - return nil, err - } - encodedOrder, err := abi.Arguments{{Type: iocOrderType}}.Pack(order) - if err != nil { - return nil, err - } - return encodedOrder, nil -} - -func (order *IOCOrder) EncodeToABI() ([]byte, error) { - iocOrderType, err := getOrderType("ioc") - if err != nil { - return nil, fmt.Errorf("failed getting abi type: %w", err) - } - encodedOrder, err := abi.Arguments{{Type: iocOrderType}}.Pack(order) - if err != nil { - return nil, fmt.Errorf("limit order packing failed: %w", err) - } - - orderType, _ := abi.NewType("uint8", "uint8", nil) - orderBytesType, _ := abi.NewType("bytes", "bytes", nil) - // 1 means ordertype = IOC/market order - encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(IOC), encodedOrder) - if err != nil { - return nil, fmt.Errorf("order encoding failed: %w", err) - } - - return encodedOrder, nil -} - -func (order *IOCOrder) DecodeFromRawOrder(rawOrder interface{}) { - marshalledOrder, _ := json.Marshal(rawOrder) - json.Unmarshal(marshalledOrder, &order) -} - -func (order *IOCOrder) Map() map[string]interface{} { - return map[string]interface{}{ - "ammIndex": order.AmmIndex, - "trader": order.Trader, - "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), - "price": utils.BigIntToFloat(order.Price, 6), - "reduceOnly": order.ReduceOnly, - "salt": order.Salt, - "orderType": order.OrderType, - "expireAt": order.ExpireAt, - } -} - -func DecodeIOCOrder(encodedOrder []byte) (*IOCOrder, error) { - iocOrderType, err := getOrderType("ioc") - if err != nil { - return nil, fmt.Errorf("failed getting abi type: %w", err) - } - order, err := abi.Arguments{{Type: iocOrderType}}.Unpack(encodedOrder) - if err != nil { - return nil, err - } - iocOrder := &IOCOrder{} - iocOrder.DecodeFromRawOrder(order[0]) - return iocOrder, nil -} - -func (order *IOCOrder) Hash() (hash common.Hash, err error) { - data, err := order.EncodeToABIWithoutType() - if err != nil { - return common.Hash{}, err - } - return common.BytesToHash(crypto.Keccak256(data)), nil -} - -// ---------------------------------------------------------------------------- -// SignedOrder - -func (order *SignedOrder) EncodeToABIWithoutType() ([]byte, error) { - signedOrderType, err := getOrderType("signed") - if err != nil { - return nil, err - } - encodedOrder, err := abi.Arguments{{Type: signedOrderType}}.Pack(order) - if err != nil { - return nil, err - } - return encodedOrder, nil -} - -func (order *SignedOrder) EncodeToABI() ([]byte, error) { - encodedSignedOrder, err := order.EncodeToABIWithoutType() - if err != nil { - return nil, fmt.Errorf("failed getting abi type: %w", err) - } - - orderType, _ := abi.NewType("uint8", "uint8", nil) - orderBytesType, _ := abi.NewType("bytes", "bytes", nil) - // 1 means ordertype = IOC/market order - encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(Signed), encodedSignedOrder) - if err != nil { - return nil, fmt.Errorf("order encoding failed: %w", err) - } - - return encodedOrder, nil -} - -func (order *SignedOrder) DecodeFromRawOrder(rawOrder interface{}) { - marshalledOrder, _ := json.Marshal(rawOrder) - json.Unmarshal(marshalledOrder, &order) -} - -// func (order *SignedOrder) Map() map[string]interface{} { -// return map[string]interface{}{ -// "ammIndex": order.AmmIndex, -// "trader": order.Trader, -// "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), -// "price": utils.BigIntToFloat(order.Price, 6), -// "reduceOnly": order.ReduceOnly, -// "salt": order.Salt, -// "orderType": order.OrderType, -// "expireAt": order.ExpireAt, -// } -// } - -func DecodeSignedOrder(encodedOrder []byte) (*SignedOrder, error) { - orderType, err := getOrderType("signed") - if err != nil { - return nil, fmt.Errorf("failed getting abi type: %w", err) - } - order, err := abi.Arguments{{Type: orderType}}.Unpack(encodedOrder) - if err != nil { - return nil, err - } - signedOrder := &SignedOrder{} - signedOrder.DecodeFromRawOrder(order[0]) - return signedOrder, nil -} - -func (order *SignedOrder) Hash() (hash common.Hash, err error) { - data, err := order.EncodeToABIWithoutType() - if err != nil { - return common.Hash{}, err - } - return common.BytesToHash(crypto.Keccak256(data)), nil -} - -// ---------------------------------------------------------------------------- -// Helper functions -type DecodeStep struct { - OrderType OrderType - EncodedOrder []byte -} - -func DecodeTypeAndEncodedOrder(data []byte) (*DecodeStep, error) { - orderType, _ := abi.NewType("uint8", "uint8", nil) - orderBytesType, _ := abi.NewType("bytes", "bytes", nil) - decodedValues, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Unpack(data) - if err != nil { - return nil, err - } - return &DecodeStep{ - OrderType: OrderType(decodedValues[0].(uint8)), - EncodedOrder: decodedValues[1].([]byte), - }, nil -} - -func getOrderType(orderType string) (abi.Type, error) { - if orderType == "limit" { - return abi.NewType("tuple", "", []abi.ArgumentMarshaling{ - {Name: "ammIndex", Type: "uint256"}, - {Name: "trader", Type: "address"}, - {Name: "baseAssetQuantity", Type: "int256"}, - {Name: "price", Type: "uint256"}, - {Name: "salt", Type: "uint256"}, - {Name: "reduceOnly", Type: "bool"}, - {Name: "postOnly", Type: "bool"}, - }) - } - if orderType == "ioc" { - return abi.NewType("tuple", "", []abi.ArgumentMarshaling{ - {Name: "orderType", Type: "uint8"}, - {Name: "expireAt", Type: "uint256"}, - {Name: "ammIndex", Type: "uint256"}, - {Name: "trader", Type: "address"}, - {Name: "baseAssetQuantity", Type: "int256"}, - {Name: "price", Type: "uint256"}, - {Name: "salt", Type: "uint256"}, - {Name: "reduceOnly", Type: "bool"}, - }) - } - if orderType == "signed" { - return abi.NewType("tuple", "", []abi.ArgumentMarshaling{ - {Name: "orderType", Type: "uint8"}, - {Name: "expireAt", Type: "uint256"}, - {Name: "ammIndex", Type: "uint256"}, - {Name: "trader", Type: "address"}, - {Name: "baseAssetQuantity", Type: "int256"}, - {Name: "price", Type: "uint256"}, - {Name: "salt", Type: "uint256"}, - {Name: "reduceOnly", Type: "bool"}, - {Name: "postOnly", Type: "bool"}, - }) - } - return abi.Type{}, fmt.Errorf("invalid order type") -} +type LimitOrder = hu.LimitOrder +type IOCOrder = hu.IOCOrder diff --git a/plugin/evm/orderbook/trading_apis.go b/plugin/evm/orderbook/trading_apis.go index b8228a7772..dcc41698d7 100644 --- a/plugin/evm/orderbook/trading_apis.go +++ b/plugin/evm/orderbook/trading_apis.go @@ -5,7 +5,6 @@ package orderbook import ( "context" - "errors" "fmt" "math/big" "strings" @@ -318,165 +317,61 @@ type PlaceOrderResponse struct { } func (api *TradingAPI) PostOrder(ctx context.Context, order_ interface{}) PlaceOrderResponse { - stateDB, _, _ := api.backend.StateAndHeaderByNumber(ctx, rpc.BlockNumber(getCurrentBlockNumber(api.backend))) - - // func (api *TradingAPI) PostOrder(ctx context.Context, order_ hexutil.Bytes) PlaceOrderResponse { - order := order_.(*SignedOrder) - var response PlaceOrderResponse - - // @todo validations - if order.OrderType != uint8(Signed) { - response.err = fmt.Errorf("order type not supported") - return response + order := order_.(*hu.SignedOrder) + marketId := int(order.AmmIndex.Int64()) + orderId, err := order.Hash() + if err != nil { + return PlaceOrderResponse{err: err} + } + trader, signer, err := hu.ValidateSignedOrder( + order, + hu.SignedOrderValidationFields{ + Now: uint64(time.Now().Unix()), + ActiveMarketsCount: api.configService.GetActiveMarketsCount(), + MinSize: api.configService.getMinSizeRequirement(marketId), + PriceMultiplier: api.configService.GetPriceMultiplier(marketId), + Status: api.configService.GetSignedOrderStatus(orderId), + }, + ) + + response := PlaceOrderResponse{} + if trader != signer && !api.configService.IsTradingAuthority(trader, signer) { + return PlaceOrderResponse{err: hu.ErrNoTradingAuthority} } - if order.ExpireAt.Int64() <= time.Now().Unix() { - response.err = fmt.Errorf("order expired") - return response + fields := api.db.GetOrderValidationFields(orderId, trader, marketId) + // @todo P1 - P3 + // P4. Post only order shouldn't cross the market + if order.PostOnly { + orderSide := hu.Side(hu.Long) + if order.BaseAssetQuantity.Sign() == -1 { + orderSide = hu.Side(hu.Short) + } + asksHead := fields.AsksHead + bidsHead := fields.BidsHead + if (orderSide == hu.Side(hu.Short) && bidsHead.Sign() != 0 && order.Price.Cmp(bidsHead) != 1) || (orderSide == hu.Side(hu.Long) && asksHead.Sign() != 0 && order.Price.Cmp(asksHead) != -1) { + return PlaceOrderResponse{err: hu.ErrCrossingMarket} + } } + // @todo P5 - hubbleutils.EcRecover(order.Sig, order.Sig) // @todo gossip order // add to db - limitOrder := Order{ + limitOrder := &Order{ Id: orderId, Market: Market(order.AmmIndex.Int64()), PositionType: getPositionTypeBasedOnBaseAssetQuantity(order.BaseAssetQuantity), - Trader: getAddressFromTopicHash(event.Topics[1]), + Trader: trader, BaseAssetQuantity: order.BaseAssetQuantity, FilledBaseAssetQuantity: big.NewInt(0), Price: order.Price, - RawOrder: &order, + RawOrder: order, Salt: order.Salt, ReduceOnly: order.ReduceOnly, - BlockNumber: big.NewInt(int64(event.BlockNumber)), + BlockNumber: big.NewInt(0), OrderType: Limit, } - orderHash, err := api.db.Add(&limitOrder) - if err != nil { - return common.Hash{}, err - } - - return orderHash, nil -} - -var ( - ErrTwoOrders = errors.New("need 2 orders") - ErrInvalidFillAmount = errors.New("invalid fillAmount") - ErrNotLongOrder = errors.New("not long") - ErrNotShortOrder = errors.New("not short") - ErrNotSameAMM = errors.New("OB_orders_for_different_amms") - ErrNoMatch = errors.New("OB_orders_do_not_match") - ErrNotMultiple = errors.New("not multiple") - - ErrInvalidOrder = errors.New("invalid order") - ErrNotIOCOrder = errors.New("not_ioc_order") - ErrInvalidPrice = errors.New("invalid price") - ErrPricePrecision = errors.New("invalid price precision") - ErrInvalidMarket = errors.New("invalid market") - ErrCancelledOrder = errors.New("cancelled order") - ErrFilledOrder = errors.New("filled order") - ErrOrderAlreadyExists = errors.New("order already exists") - ErrTooLow = errors.New("long price below lower bound") - ErrTooHigh = errors.New("short price above upper bound") - ErrOverFill = errors.New("overfill") - ErrReduceOnlyAmountExceeded = errors.New("not reducing pos") - ErrBaseAssetQuantityZero = errors.New("baseAssetQuantity is zero") - ErrReduceOnlyBaseAssetQuantityInvalid = errors.New("reduce only order must reduce position") - ErrNetReduceOnlyAmountExceeded = errors.New("net reduce only amount exceeded") - ErrStaleReduceOnlyOrders = errors.New("cancel stale reduce only orders") - ErrInsufficientMargin = errors.New("insufficient margin") - ErrCrossingMarket = errors.New("crossing market") - ErrIOCOrderExpired = errors.New("IOC order expired") - ErrOpenOrders = errors.New("open orders") - ErrOpenReduceOnlyOrders = errors.New("open reduce only orders") - ErrNoTradingAuthority = errors.New("no trading authority") - ErrNoReferrer = errors.New("no referrer") -) - -// Place Order Checks -// 1. price >= 0 -// 2. signer is valid trading authority -// 3. market is valid -// 4. baseAssetQuantity is not 0 and multiple of minSize -// 5. order is not already placed, filled or cancelled -// 6. reduce only amount check OR margin availablity check (not in state, simply compared to other active orders) -// 7. post only order shouldn't cross the market -// 8. HasReferrer -// 9. price precision check -// -// Matching Order Checks -// Since all the above checks are in memory and not guaranteed by consensus, we need to perform them at the time of matching too. In addition: -// (note) 6. Not required in the precompile -// 10. Order is not being overfilled -// 11. Not both post only orders are being matched -func (api *TradingAPI) ValidatePlaceSignedOrder(order *SignedOrder) error { - if order.Price.Sign() != 1 { - return ErrInvalidPrice - } - - if order.BaseAssetQuantity.Sign() == 0 { - return ErrBaseAssetQuantityZero - } - - orderHash, err := order.Hash() - if err != nil { - return err - } - - signer, err := hu.ECRecover(order.Sig, order.Sig) - trader := order.Trader - if trader != signer && !api.configService.IsTradingAuthority(stateDB, trader, signer) { - return ErrNoTradingAuthority - } - - markets := api.configService.GetActiveMarketsCount() - // assumes all markets are active and in sequential order - if order.AmmIndex.Int64() >= markets { - return ErrInvalidMarket - } - // ammAddress := getMarketAddressFromMarketID(order.AmmIndex.Int64(), stateDB) - - market := Market(order.AmmIndex.Int64()) - minSize := api.configService.getMinSizeRequirement(market) - if new(big.Int).Mod(order.BaseAssetQuantity, minSize).Sign() != 0 { - return ErrNotMultiple - } - - // order status - // should not already be submitted - fields := api.db.GetOrderValidationFields() - if fields.Exists { - return ErrOrderAlreadyExists - } - // should not be filled or cancelled - status := hu.OrderStatus(api.configService.GetSignedOrderStatus(orderHash)) - if status != hu.Invalid { - return ErrOrderAlreadyExists - } - - if order.PostOnly { - orderSide := hu.Side(hu.Long) - if order.BaseAssetQuantity.Sign() == -1 { - orderSide = hu.Side(hu.Short) - } - asksHead := fields.AsksHead - bidsHead := fields.BidsHead - if (orderSide == hu.Side(hu.Short) && bidsHead.Sign() != 0 && order.Price.Cmp(bidsHead) != 1) || (orderSide == hu.Side(hu.Long) && asksHead.Sign() != 0 && order.Price.Cmp(asksHead) != -1) { - return ErrCrossingMarket - } - } - - // @todo - // if !bibliophile.HasReferrer(order.Trader) { - // response.Err = ErrNoReferrer.Error() - // } - - // if hu.Mod(order.Price, bibliophile.GetPriceMultiplier(ammAddress)).Sign() != 0 { - // response.Err = ErrPricePrecision.Error() - // return - // } - + api.db.Add(limitOrder) return response } diff --git a/precompile/contracts/bibliophile/amm.go b/precompile/contracts/bibliophile/amm.go index 86cebaa6f6..a57f8abb5d 100644 --- a/precompile/contracts/bibliophile/amm.go +++ b/precompile/contracts/bibliophile/amm.go @@ -80,6 +80,10 @@ func getMultiplier(stateDB contract.StateDB, market common.Address) *big.Int { return stateDB.GetState(market, common.BigToHash(big.NewInt(MULTIPLIER_SLOT))).Big() } +func GetMultiplier(stateDB contract.StateDB, marketID int64) *big.Int { + return getMultiplier(stateDB, getMarketAddressFromMarketID(marketID, stateDB)) +} + func getUnderlyingAssetAddress(stateDB contract.StateDB, market common.Address) common.Address { return common.BytesToAddress(stateDB.GetState(market, common.BigToHash(big.NewInt(UNDERLYING_ASSET_SLOT))).Bytes()) } diff --git a/precompile/contracts/bibliophile/client.go b/precompile/contracts/bibliophile/client.go index d3b882a530..5dc5999cf5 100644 --- a/precompile/contracts/bibliophile/client.go +++ b/precompile/contracts/bibliophile/client.go @@ -54,6 +54,7 @@ type BibliophileClient interface { GetTimeStamp() uint64 GetNotionalPositionAndMargin(trader common.Address, includeFundingPayments bool, mode uint8) (*big.Int, *big.Int) HasReferrer(trader common.Address) bool + GetActiveMarketsCount() int64 } // Define a structure that will implement the Bibliophile interface @@ -67,6 +68,18 @@ func NewBibliophileClient(accessibleState contract.AccessibleState) BibliophileC } } +func (b *bibliophileClient) GetSignedOrderFilledAmount(orderHash [32]byte) *big.Int { + return GetSignedOrderFilledAmount(b.accessibleState.GetStateDB(), orderHash) +} + +func (b *bibliophileClient) GetSignedOrderStatus(orderHash [32]byte) int64 { + return GetSignedOrderStatus(b.accessibleState.GetStateDB(), orderHash) +} + +func (b *bibliophileClient) GetActiveMarketsCount() int64 { + return GetActiveMarketsCount(b.accessibleState.GetStateDB()) +} + func (b *bibliophileClient) GetTimeStamp() uint64 { return b.accessibleState.GetBlockContext().Timestamp() } diff --git a/precompile/contracts/bibliophile/signed_order_book.go b/precompile/contracts/bibliophile/signed_order_book.go new file mode 100644 index 0000000000..2d3dd2a6e7 --- /dev/null +++ b/precompile/contracts/bibliophile/signed_order_book.go @@ -0,0 +1,32 @@ +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" +) + +// @todo change this to the correct values +const ( + SIGNED_ORDERBOOK_ADDRESS = "0x03000000000000000000000000000000000000b4" + SIGNED_ORDER_INFO_SLOT int64 = 1 +) + +// State Reader +func GetSignedOrderFilledAmount(stateDB contract.StateDB, orderHash [32]byte) *big.Int { + orderInfo := signedOrderInfoMappingStorageSlot(orderHash) + num := stateDB.GetState(common.HexToAddress(SIGNED_ORDERBOOK_ADDRESS), common.BigToHash(orderInfo)).Bytes() + return fromTwosComplement(num) +} + +func GetSignedOrderStatus(stateDB contract.StateDB, orderHash [32]byte) int64 { + orderInfo := signedOrderInfoMappingStorageSlot(orderHash) + return new(big.Int).SetBytes(stateDB.GetState(common.HexToAddress(SIGNED_ORDERBOOK_ADDRESS), common.BigToHash(new(big.Int).Add(orderInfo, big.NewInt(1)))).Bytes()).Int64() +} + +func signedOrderInfoMappingStorageSlot(orderHash [32]byte) *big.Int { + return new(big.Int).SetBytes(crypto.Keccak256(append(orderHash[:], common.LeftPadBytes(big.NewInt(SIGNED_ORDER_INFO_SLOT).Bytes(), 32)...))) +} diff --git a/precompile/contracts/juror/ioc_orders.go b/precompile/contracts/juror/ioc_orders.go index 867261ec9c..9958960665 100644 --- a/precompile/contracts/juror/ioc_orders.go +++ b/precompile/contracts/juror/ioc_orders.go @@ -96,7 +96,7 @@ func ValidatePlaceIOCorder(bibliophile b.BibliophileClient, inputStruct *Validat func IImmediateOrCancelOrdersOrderToIOCOrder(order *IImmediateOrCancelOrdersOrder) *ob.IOCOrder { return &ob.IOCOrder{ - BaseOrder: ob.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: order.AmmIndex, Trader: order.Trader, BaseAssetQuantity: order.BaseAssetQuantity, diff --git a/precompile/contracts/juror/limit_orders.go b/precompile/contracts/juror/limit_orders.go index 1f7689d3ee..7ec7ba8025 100644 --- a/precompile/contracts/juror/limit_orders.go +++ b/precompile/contracts/juror/limit_orders.go @@ -166,7 +166,7 @@ func ValidateCancelLimitOrder(bibliophile b.BibliophileClient, inputStruct *Vali func ILimitOrderBookOrderToLimitOrder(o *ILimitOrderBookOrder) *ob.LimitOrder { return &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: o.AmmIndex, Trader: o.Trader, BaseAssetQuantity: o.BaseAssetQuantity, diff --git a/precompile/contracts/juror/matching_validation.go b/precompile/contracts/juror/matching_validation.go index 8b3e9d1890..3a26152033 100644 --- a/precompile/contracts/juror/matching_validation.go +++ b/precompile/contracts/juror/matching_validation.go @@ -4,7 +4,7 @@ import ( "errors" "math/big" - ob "github.com/ava-labs/subnet-evm/plugin/evm/orderbook" + 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" @@ -17,7 +17,7 @@ type Metadata struct { Price *big.Int BlockPlaced *big.Int OrderHash common.Hash - OrderType ob.OrderType + OrderType hu.OrderType PostOnly bool } @@ -93,7 +93,7 @@ func ValidateOrdersAndDetermineFillPrice(bibliophile b.BibliophileClient, inputS return getValidateOrdersAndDetermineFillPriceErrorOutput(ErrInvalidFillAmount, Generic, common.Hash{}) } - decodeStep0, err := ob.DecodeTypeAndEncodedOrder(inputStruct.Data[0]) + decodeStep0, err := hu.DecodeTypeAndEncodedOrder(inputStruct.Data[0]) if err != nil { return getValidateOrdersAndDetermineFillPriceErrorOutput(err, Order0, common.Hash{}) } @@ -102,7 +102,7 @@ func ValidateOrdersAndDetermineFillPrice(bibliophile b.BibliophileClient, inputS return getValidateOrdersAndDetermineFillPriceErrorOutput(err, Order0, m0.OrderHash) } - decodeStep1, err := ob.DecodeTypeAndEncodedOrder(inputStruct.Data[1]) + decodeStep1, err := hu.DecodeTypeAndEncodedOrder(inputStruct.Data[1]) if err != nil { return getValidateOrdersAndDetermineFillPriceErrorOutput(err, Order1, common.Hash{}) } @@ -190,29 +190,29 @@ func determineFillPrice(bibliophile b.BibliophileClient, m0, m1 *Metadata) (*Fil blockDiff := m0.BlockPlaced.Cmp(m1.BlockPlaced) if blockDiff == -1 { // order0 came first, can't be IOC order - if m0.OrderType == ob.IOC { + if m0.OrderType == hu.IOC { return nil, ErrIOCOrderExpired, Order0 } // order1 came second, can't be post only order - if m1.OrderType == ob.Limit && m1.PostOnly { + if m1.OrderType == hu.Limit && m1.PostOnly { return nil, ErrCrossingMarket, Order1 } output.Mode0 = Maker output.Mode1 = Taker } else if blockDiff == 1 { // order1 came first, can't be IOC order - if m1.OrderType == ob.IOC { + if m1.OrderType == hu.IOC { return nil, ErrIOCOrderExpired, Order1 } // order0 came second, can't be post only order - if m0.OrderType == ob.Limit && m0.PostOnly { + if m0.OrderType == hu.Limit && m0.PostOnly { return nil, ErrCrossingMarket, Order0 } output.Mode0 = Taker output.Mode1 = Maker } else { // both orders were placed in same block - if m1.OrderType == ob.IOC { + if m1.OrderType == hu.IOC { // order1 is IOC, order0 is Limit or post only output.Mode0 = Maker output.Mode1 = Taker @@ -239,7 +239,7 @@ func ValidateLiquidationOrderAndDetermineFillPrice(bibliophile b.BibliophileClie return getValidateLiquidationOrderAndDetermineFillPriceErrorOutput(ErrInvalidFillAmount, Generic, common.Hash{}) } - decodeStep0, err := ob.DecodeTypeAndEncodedOrder(inputStruct.Data) + decodeStep0, err := hu.DecodeTypeAndEncodedOrder(inputStruct.Data) if err != nil { return getValidateLiquidationOrderAndDetermineFillPriceErrorOutput(err, Order0, common.Hash{}) } @@ -299,16 +299,16 @@ func determineLiquidationFillPrice(bibliophile b.BibliophileClient, m0 *Metadata return utils.BigIntMax(m0.Price, lowerBound /* oracle spread lower bound */), nil } -func validateOrder(bibliophile b.BibliophileClient, orderType ob.OrderType, encodedOrder []byte, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { - if orderType == ob.Limit { - order, err := ob.DecodeLimitOrder(encodedOrder) +func validateOrder(bibliophile b.BibliophileClient, orderType hu.OrderType, encodedOrder []byte, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { + if orderType == hu.Limit { + order, err := hu.DecodeLimitOrder(encodedOrder) if err != nil { return nil, err } return validateExecuteLimitOrder(bibliophile, order, side, fillAmount) } - if orderType == ob.IOC { - order, err := ob.DecodeIOCOrder(encodedOrder) + if orderType == hu.IOC { + order, err := hu.DecodeIOCOrder(encodedOrder) if err != nil { return nil, err } @@ -317,7 +317,7 @@ func validateOrder(bibliophile b.BibliophileClient, orderType ob.OrderType, enco return nil, errors.New("invalid order type") } -func validateExecuteLimitOrder(bibliophile b.BibliophileClient, order *ob.LimitOrder, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { +func validateExecuteLimitOrder(bibliophile b.BibliophileClient, order *hu.LimitOrder, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { orderHash, err := order.Hash() if err != nil { return nil, err @@ -332,17 +332,17 @@ func validateExecuteLimitOrder(bibliophile b.BibliophileClient, order *ob.LimitO BlockPlaced: bibliophile.GetBlockPlaced(orderHash), Price: order.Price, OrderHash: orderHash, - OrderType: ob.Limit, + OrderType: hu.Limit, PostOnly: order.PostOnly, }, nil } -func validateExecuteIOCOrder(bibliophile b.BibliophileClient, order *ob.IOCOrder, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { +func validateExecuteIOCOrder(bibliophile b.BibliophileClient, order *hu.IOCOrder, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { orderHash, err := order.Hash() if err != nil { return nil, err } - if ob.OrderType(order.OrderType) != ob.IOC { + if hu.OrderType(order.OrderType) != hu.IOC { return &Metadata{OrderHash: orderHash}, errors.New("not ioc order") } if order.ExpireAt.Uint64() < bibliophile.GetTimeStamp() { @@ -358,12 +358,12 @@ func validateExecuteIOCOrder(bibliophile b.BibliophileClient, order *ob.IOCOrder BlockPlaced: bibliophile.IOC_GetBlockPlaced(orderHash), Price: order.Price, OrderHash: orderHash, - OrderType: ob.IOC, + OrderType: hu.IOC, PostOnly: false, }, nil } -func validateLimitOrderLike(bibliophile b.BibliophileClient, order *ob.BaseOrder, filledAmount *big.Int, status OrderStatus, side Side, fillAmount *big.Int) error { +func validateLimitOrderLike(bibliophile b.BibliophileClient, order *hu.BaseOrder, filledAmount *big.Int, status OrderStatus, side Side, fillAmount *big.Int) error { if status != Placed { return ErrInvalidOrder } @@ -447,13 +447,13 @@ func getRequiredMargin(bibliophile b.BibliophileClient, order ILimitOrderBookOrd } func formatOrder(orderBytes []byte) interface{} { - decodeStep0, err := ob.DecodeTypeAndEncodedOrder(orderBytes) + decodeStep0, err := hu.DecodeTypeAndEncodedOrder(orderBytes) if err != nil { return orderBytes } - if decodeStep0.OrderType == ob.Limit { - order, err := ob.DecodeLimitOrder(decodeStep0.EncodedOrder) + if decodeStep0.OrderType == hu.Limit { + order, err := hu.DecodeLimitOrder(decodeStep0.EncodedOrder) if err != nil { return decodeStep0 } @@ -465,8 +465,8 @@ func formatOrder(orderBytes []byte) interface{} { orderJson["hash"] = orderHash.String() return orderJson } - if decodeStep0.OrderType == ob.IOC { - order, err := ob.DecodeIOCOrder(decodeStep0.EncodedOrder) + if decodeStep0.OrderType == hu.IOC { + order, err := hu.DecodeIOCOrder(decodeStep0.EncodedOrder) if err != nil { return decodeStep0 } diff --git a/precompile/contracts/jurorv2/matching_validation.go b/precompile/contracts/jurorv2/matching_validation.go index dc8c30ecf6..973cf3bcd6 100644 --- a/precompile/contracts/jurorv2/matching_validation.go +++ b/precompile/contracts/jurorv2/matching_validation.go @@ -5,6 +5,7 @@ import ( "math/big" ob "github.com/ava-labs/subnet-evm/plugin/evm/orderbook" + 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" @@ -94,7 +95,7 @@ func ValidateOrdersAndDetermineFillPrice(bibliophile b.BibliophileClient, inputS return getValidateOrdersAndDetermineFillPriceErrorOutput(ErrInvalidFillAmount, Generic, common.Hash{}) } - decodeStep0, err := ob.DecodeTypeAndEncodedOrder(inputStruct.Data[0]) + decodeStep0, err := hu.DecodeTypeAndEncodedOrder(inputStruct.Data[0]) if err != nil { return getValidateOrdersAndDetermineFillPriceErrorOutput(err, Order0, common.Hash{}) } @@ -103,7 +104,7 @@ func ValidateOrdersAndDetermineFillPrice(bibliophile b.BibliophileClient, inputS return getValidateOrdersAndDetermineFillPriceErrorOutput(err, Order0, m0.OrderHash) } - decodeStep1, err := ob.DecodeTypeAndEncodedOrder(inputStruct.Data[1]) + decodeStep1, err := hu.DecodeTypeAndEncodedOrder(inputStruct.Data[1]) if err != nil { return getValidateOrdersAndDetermineFillPriceErrorOutput(err, Order1, common.Hash{}) } @@ -120,6 +121,7 @@ func ValidateOrdersAndDetermineFillPrice(bibliophile b.BibliophileClient, inputS return getValidateOrdersAndDetermineFillPriceErrorOutput(ErrNoMatch, Generic, common.Hash{}) } + // check 11 if m0.PostOnly && m1.PostOnly { return getValidateOrdersAndDetermineFillPriceErrorOutput(ErrBothPostOnly, Generic, common.Hash{}) } @@ -244,7 +246,7 @@ func ValidateLiquidationOrderAndDetermineFillPrice(bibliophile b.BibliophileClie return getValidateLiquidationOrderAndDetermineFillPriceErrorOutput(ErrInvalidFillAmount, Generic, common.Hash{}) } - decodeStep0, err := ob.DecodeTypeAndEncodedOrder(inputStruct.Data) + decodeStep0, err := hu.DecodeTypeAndEncodedOrder(inputStruct.Data) if err != nil { return getValidateLiquidationOrderAndDetermineFillPriceErrorOutput(err, Order0, common.Hash{}) } @@ -306,21 +308,21 @@ func determineLiquidationFillPrice(bibliophile b.BibliophileClient, m0 *Metadata func validateOrder(bibliophile b.BibliophileClient, orderType ob.OrderType, encodedOrder []byte, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { if orderType == ob.Limit { - order, err := ob.DecodeLimitOrder(encodedOrder) + order, err := hu.DecodeLimitOrder(encodedOrder) if err != nil { return nil, err } return validateExecuteLimitOrder(bibliophile, order, side, fillAmount) } if orderType == ob.IOC { - order, err := ob.DecodeIOCOrder(encodedOrder) + order, err := hu.DecodeIOCOrder(encodedOrder) if err != nil { return nil, err } return validateExecuteIOCOrder(bibliophile, order, side, fillAmount) } if orderType == ob.Signed { - order, err := ob.DecodeSignedOrder(encodedOrder) + order, err := hu.DecodeSignedOrder(encodedOrder) if err != nil { return nil, err } @@ -375,20 +377,31 @@ func validateExecuteIOCOrder(bibliophile b.BibliophileClient, order *ob.IOCOrder }, nil } -func validateExecuteSignedOrder(bibliophile b.BibliophileClient, order *ob.SignedOrder, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { +func validateExecuteSignedOrder(bibliophile b.BibliophileClient, order *hu.SignedOrder, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { orderHash, err := order.Hash() if err != nil { return nil, err } - if ob.OrderType(order.OrderType) != ob.Signed { - return &Metadata{OrderHash: orderHash}, errors.New("not signed order") - } - if order.ExpireAt.Uint64() < bibliophile.GetTimeStamp() { - return &Metadata{OrderHash: orderHash}, errors.New("order expired") + + trader, signer, err := hu.ValidateSignedOrder( + order, + hu.SignedOrderValidationFields{ + Now: bibliophile.GetTimeStamp(), + ActiveMarketsCount: bibliophile.GetActiveMarketsCount(), + MinSize: bibliophile.GetMinSizeRequirement(order.AmmIndex.Int64()), + PriceMultiplier: bibliophile.GetPriceMultiplier(bibliophile.GetMarketAddressFromMarketID(order.AmmIndex.Int64())), + Status: bibliophile.GetSignedOrderStatus(orderHash), + }, + ) + if err != nil { + return &Metadata{OrderHash: orderHash}, err } - if !order.PostOnly { - return &Metadata{OrderHash: orderHash}, errors.New("not post only") + + if trader != signer && !bibliophile.IsTradingAuthority(trader, signer) { + return &Metadata{OrderHash: orderHash}, hu.ErrNoTradingAuthority } + + // M1, M2 orderStatus := OrderStatus(bibliophile.GetSignedOrderStatus(orderHash)) if orderStatus == Invalid { // signed orders don't get placed in the contract, so we consider them placed by default @@ -397,6 +410,12 @@ func validateExecuteSignedOrder(bibliophile b.BibliophileClient, order *ob.Signe if err := validateLimitOrderLike(bibliophile, &order.BaseOrder, bibliophile.GetSignedOrderFilledAmount(orderHash), OrderStatus(bibliophile.GetSignedOrderStatus(orderHash)), side, fillAmount); err != nil { return &Metadata{OrderHash: orderHash}, err } + + // M3 + if !bibliophile.HasReferrer(order.Trader) { + return &Metadata{OrderHash: orderHash}, ErrNoReferrer + } + return &Metadata{ AmmIndex: order.AmmIndex, Trader: order.Trader, @@ -409,7 +428,7 @@ func validateExecuteSignedOrder(bibliophile b.BibliophileClient, order *ob.Signe }, nil } -func validateLimitOrderLike(bibliophile b.BibliophileClient, order *ob.BaseOrder, filledAmount *big.Int, status OrderStatus, side Side, fillAmount *big.Int) error { +func validateLimitOrderLike(bibliophile b.BibliophileClient, order *hu.BaseOrder, filledAmount *big.Int, status OrderStatus, side Side, fillAmount *big.Int) error { if status != Placed { return ErrInvalidOrder } @@ -468,7 +487,6 @@ func validateLimitOrderLike(bibliophile b.BibliophileClient, order *ob.BaseOrder } // Common - func reducesPosition(positionSize *big.Int, baseAssetQuantity *big.Int) bool { if positionSize.Sign() == 1 && baseAssetQuantity.Sign() == -1 && big.NewInt(0).Add(positionSize, baseAssetQuantity).Sign() != -1 { return true @@ -493,13 +511,13 @@ func getRequiredMargin(bibliophile b.BibliophileClient, order ILimitOrderBookOrd } func formatOrder(orderBytes []byte) interface{} { - decodeStep0, err := ob.DecodeTypeAndEncodedOrder(orderBytes) + decodeStep0, err := hu.DecodeTypeAndEncodedOrder(orderBytes) if err != nil { return orderBytes } if decodeStep0.OrderType == ob.Limit { - order, err := ob.DecodeLimitOrder(decodeStep0.EncodedOrder) + order, err := hu.DecodeLimitOrder(decodeStep0.EncodedOrder) if err != nil { return decodeStep0 } @@ -512,7 +530,7 @@ func formatOrder(orderBytes []byte) interface{} { return orderJson } if decodeStep0.OrderType == ob.IOC { - order, err := ob.DecodeIOCOrder(decodeStep0.EncodedOrder) + order, err := hu.DecodeIOCOrder(decodeStep0.EncodedOrder) if err != nil { return decodeStep0 } From 9bd0b7db1e923a743d34a2745d1f25f6ae14cd03 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Fri, 15 Dec 2023 10:31:43 +0000 Subject: [PATCH 04/12] stash --- precompile/contracts/jurorv2/ioc_orders.go | 2 +- precompile/contracts/jurorv2/limit_orders.go | 2 +- precompile/registry/registry.go | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/precompile/contracts/jurorv2/ioc_orders.go b/precompile/contracts/jurorv2/ioc_orders.go index 867261ec9c..9958960665 100644 --- a/precompile/contracts/jurorv2/ioc_orders.go +++ b/precompile/contracts/jurorv2/ioc_orders.go @@ -96,7 +96,7 @@ func ValidatePlaceIOCorder(bibliophile b.BibliophileClient, inputStruct *Validat func IImmediateOrCancelOrdersOrderToIOCOrder(order *IImmediateOrCancelOrdersOrder) *ob.IOCOrder { return &ob.IOCOrder{ - BaseOrder: ob.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: order.AmmIndex, Trader: order.Trader, BaseAssetQuantity: order.BaseAssetQuantity, diff --git a/precompile/contracts/jurorv2/limit_orders.go b/precompile/contracts/jurorv2/limit_orders.go index 1f7689d3ee..7ec7ba8025 100644 --- a/precompile/contracts/jurorv2/limit_orders.go +++ b/precompile/contracts/jurorv2/limit_orders.go @@ -166,7 +166,7 @@ func ValidateCancelLimitOrder(bibliophile b.BibliophileClient, inputStruct *Vali func ILimitOrderBookOrderToLimitOrder(o *ILimitOrderBookOrder) *ob.LimitOrder { return &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: o.AmmIndex, Trader: o.Trader, BaseAssetQuantity: o.BaseAssetQuantity, diff --git a/precompile/registry/registry.go b/precompile/registry/registry.go index 4a4bd34954..0abc5174b1 100644 --- a/precompile/registry/registry.go +++ b/precompile/registry/registry.go @@ -18,6 +18,7 @@ import ( _ "github.com/ava-labs/subnet-evm/precompile/contracts/rewardmanager" _ "github.com/ava-labs/subnet-evm/precompile/contracts/juror" + _ "github.com/ava-labs/subnet-evm/precompile/contracts/jurorv2" _ "github.com/ava-labs/subnet-evm/precompile/contracts/ticks" _ "github.com/ava-labs/subnet-evm/x/warp" // ADD YOUR PRECOMPILE HERE From 210c106ba8ac9eec0ebd3b0ac383b4373fb67ef0 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:42:36 +0000 Subject: [PATCH 05/12] fix unit tests --- plugin/evm/orderbook/config_service.go | 14 ++--- .../contract_events_processor_test.go | 2 +- .../evm/orderbook/matching_pipeline_test.go | 5 +- plugin/evm/orderbook/memory_database.go | 4 -- plugin/evm/orderbook/memory_database_test.go | 2 +- plugin/evm/orderbook/mocks.go | 20 ++++++ plugin/evm/orderbook/order_types_test.go | 25 ++++---- .../contracts/bibliophile/client_mock.go | 58 ++++++++++++----- precompile/contracts/bibliophile/orderbook.go | 7 ++- .../bibliophile/signed_order_book.go | 13 ++-- precompile/contracts/juror/ioc_orders_test.go | 9 +-- .../juror/matching_validation_test.go | 62 +++++++++---------- .../contracts/jurorv2/ioc_orders_test.go | 18 +++--- .../jurorv2/matching_validation_test.go | 62 +++++++++---------- precompile/contracts/jurorv2/module.go | 4 +- precompile/registry/registry.go | 1 + 16 files changed, 178 insertions(+), 128 deletions(-) diff --git a/plugin/evm/orderbook/config_service.go b/plugin/evm/orderbook/config_service.go index 0e85abd9dc..3c525247f3 100644 --- a/plugin/evm/orderbook/config_service.go +++ b/plugin/evm/orderbook/config_service.go @@ -77,13 +77,13 @@ func (cs *ConfigService) getStateAtCurrentBlock() *state.StateDB { return stateDB } -func (cs *ConfigService) getStateAtBlock(number uint64) *state.StateDB { - stateDB, err := cs.blockChain.StateAt(cs.blockChain.GetHeaderByNumber(number).Root) - if err != nil { - panic(err) - } - return stateDB -} +// func (cs *ConfigService) getStateAtBlock(number uint64) *state.StateDB { +// stateDB, err := cs.blockChain.StateAt(cs.blockChain.GetHeaderByNumber(number).Root) +// if err != nil { +// panic(err) +// } +// return stateDB +// } func (cs *ConfigService) GetActiveMarketsCount() int64 { return bibliophile.GetActiveMarketsCount(cs.getStateAtCurrentBlock()) diff --git a/plugin/evm/orderbook/contract_events_processor_test.go b/plugin/evm/orderbook/contract_events_processor_test.go index 7b751bfcb4..c0333a606d 100644 --- a/plugin/evm/orderbook/contract_events_processor_test.go +++ b/plugin/evm/orderbook/contract_events_processor_test.go @@ -751,7 +751,7 @@ func getEventFromABI(contractABI abi.ABI, eventName string) abi.Event { func getLimitOrder(ammIndex *big.Int, traderAddress common.Address, baseAssetQuantity *big.Int, price *big.Int, salt *big.Int) LimitOrder { return LimitOrder{ - BaseOrder: BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: ammIndex, Trader: traderAddress, BaseAssetQuantity: baseAssetQuantity, diff --git a/plugin/evm/orderbook/matching_pipeline_test.go b/plugin/evm/orderbook/matching_pipeline_test.go index f8fbbfd6b1..5420290d37 100644 --- a/plugin/evm/orderbook/matching_pipeline_test.go +++ b/plugin/evm/orderbook/matching_pipeline_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -505,7 +506,7 @@ func TestAreMatchingOrders(t *testing.T) { LifecycleList: []Lifecycle{Lifecycle{}}, BlockNumber: big.NewInt(21), RawOrder: &LimitOrder{ - BaseOrder: BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(1), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -529,7 +530,7 @@ func TestAreMatchingOrders(t *testing.T) { LifecycleList: []Lifecycle{Lifecycle{}}, BlockNumber: big.NewInt(21), RawOrder: &LimitOrder{ - BaseOrder: BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(1), Trader: trader, BaseAssetQuantity: big.NewInt(-10), diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index 5d0c4765c1..27d6cd1284 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -87,10 +87,6 @@ const ( Signed = hu.Signed ) -// func (o OrderType) String() string { -// return [...]string{"limit", "ioc"}[o] -// } - type Lifecycle struct { BlockNumber uint64 Status Status diff --git a/plugin/evm/orderbook/memory_database_test.go b/plugin/evm/orderbook/memory_database_test.go index 587d289ba9..548898b4fe 100644 --- a/plugin/evm/orderbook/memory_database_test.go +++ b/plugin/evm/orderbook/memory_database_test.go @@ -847,7 +847,7 @@ func createIOCOrder(positionType PositionType, userAddress string, baseAssetQuan RawOrder: &IOCOrder{ OrderType: uint8(IOC), ExpireAt: expireAt, - BaseOrder: BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: common.HexToAddress(userAddress), BaseAssetQuantity: baseAssetQuantity, diff --git a/plugin/evm/orderbook/mocks.go b/plugin/evm/orderbook/mocks.go index 38d74b4327..ce5075a921 100644 --- a/plugin/evm/orderbook/mocks.go +++ b/plugin/evm/orderbook/mocks.go @@ -157,6 +157,14 @@ func (db *MockLimitOrderDatabase) GetSamplePIAttemptedTime() uint64 { func (db *MockLimitOrderDatabase) SignalSamplePIAttempted(time uint64) {} +func (db *MockLimitOrderDatabase) GetOrderValidationFields( + orderId common.Hash, + trader common.Address, + marketId int, +) OrderValidationFields { + return OrderValidationFields{} +} + type MockLimitOrderTxProcessor struct { mock.Mock } @@ -287,6 +295,18 @@ func (cs *MockConfigService) GetCollaterals() []hu.Collateral { return []hu.Collateral{{Price: big.NewInt(1e6), Weight: big.NewInt(1e6), Decimals: 6}} } +func (cs *MockConfigService) GetPriceMultiplier(market Market) *big.Int { + return big.NewInt(1e6) +} + +func (cs *MockConfigService) GetSignedOrderStatus(orderHash common.Hash) int64 { + return 0 +} + +func (cs *MockConfigService) IsTradingAuthority(trader, signer common.Address) bool { + return false +} + func NewMockConfigService() *MockConfigService { return &MockConfigService{} } diff --git a/plugin/evm/orderbook/order_types_test.go b/plugin/evm/orderbook/order_types_test.go index 6c672732e8..af5265a333 100644 --- a/plugin/evm/orderbook/order_types_test.go +++ b/plugin/evm/orderbook/order_types_test.go @@ -8,6 +8,7 @@ import ( "testing" + hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" ) @@ -20,7 +21,7 @@ func TestDecodeLimitOrder(t *testing.T) { strings.TrimPrefix("0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000004563918244f4000000000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000018a82b01e9d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0x"), Limit, LimitOrder{ - BaseOrder: BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), BaseAssetQuantity: big.NewInt(5000000000000000000), @@ -40,7 +41,7 @@ func TestDecodeLimitOrder(t *testing.T) { strings.TrimPrefix("0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000004563918244f4000000000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000018a82b4121c00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", "0x"), Limit, LimitOrder{ - BaseOrder: BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), BaseAssetQuantity: big.NewInt(5000000000000000000), @@ -55,7 +56,7 @@ func TestDecodeLimitOrder(t *testing.T) { t.Run("short order", func(t *testing.T) { order := LimitOrder{ - BaseOrder: BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), BaseAssetQuantity: big.NewInt(-5000000000000000000), @@ -84,7 +85,7 @@ func TestDecodeLimitOrder(t *testing.T) { strings.TrimPrefix("0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c000000000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000018a82b7597700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", "0x"), Limit, LimitOrder{ - BaseOrder: BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), BaseAssetQuantity: big.NewInt(-5000000000000000000), @@ -103,7 +104,7 @@ func TestDecodeLimitOrder(t *testing.T) { strings.TrimPrefix("0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c000000000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000018a82b8382e00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001", "0x"), Limit, LimitOrder{ - BaseOrder: BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), BaseAssetQuantity: big.NewInt(-5000000000000000000), @@ -121,7 +122,7 @@ func testDecodeTypeAndEncodedOrder(t *testing.T, typedEncodedOrder string, encod testData, err := hex.DecodeString(typedEncodedOrder) assert.Nil(t, err) - decodeStep, err := DecodeTypeAndEncodedOrder(testData) + decodeStep, err := hu.DecodeTypeAndEncodedOrder(testData) assert.Nil(t, err) assert.Equal(t, orderType, decodeStep.OrderType) @@ -133,7 +134,7 @@ func testDecodeLimitOrder(t *testing.T, encodedOrder string, expectedOutput inte testData, err := hex.DecodeString(encodedOrder) assert.Nil(t, err) - result, err := DecodeLimitOrder(testData) + result, err := hu.DecodeLimitOrder(testData) fmt.Println(result) assert.NoError(t, err) assert.NotNil(t, result) @@ -146,7 +147,7 @@ func TestDecodeIOCOrder(t *testing.T) { order := &IOCOrder{ OrderType: 1, ExpireAt: big.NewInt(1688994854), - BaseOrder: BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), BaseAssetQuantity: big.NewInt(5000000000000000000), @@ -171,7 +172,7 @@ func TestDecodeIOCOrder(t *testing.T) { order := &IOCOrder{ OrderType: 1, ExpireAt: big.NewInt(1688994854), - BaseOrder: BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), BaseAssetQuantity: big.NewInt(-5000000000000000000), @@ -197,7 +198,7 @@ func testDecodeTypeAndEncodedIOCOrder(t *testing.T, typedEncodedOrder string, en testData, err := hex.DecodeString(typedEncodedOrder) assert.Nil(t, err) - decodeStep, err := DecodeTypeAndEncodedOrder(testData) + decodeStep, err := hu.DecodeTypeAndEncodedOrder(testData) assert.Nil(t, err) assert.Equal(t, orderType, decodeStep.OrderType) @@ -206,7 +207,7 @@ func testDecodeTypeAndEncodedIOCOrder(t *testing.T, typedEncodedOrder string, en } func testDecodeIOCOrder(t *testing.T, encodedOrder []byte, expectedOutput *IOCOrder) { - result, err := DecodeIOCOrder(encodedOrder) + result, err := hu.DecodeIOCOrder(encodedOrder) assert.NoError(t, err) fmt.Println(result) assert.NotNil(t, result) @@ -219,7 +220,7 @@ func assertIOCOrderEquality(t *testing.T, expected, actual *IOCOrder) { assertLimitOrderEquality(t, expected.BaseOrder, actual.BaseOrder) } -func assertLimitOrderEquality(t *testing.T, expected, actual BaseOrder) { +func assertLimitOrderEquality(t *testing.T, expected, actual hu.BaseOrder) { assert.Equal(t, expected.AmmIndex.Int64(), actual.AmmIndex.Int64()) assert.Equal(t, expected.Trader, actual.Trader) assert.Equal(t, expected.BaseAssetQuantity, actual.BaseAssetQuantity) diff --git a/precompile/contracts/bibliophile/client_mock.go b/precompile/contracts/bibliophile/client_mock.go index 08ad27333d..99313d278a 100644 --- a/precompile/contracts/bibliophile/client_mock.go +++ b/precompile/contracts/bibliophile/client_mock.go @@ -8,8 +8,6 @@ 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" ) @@ -52,6 +50,20 @@ func (mr *MockBibliophileClientMockRecorder) GetAcceptableBoundsForLiquidation(m return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAcceptableBoundsForLiquidation", reflect.TypeOf((*MockBibliophileClient)(nil).GetAcceptableBoundsForLiquidation), marketId) } +// GetActiveMarketsCount mocks base method. +func (m *MockBibliophileClient) GetActiveMarketsCount() int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetActiveMarketsCount") + ret0, _ := ret[0].(int64) + return ret0 +} + +// GetActiveMarketsCount indicates an expected call of GetActiveMarketsCount. +func (mr *MockBibliophileClientMockRecorder) GetActiveMarketsCount() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveMarketsCount", reflect.TypeOf((*MockBibliophileClient)(nil).GetActiveMarketsCount)) +} + // GetAskSize mocks base method. func (m *MockBibliophileClient) GetAskSize(ammAddress common.Address, price *big.Int) *big.Int { m.ctrl.T.Helper() @@ -136,20 +148,6 @@ 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() @@ -347,6 +345,34 @@ func (mr *MockBibliophileClientMockRecorder) GetShortOpenOrdersAmount(trader, am return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetShortOpenOrdersAmount", reflect.TypeOf((*MockBibliophileClient)(nil).GetShortOpenOrdersAmount), trader, ammIndex) } +// GetSignedOrderFilledAmount mocks base method. +func (m *MockBibliophileClient) GetSignedOrderFilledAmount(orderHash [32]byte) *big.Int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSignedOrderFilledAmount", orderHash) + ret0, _ := ret[0].(*big.Int) + return ret0 +} + +// GetSignedOrderFilledAmount indicates an expected call of GetSignedOrderFilledAmount. +func (mr *MockBibliophileClientMockRecorder) GetSignedOrderFilledAmount(orderHash interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSignedOrderFilledAmount", reflect.TypeOf((*MockBibliophileClient)(nil).GetSignedOrderFilledAmount), orderHash) +} + +// GetSignedOrderStatus mocks base method. +func (m *MockBibliophileClient) GetSignedOrderStatus(orderHash [32]byte) int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSignedOrderStatus", orderHash) + ret0, _ := ret[0].(int64) + return ret0 +} + +// GetSignedOrderStatus indicates an expected call of GetSignedOrderStatus. +func (mr *MockBibliophileClientMockRecorder) GetSignedOrderStatus(orderHash interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSignedOrderStatus", reflect.TypeOf((*MockBibliophileClient)(nil).GetSignedOrderStatus), orderHash) +} + // GetSize mocks base method. func (m *MockBibliophileClient) GetSize(market common.Address, trader *common.Address) *big.Int { m.ctrl.T.Helper() diff --git a/precompile/contracts/bibliophile/orderbook.go b/precompile/contracts/bibliophile/orderbook.go index d1bd2b0067..f365199283 100644 --- a/precompile/contracts/bibliophile/orderbook.go +++ b/precompile/contracts/bibliophile/orderbook.go @@ -12,9 +12,10 @@ import ( ) const ( - ORDERBOOK_GENESIS_ADDRESS = "0x03000000000000000000000000000000000000b0" - IS_VALIDATOR_SLOT int64 = 1 - IS_TRADING_AUTHORITY_SLOT int64 = 2 + ORDERBOOK_GENESIS_ADDRESS = "0x03000000000000000000000000000000000000b0" + IS_VALIDATOR_SLOT int64 = 1 + IS_TRADING_AUTHORITY_SLOT int64 = 2 + ORDER_HANDLER_STORAGE_SLOT int64 = 5 ) var ( diff --git a/precompile/contracts/bibliophile/signed_order_book.go b/precompile/contracts/bibliophile/signed_order_book.go index 2d3dd2a6e7..a29832ab01 100644 --- a/precompile/contracts/bibliophile/signed_order_book.go +++ b/precompile/contracts/bibliophile/signed_order_book.go @@ -9,24 +9,27 @@ import ( "github.com/ethereum/go-ethereum/crypto" ) -// @todo change this to the correct values const ( - SIGNED_ORDERBOOK_ADDRESS = "0x03000000000000000000000000000000000000b4" - SIGNED_ORDER_INFO_SLOT int64 = 1 + SIGNED_ORDER_INFO_SLOT int64 = 53 ) // State Reader func GetSignedOrderFilledAmount(stateDB contract.StateDB, orderHash [32]byte) *big.Int { orderInfo := signedOrderInfoMappingStorageSlot(orderHash) - num := stateDB.GetState(common.HexToAddress(SIGNED_ORDERBOOK_ADDRESS), common.BigToHash(orderInfo)).Bytes() + num := stateDB.GetState(signedOrderBookAddress(stateDB), common.BigToHash(orderInfo)).Bytes() return fromTwosComplement(num) } func GetSignedOrderStatus(stateDB contract.StateDB, orderHash [32]byte) int64 { orderInfo := signedOrderInfoMappingStorageSlot(orderHash) - return new(big.Int).SetBytes(stateDB.GetState(common.HexToAddress(SIGNED_ORDERBOOK_ADDRESS), common.BigToHash(new(big.Int).Add(orderInfo, big.NewInt(1)))).Bytes()).Int64() + return new(big.Int).SetBytes(stateDB.GetState(signedOrderBookAddress(stateDB), common.BigToHash(new(big.Int).Add(orderInfo, big.NewInt(1)))).Bytes()).Int64() } func signedOrderInfoMappingStorageSlot(orderHash [32]byte) *big.Int { return new(big.Int).SetBytes(crypto.Keccak256(append(orderHash[:], common.LeftPadBytes(big.NewInt(SIGNED_ORDER_INFO_SLOT).Bytes(), 32)...))) } + +func signedOrderBookAddress(stateDB contract.StateDB) common.Address { + slot := crypto.Keccak256(append(common.LeftPadBytes(big.NewInt(2).Bytes() /* orderType */, 32), common.LeftPadBytes(big.NewInt(ORDER_HANDLER_STORAGE_SLOT).Bytes(), 32)...)) + return common.BytesToAddress(stateDB.GetState(common.HexToAddress(ORDERBOOK_GENESIS_ADDRESS), common.BytesToHash(slot)).Bytes()) +} diff --git a/precompile/contracts/juror/ioc_orders_test.go b/precompile/contracts/juror/ioc_orders_test.go index 19c1a1f5dc..c73f0f5e6a 100644 --- a/precompile/contracts/juror/ioc_orders_test.go +++ b/precompile/contracts/juror/ioc_orders_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/ava-labs/subnet-evm/plugin/evm/orderbook" + hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" b "github.com/ava-labs/subnet-evm/precompile/contracts/bibliophile" "github.com/ethereum/go-ethereum/common" gomock "github.com/golang/mock/gomock" @@ -428,7 +429,7 @@ func TestValidateExecuteIOCOrder(t *testing.T) { order := orderbook.IOCOrder{ OrderType: 0, // incoreect order type ExpireAt: big.NewInt(1001), - BaseOrder: orderbook.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -449,7 +450,7 @@ func TestValidateExecuteIOCOrder(t *testing.T) { order := orderbook.IOCOrder{ OrderType: 1, ExpireAt: big.NewInt(990), - BaseOrder: orderbook.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -472,7 +473,7 @@ func TestValidateExecuteIOCOrder(t *testing.T) { order := orderbook.IOCOrder{ OrderType: 1, ExpireAt: big.NewInt(1001), - BaseOrder: orderbook.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -507,7 +508,7 @@ func TestValidateExecuteIOCOrder(t *testing.T) { order := orderbook.IOCOrder{ OrderType: 1, ExpireAt: big.NewInt(1001), - BaseOrder: orderbook.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), diff --git a/precompile/contracts/juror/matching_validation_test.go b/precompile/contracts/juror/matching_validation_test.go index 538809aac4..3abd025bd0 100644 --- a/precompile/contracts/juror/matching_validation_test.go +++ b/precompile/contracts/juror/matching_validation_test.go @@ -26,7 +26,7 @@ func TestValidateLimitOrderLike(t *testing.T) { mockBibliophile := b.NewMockBibliophileClient(ctrl) trader := common.HexToAddress("0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC") - order := &ob.BaseOrder{ + order := &hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -106,7 +106,7 @@ func TestValidateLimitOrderLike(t *testing.T) { }) t.Run("Side=Short", func(t *testing.T) { - order := &ob.BaseOrder{ + order := &hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -184,7 +184,7 @@ func TestValidateLimitOrderLike(t *testing.T) { }) t.Run("invalid side", func(t *testing.T) { - order := &ob.BaseOrder{ + order := &hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -208,8 +208,8 @@ func TestValidateExecuteLimitOrder(t *testing.T) { marketAddress := common.HexToAddress("0xa72b463C21dA61cCc86069cFab82e9e8491152a0") trader := common.HexToAddress("0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC") - order := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(534), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -682,8 +682,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { defer ctrl.Finish() t.Run("invalid fillAmount", func(t *testing.T) { - order0 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order0 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -693,8 +693,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { }, PostOnly: false, } - order1 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order1 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -720,8 +720,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { }) t.Run("different amm", func(t *testing.T) { - order0 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order0 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -732,8 +732,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { PostOnly: false, } order0Hash, _ := order0.Hash() - order1 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order1 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(1), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -768,8 +768,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { }) t.Run("price mismatch", func(t *testing.T) { - order0 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order0 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -780,8 +780,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { PostOnly: false, } order0Hash, _ := order0.Hash() - order1 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order1 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -816,8 +816,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { }) t.Run("fillAmount not multiple", func(t *testing.T) { - order0 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order0 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -828,8 +828,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { PostOnly: false, } order0Hash, _ := order0.Hash() - order1 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order1 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -867,8 +867,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { }) t.Run("success", func(t *testing.T) { - order0 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order0 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -879,8 +879,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { PostOnly: false, } order0Hash, _ := order0.Hash() - order1 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order1 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -976,8 +976,8 @@ func TestValidateLiquidationOrderAndDetermineFillPrice(t *testing.T) { defer ctrl.Finish() t.Run("invalid liquidationAmount", func(t *testing.T) { - order := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -1001,8 +1001,8 @@ func TestValidateLiquidationOrderAndDetermineFillPrice(t *testing.T) { }) t.Run("fillAmount not multiple", func(t *testing.T) { - order := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -1034,8 +1034,8 @@ func TestValidateLiquidationOrderAndDetermineFillPrice(t *testing.T) { }) t.Run("success", func(t *testing.T) { - order := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), diff --git a/precompile/contracts/jurorv2/ioc_orders_test.go b/precompile/contracts/jurorv2/ioc_orders_test.go index 19c1a1f5dc..8effac87ef 100644 --- a/precompile/contracts/jurorv2/ioc_orders_test.go +++ b/precompile/contracts/jurorv2/ioc_orders_test.go @@ -5,7 +5,7 @@ import ( "math/big" "testing" - "github.com/ava-labs/subnet-evm/plugin/evm/orderbook" + hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" b "github.com/ava-labs/subnet-evm/precompile/contracts/bibliophile" "github.com/ethereum/go-ethereum/common" gomock "github.com/golang/mock/gomock" @@ -425,10 +425,10 @@ func TestValidateExecuteIOCOrder(t *testing.T) { t.Run("not ioc order", func(t *testing.T) { mockBibliophile := b.NewMockBibliophileClient(ctrl) - order := orderbook.IOCOrder{ + order := hu.IOCOrder{ OrderType: 0, // incoreect order type ExpireAt: big.NewInt(1001), - BaseOrder: orderbook.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -446,10 +446,10 @@ func TestValidateExecuteIOCOrder(t *testing.T) { t.Run("ioc expired", func(t *testing.T) { mockBibliophile := b.NewMockBibliophileClient(ctrl) - order := orderbook.IOCOrder{ + order := hu.IOCOrder{ OrderType: 1, ExpireAt: big.NewInt(990), - BaseOrder: orderbook.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -469,10 +469,10 @@ func TestValidateExecuteIOCOrder(t *testing.T) { t.Run("valid order", func(t *testing.T) { mockBibliophile := b.NewMockBibliophileClient(ctrl) - order := orderbook.IOCOrder{ + order := hu.IOCOrder{ OrderType: 1, ExpireAt: big.NewInt(1001), - BaseOrder: orderbook.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -504,10 +504,10 @@ func TestValidateExecuteIOCOrder(t *testing.T) { t.Run("valid order - reduce only", func(t *testing.T) { mockBibliophile := b.NewMockBibliophileClient(ctrl) - order := orderbook.IOCOrder{ + order := hu.IOCOrder{ OrderType: 1, ExpireAt: big.NewInt(1001), - BaseOrder: orderbook.BaseOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), diff --git a/precompile/contracts/jurorv2/matching_validation_test.go b/precompile/contracts/jurorv2/matching_validation_test.go index 538809aac4..3abd025bd0 100644 --- a/precompile/contracts/jurorv2/matching_validation_test.go +++ b/precompile/contracts/jurorv2/matching_validation_test.go @@ -26,7 +26,7 @@ func TestValidateLimitOrderLike(t *testing.T) { mockBibliophile := b.NewMockBibliophileClient(ctrl) trader := common.HexToAddress("0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC") - order := &ob.BaseOrder{ + order := &hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -106,7 +106,7 @@ func TestValidateLimitOrderLike(t *testing.T) { }) t.Run("Side=Short", func(t *testing.T) { - order := &ob.BaseOrder{ + order := &hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -184,7 +184,7 @@ func TestValidateLimitOrderLike(t *testing.T) { }) t.Run("invalid side", func(t *testing.T) { - order := &ob.BaseOrder{ + order := &hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -208,8 +208,8 @@ func TestValidateExecuteLimitOrder(t *testing.T) { marketAddress := common.HexToAddress("0xa72b463C21dA61cCc86069cFab82e9e8491152a0") trader := common.HexToAddress("0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC") - order := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(534), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -682,8 +682,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { defer ctrl.Finish() t.Run("invalid fillAmount", func(t *testing.T) { - order0 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order0 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -693,8 +693,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { }, PostOnly: false, } - order1 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order1 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -720,8 +720,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { }) t.Run("different amm", func(t *testing.T) { - order0 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order0 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -732,8 +732,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { PostOnly: false, } order0Hash, _ := order0.Hash() - order1 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order1 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(1), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -768,8 +768,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { }) t.Run("price mismatch", func(t *testing.T) { - order0 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order0 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -780,8 +780,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { PostOnly: false, } order0Hash, _ := order0.Hash() - order1 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order1 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -816,8 +816,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { }) t.Run("fillAmount not multiple", func(t *testing.T) { - order0 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order0 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -828,8 +828,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { PostOnly: false, } order0Hash, _ := order0.Hash() - order1 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order1 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -867,8 +867,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { }) t.Run("success", func(t *testing.T) { - order0 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order0 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -879,8 +879,8 @@ func TestValidateOrdersAndDetermineFillPrice(t *testing.T) { PostOnly: false, } order0Hash, _ := order0.Hash() - order1 := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order1 := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -976,8 +976,8 @@ func TestValidateLiquidationOrderAndDetermineFillPrice(t *testing.T) { defer ctrl.Finish() t.Run("invalid liquidationAmount", func(t *testing.T) { - order := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(10), @@ -1001,8 +1001,8 @@ func TestValidateLiquidationOrderAndDetermineFillPrice(t *testing.T) { }) t.Run("fillAmount not multiple", func(t *testing.T) { - order := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), @@ -1034,8 +1034,8 @@ func TestValidateLiquidationOrderAndDetermineFillPrice(t *testing.T) { }) t.Run("success", func(t *testing.T) { - order := &ob.LimitOrder{ - BaseOrder: ob.BaseOrder{ + order := &hu.LimitOrder{ + BaseOrder: hu.BaseOrder{ AmmIndex: big.NewInt(0), Trader: trader, BaseAssetQuantity: big.NewInt(-10), diff --git a/precompile/contracts/jurorv2/module.go b/precompile/contracts/jurorv2/module.go index d65d2bbf7c..00b8f75a1d 100644 --- a/precompile/contracts/jurorv2/module.go +++ b/precompile/contracts/jurorv2/module.go @@ -18,12 +18,12 @@ var _ contract.Configurator = &configurator{} // ConfigKey is the key used in json config files to specify this precompile precompileconfig. // must be unique across all precompiles. -const ConfigKey = "jurorConfig" +const ConfigKey = "jurorV2Config" // ContractAddress is the defined address of the precompile contract. // This should be unique across all precompile contracts. // See precompile/registry/registry.go for registered precompile contracts and more information. -var ContractAddress = common.HexToAddress("0x03000000000000000000000000000000000000a0") // SET A SUITABLE HEX ADDRESS HERE +var ContractAddress = common.HexToAddress("0x03000000000000000000000000000000000000a2") // SET A SUITABLE HEX ADDRESS HERE // Module is the precompile module. It is used to register the precompile contract. var Module = modules.Module{ diff --git a/precompile/registry/registry.go b/precompile/registry/registry.go index 0abc5174b1..2be8ef898d 100644 --- a/precompile/registry/registry.go +++ b/precompile/registry/registry.go @@ -47,6 +47,7 @@ import ( // ADD YOUR PRECOMPILE HERE // juror = common.HexToAddress("0x03000000000000000000000000000000000000a0") // ticks = common.HexToAddress("0x03000000000000000000000000000000000000a1") +// jurorV2 = common.HexToAddress("0x03000000000000000000000000000000000000a2") // GenesisAddress // OrderBook = common.HexToAddress("0x03000000000000000000000000000000000000b0") From 2ad89aa6f587d281c3a53de94ff67763430d805a Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Thu, 21 Dec 2023 21:25:39 +0000 Subject: [PATCH 06/12] trading_postOrder API works :tada: --- plugin/evm/orderbook/config_service.go | 8 -- .../orderbook/hubbleutils/data_structures.go | 98 +++++++++++---- plugin/evm/orderbook/hubbleutils/eip712.go | 112 ++++++++++++++++++ .../evm/orderbook/hubbleutils/hubble_math.go | 8 +- .../orderbook/hubbleutils/hubble_math_test.go | 16 +++ .../evm/orderbook/hubbleutils/validations.go | 12 +- plugin/evm/orderbook/memory_database.go | 18 ++- plugin/evm/orderbook/trading_apis.go | 35 ++++-- scripts/run_local.sh | 4 +- 9 files changed, 262 insertions(+), 49 deletions(-) create mode 100644 plugin/evm/orderbook/hubbleutils/eip712.go create mode 100644 plugin/evm/orderbook/hubbleutils/hubble_math_test.go diff --git a/plugin/evm/orderbook/config_service.go b/plugin/evm/orderbook/config_service.go index 3c525247f3..1a37884f36 100644 --- a/plugin/evm/orderbook/config_service.go +++ b/plugin/evm/orderbook/config_service.go @@ -77,14 +77,6 @@ func (cs *ConfigService) getStateAtCurrentBlock() *state.StateDB { return stateDB } -// func (cs *ConfigService) getStateAtBlock(number uint64) *state.StateDB { -// stateDB, err := cs.blockChain.StateAt(cs.blockChain.GetHeaderByNumber(number).Root) -// if err != nil { -// panic(err) -// } -// return stateDB -// } - func (cs *ConfigService) GetActiveMarketsCount() int64 { return bibliophile.GetActiveMarketsCount(cs.getStateAtCurrentBlock()) } diff --git a/plugin/evm/orderbook/hubbleutils/data_structures.go b/plugin/evm/orderbook/hubbleutils/data_structures.go index f6c41a949e..23a48d44ec 100644 --- a/plugin/evm/orderbook/hubbleutils/data_structures.go +++ b/plugin/evm/orderbook/hubbleutils/data_structures.go @@ -271,23 +271,85 @@ func (order *SignedOrder) EncodeToABI() ([]byte, error) { return encodedOrder, nil } +func (o *SignedOrder) UnmarshalJSON(data []byte) error { + // Alias types to avoid recursive call to UnmarshalJSON + // type AliasSignedOrder SignedOrder + // type AliasBaseOrder BaseOrder + // type AliasLimitOrder LimitOrder + + // Redefine the structs with simple types for JSON unmarshalling + aux := &struct { + AmmIndex uint64 `json:"ammIndex"` + Trader common.Address `json:"trader"` + BaseAssetQuantity string `json:"baseAssetQuantity"` + Price string `json:"price"` + Salt string `json:"salt"` + ReduceOnly bool `json:"reduceOnly"` + PostOnly bool `json:"postOnly"` + OrderType uint8 `json:"orderType"` + ExpireAt uint64 `json:"expireAt"` + Sig string `json:"sig"` + }{} + + // Perform the unmarshalling + if err := json.Unmarshal(data, aux); err != nil { + return err + } + + // Convert and assign the values to the original struct + o.AmmIndex = new(big.Int).SetUint64(aux.AmmIndex) + + o.Trader = aux.Trader + + o.BaseAssetQuantity = new(big.Int) + o.BaseAssetQuantity.SetString(aux.BaseAssetQuantity, 10) + + o.Price = new(big.Int) + o.Price.SetString(aux.Price, 10) + + o.Salt = new(big.Int) + o.Salt.SetBytes(common.FromHex(aux.Salt)) + + o.ReduceOnly = aux.ReduceOnly + o.PostOnly = aux.PostOnly + o.OrderType = aux.OrderType + + o.ExpireAt = new(big.Int).SetUint64(aux.ExpireAt) + o.Sig = common.FromHex(aux.Sig) + return nil +} + func (order *SignedOrder) DecodeFromRawOrder(rawOrder interface{}) { - marshalledOrder, _ := json.Marshal(rawOrder) - json.Unmarshal(marshalledOrder, &order) + // marshalledOrder, err := json.Marshal(rawOrder) + // if err != nil { + // fmt.Println(err) + // } + order_, ok := rawOrder.(string) + fmt.Println("ok", ok) + + orderJson := []byte(order_) + err := json.Unmarshal(orderJson, &order) + if err != nil { + fmt.Println("err in DecodeFromRawOrder") + fmt.Println(err) + } + // json.Unmarshal(marshalledOrder, &order) } -// func (order *SignedOrder) Map() map[string]interface{} { -// return map[string]interface{}{ -// "ammIndex": order.AmmIndex, -// "trader": order.Trader, -// "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), -// "price": utils.BigIntToFloat(order.Price, 6), -// "reduceOnly": order.ReduceOnly, -// "salt": order.Salt, -// "orderType": order.OrderType, -// "expireAt": order.ExpireAt, -// } -// } +func (order *SignedOrder) Map() map[string]interface{} { + return map[string]interface{}{ + "ammIndex": order.AmmIndex, + "trader": order.Trader, + "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), + "price": utils.BigIntToFloat(order.Price, 6), + "reduceOnly": order.ReduceOnly, + "postOnly": order.PostOnly, + "salt": order.Salt, + "orderType": order.OrderType, + "expireAt": order.ExpireAt, + "sig": order.Sig, + } +} func DecodeSignedOrder(encodedOrder []byte) (*SignedOrder, error) { orderType, err := getOrderType("signed") @@ -303,14 +365,6 @@ func DecodeSignedOrder(encodedOrder []byte) (*SignedOrder, error) { return signedOrder, nil } -func (order *SignedOrder) Hash() (hash common.Hash, err error) { - data, err := order.EncodeToABIWithoutType() - if err != nil { - return common.Hash{}, err - } - return common.BytesToHash(crypto.Keccak256(data)), nil -} - // ---------------------------------------------------------------------------- // Helper functions type DecodeStep struct { diff --git a/plugin/evm/orderbook/hubbleutils/eip712.go b/plugin/evm/orderbook/hubbleutils/eip712.go new file mode 100644 index 0000000000..61ec5e88de --- /dev/null +++ b/plugin/evm/orderbook/hubbleutils/eip712.go @@ -0,0 +1,112 @@ +package hubbleutils + +import ( + "fmt" + "strconv" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/signer/core/apitypes" +) + +func (o *SignedOrder) Hash() (hash common.Hash, err error) { + message := map[string]interface{}{ + "orderType": strconv.FormatUint(uint64(o.OrderType), 10), + "expireAt": o.ExpireAt.String(), + "ammIndex": o.AmmIndex.String(), + "trader": o.Trader.String(), + "baseAssetQuantity": o.BaseAssetQuantity.String(), + "price": o.Price.String(), + "salt": o.Salt.String(), + "reduceOnly": o.ReduceOnly, + "postOnly": o.PostOnly, + } + domain := apitypes.TypedDataDomain{ + Name: "Hubble", + Version: "2.0", + ChainId: math.NewHexOrDecimal256(321123), // @todo chain id from config + VerifyingContract: common.HexToAddress("0x9d4454B023096f34B160D6B654540c56A1F81688").String(), + } + typedData := apitypes.TypedData{ + Types: Eip712OrderTypes, + PrimaryType: "Order", + Domain: domain, + Message: message, + } + return EncodeForSigning(typedData) +} + +// EncodeForSigning - Encoding the typed data +func EncodeForSigning(typedData apitypes.TypedData) (hash common.Hash, err error) { + domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) + if err != nil { + return + } + typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) + if err != nil { + return + } + rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash))) + hash = common.BytesToHash(crypto.Keccak256(rawData)) + return +} + +var Eip712OrderTypes = apitypes.Types{ + "EIP712Domain": { + { + Name: "name", + Type: "string", + }, + { + Name: "version", + Type: "string", + }, + { + Name: "chainId", + Type: "uint256", + }, + { + Name: "verifyingContract", + Type: "address", + }, + }, + "Order": { // has to be same as the struct name or whatever was passed when building the typed hash + { + Name: "orderType", + Type: "uint8", + }, + { + Name: "expireAt", + Type: "uint256", + }, + { + Name: "ammIndex", + Type: "uint256", + }, + { + Name: "trader", + Type: "address", + }, + { + Name: "baseAssetQuantity", + Type: "int256", + }, + { + Name: "price", + Type: "uint256", + }, + { + Name: "salt", + Type: "uint256", + }, + { + Name: "reduceOnly", + Type: "bool", + }, + { + Name: "postOnly", + Type: "bool", + }, + }, +} diff --git a/plugin/evm/orderbook/hubbleutils/hubble_math.go b/plugin/evm/orderbook/hubbleutils/hubble_math.go index 49c6472969..1a9a42fe63 100644 --- a/plugin/evm/orderbook/hubbleutils/hubble_math.go +++ b/plugin/evm/orderbook/hubbleutils/hubble_math.go @@ -4,7 +4,7 @@ import ( "fmt" "math/big" - "github.com/ava-labs/subnet-evm/accounts" + // "github.com/ava-labs/subnet-evm/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" @@ -81,9 +81,11 @@ func ECRecover(data, sig hexutil.Bytes) (common.Address, error) { } sig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1 - rpk, err := crypto.SigToPub(accounts.TextHash(data), sig) + // rpk, err := crypto.SigToPub(accounts.TextHash(data), sig) + rpk, err := crypto.Ecrecover(data, sig) if err != nil { return common.Address{}, err } - return crypto.PubkeyToAddress(*rpk), nil + return common.BytesToAddress(common.LeftPadBytes(crypto.Keccak256(rpk[1:])[12:], 32)), nil + // return crypto.PubkeyToAddress(*rpk), nil } diff --git a/plugin/evm/orderbook/hubbleutils/hubble_math_test.go b/plugin/evm/orderbook/hubbleutils/hubble_math_test.go new file mode 100644 index 0000000000..9ca72fb8de --- /dev/null +++ b/plugin/evm/orderbook/hubbleutils/hubble_math_test.go @@ -0,0 +1,16 @@ +package hubbleutils + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" +) + +func TestECRecovers(t *testing.T) { + // 1. Test case from + orderHash := "0xc03560e0135777a3e7f155dbe7edbda36b217d6d9817f61ac914ed7f1029b387" + address, err := ECRecover(common.FromHex(orderHash), common.FromHex("0x4f47baaf7e2c447a3eaddb49e50908cea40811841077b2d0c53b7b496384c2ea783d1e53321e23b6f033750c46401b9e0705bde1b319bb461b55c752b9e980cc1c")) + assert.Nil(t, err) + assert.Equal(t, "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", address.String()) +} diff --git a/plugin/evm/orderbook/hubbleutils/validations.go b/plugin/evm/orderbook/hubbleutils/validations.go index 646fcaafa8..88a4e343f6 100644 --- a/plugin/evm/orderbook/hubbleutils/validations.go +++ b/plugin/evm/orderbook/hubbleutils/validations.go @@ -2,6 +2,7 @@ package hubbleutils import ( "errors" + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -88,7 +89,16 @@ func ValidateSignedOrder(order *SignedOrder, fields SignedOrderValidationFields) } // 6. caller will perform the check - signer, err = ECRecover(order.Sig, order.Sig) // @todo construct data + orderHash, err := order.Hash() + fmt.Println("orderHash", orderHash) + if err != nil { + return trader, signer, err + } + signer, err = ECRecover(orderHash.Bytes(), order.Sig) + fmt.Println("signer", signer) + if err != nil { + return trader, signer, err + } trader = order.Trader // assumes all markets are active and in sequential order diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index 27d6cd1284..e7bce980de 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -1186,10 +1186,22 @@ func (db *InMemoryDatabase) GetOrderValidationFields( trader common.Address, marketId int, ) OrderValidationFields { + posSize := big.NewInt(0) + if db.TraderMap[trader] != nil && db.TraderMap[trader].Positions[marketId] != nil && db.TraderMap[trader].Positions[marketId].Size != nil { + posSize = db.TraderMap[trader].Positions[marketId].Size + } + asksHead := big.NewInt(0) + if len(db.ShortOrders[marketId]) > 0 { + asksHead = db.ShortOrders[marketId][0].Price + } + bidsHead := big.NewInt(0) + if len(db.LongOrders[marketId]) > 0 { + bidsHead = db.LongOrders[marketId][0].Price + } fields := OrderValidationFields{ - PosSize: new(big.Int).Set(db.TraderMap[trader].Positions[marketId].Size), - AsksHead: db.ShortOrders[marketId][0].Price, - BidsHead: db.LongOrders[marketId][0].Price, + PosSize: posSize, + AsksHead: asksHead, + BidsHead: bidsHead, } if db.Orders[orderId] != nil { fields.Exists = true diff --git a/plugin/evm/orderbook/trading_apis.go b/plugin/evm/orderbook/trading_apis.go index dcc41698d7..40f16c34e9 100644 --- a/plugin/evm/orderbook/trading_apis.go +++ b/plugin/evm/orderbook/trading_apis.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "math/big" + "reflect" "strings" "time" @@ -313,18 +314,29 @@ func (api *TradingAPI) StreamMarketTrades(ctx context.Context, market Market, bl } type PlaceOrderResponse struct { - err error + Success bool `json:"success"` } -func (api *TradingAPI) PostOrder(ctx context.Context, order_ interface{}) PlaceOrderResponse { - order := order_.(*hu.SignedOrder) +func (api *TradingAPI) PostOrder(ctx context.Context, rawOrder interface{}) (PlaceOrderResponse, error) { + // print type of rawOrder + // order := rawOrder.(hu.SignedOrder) + + fmt.Println("type", reflect.TypeOf(rawOrder)) + + // order := rawOrder.(hu.SignedOrder) + + fmt.Println("rawOrder", rawOrder) + order := hu.SignedOrder{} + order.DecodeFromRawOrder(rawOrder) + fmt.Println("PostOrder", order) + marketId := int(order.AmmIndex.Int64()) orderId, err := order.Hash() if err != nil { - return PlaceOrderResponse{err: err} + return PlaceOrderResponse{Success: false}, err } trader, signer, err := hu.ValidateSignedOrder( - order, + &order, hu.SignedOrderValidationFields{ Now: uint64(time.Now().Unix()), ActiveMarketsCount: api.configService.GetActiveMarketsCount(), @@ -333,10 +345,12 @@ func (api *TradingAPI) PostOrder(ctx context.Context, order_ interface{}) PlaceO Status: api.configService.GetSignedOrderStatus(orderId), }, ) + if err != nil { + return PlaceOrderResponse{Success: false}, err + } - response := PlaceOrderResponse{} if trader != signer && !api.configService.IsTradingAuthority(trader, signer) { - return PlaceOrderResponse{err: hu.ErrNoTradingAuthority} + return PlaceOrderResponse{Success: false}, hu.ErrNoTradingAuthority } fields := api.db.GetOrderValidationFields(orderId, trader, marketId) @@ -350,7 +364,7 @@ func (api *TradingAPI) PostOrder(ctx context.Context, order_ interface{}) PlaceO asksHead := fields.AsksHead bidsHead := fields.BidsHead if (orderSide == hu.Side(hu.Short) && bidsHead.Sign() != 0 && order.Price.Cmp(bidsHead) != 1) || (orderSide == hu.Side(hu.Long) && asksHead.Sign() != 0 && order.Price.Cmp(asksHead) != -1) { - return PlaceOrderResponse{err: hu.ErrCrossingMarket} + return PlaceOrderResponse{Success: false}, hu.ErrCrossingMarket } } // @todo P5 @@ -366,12 +380,13 @@ func (api *TradingAPI) PostOrder(ctx context.Context, order_ interface{}) PlaceO BaseAssetQuantity: order.BaseAssetQuantity, FilledBaseAssetQuantity: big.NewInt(0), Price: order.Price, - RawOrder: order, + RawOrder: &order, Salt: order.Salt, ReduceOnly: order.ReduceOnly, BlockNumber: big.NewInt(0), OrderType: Limit, } api.db.Add(limitOrder) - return response + fmt.Println("success", "limitOrder", limitOrder) + return PlaceOrderResponse{Success: true}, nil } diff --git a/scripts/run_local.sh b/scripts/run_local.sh index 16ab8999fa..b81e20649b 100755 --- a/scripts/run_local.sh +++ b/scripts/run_local.sh @@ -19,8 +19,8 @@ then echo "31b571bf6894a248831ff937bb49f7754509fe93bbd2517c9c73c4144c0e97dc" > $FILE fi -avalanche subnet create hubblenet --force --custom --genesis genesis.json --custom-vm-path custom_evm.bin --custom-vm-branch main --custom-vm-build-script scripts/build.sh --custom-vm-repo-url https://github.com/hubble-exchange/hubblenet --config .avalanche-cli.json - +avalanche subnet create hubblenet --force --custom --genesis genesis.json --vm custom_evm.bin --config .avalanche-cli.json +# avalanche subnet create hubblenet --force --custom --genesis genesis.json --custom-vm-path custom_evm.bin --custom-vm-branch main --custom-vm-build-script scripts/build.sh --custom-vm-repo-url https://github.com/hubble-exchange/hubblenet --config .avalanche-cli.json # configure and add chain.json avalanche subnet configure hubblenet --chain-config chain.json --config .avalanche-cli.json avalanche subnet configure hubblenet --subnet-config subnet.json --config .avalanche-cli.json From a390e8195ecc42426da405bc7afa7abfdb92c972 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Sat, 23 Dec 2023 09:56:35 +0000 Subject: [PATCH 07/12] misc --- genesis.json | 6 +- .../orderbook/hubbleutils/data_structures.go | 135 +------------ plugin/evm/orderbook/hubbleutils/eip712.go | 29 --- .../orderbook/hubbleutils/signed_orders.go | 177 ++++++++++++++++++ .../hubbleutils/signed_orders_test.go | 124 ++++++++++++ plugin/evm/orderbook/trading_apis.go | 4 +- precompile/contracts/jurorv2/contract.go | 1 + .../jurorv2/matching_validation_test.go | 4 + 8 files changed, 314 insertions(+), 166 deletions(-) create mode 100644 plugin/evm/orderbook/hubbleutils/signed_orders.go create mode 100644 plugin/evm/orderbook/hubbleutils/signed_orders_test.go diff --git a/genesis.json b/genesis.json index 32ce4855a5..18c1184511 100644 --- a/genesis.json +++ b/genesis.json @@ -30,7 +30,7 @@ "blockTimestamp": 0, "adminAddresses": ["0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC","0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] }, - "jurorConfig": { + "jurorV2Config": { "blockTimestamp": 0 }, "ticksConfig": { @@ -78,6 +78,10 @@ "0x03000000000000000000000000000000000000b4": { "balance": "0x0", "code": "0x6080604052600436106100695760003560e01c80635c60da1b116100435780635c60da1b146100d35780638f28397014610111578063f851a4401461013157610078565b80632c6eefd5146100805780633659cfe6146100a05780634f1ef286146100c057610078565b3661007857610076610146565b005b610076610146565b34801561008c57600080fd5b5061007661009b3660046109e4565b610160565b3480156100ac57600080fd5b506100766100bb3660046109e4565b6101f8565b6100766100ce3660046109ff565b610256565b3480156100df57600080fd5b506100e86102e1565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561011d57600080fd5b5061007661012c3660046109e4565b610336565b34801561013d57600080fd5b506100e861037a565b61014e610407565b61015e6101596104f0565b6104fa565b565b600061016a61051e565b73ffffffffffffffffffffffffffffffffffffffff16146101ec576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f616c726561647920696e697469616c697a65640000000000000000000000000060448201526064015b60405180910390fd5b6101f581610528565b50565b610200610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561024e576101f5816040518060200160405280600081525060006105c9565b6101f5610146565b61025e610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102d9576102d48383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506105c9915050565b505050565b6102d4610146565b60006102eb610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561032b576103266104f0565b905090565b610333610146565b90565b61033e610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561024e576101f581610528565b6000610384610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561032b57610326610589565b60606103e48383604051806060016040528060278152602001610b1c602791396105f4565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b61040f610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561015e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f7879207461726760648201527f6574000000000000000000000000000000000000000000000000000000000000608482015260a4016101e3565b600061032661071c565b3660008037600080366000845af43d6000803e808015610519573d6000f35b3d6000fd5b6000610326610589565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610551610589565b6040805173ffffffffffffffffffffffffffffffffffffffff928316815291841660208301520160405180910390a16101f581610744565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b5473ffffffffffffffffffffffffffffffffffffffff16919050565b6105d283610850565b6000825111806105df5750805b156102d4576105ee83836103bf565b50505050565b606073ffffffffffffffffffffffffffffffffffffffff84163b61069a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016101e3565b6000808573ffffffffffffffffffffffffffffffffffffffff16856040516106c29190610aae565b600060405180830381855af49150503d80600081146106fd576040519150601f19603f3d011682016040523d82523d6000602084013e610702565b606091505b509150915061071282828661089d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6105ad565b73ffffffffffffffffffffffffffffffffffffffff81166107e7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016101e3565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9290921691909117905550565b610859816108f0565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b606083156108ac5750816103e4565b8251156108bc5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101e39190610aca565b73ffffffffffffffffffffffffffffffffffffffff81163b610994576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e74726163740000000000000000000000000000000000000060648201526084016101e3565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61080a565b803573ffffffffffffffffffffffffffffffffffffffff811681146109df57600080fd5b919050565b6000602082840312156109f657600080fd5b6103e4826109bb565b600080600060408486031215610a1457600080fd5b610a1d846109bb565b9250602084013567ffffffffffffffff80821115610a3a57600080fd5b818601915086601f830112610a4e57600080fd5b813581811115610a5d57600080fd5b876020828501011115610a6f57600080fd5b6020830194508093505050509250925092565b60005b83811015610a9d578181015183820152602001610a85565b838111156105ee5750506000910152565b60008251610ac0818460208701610a82565b9190910192915050565b6020815260008251806020840152610ae9816040850160208701610a82565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212208f0bc66b8ee51a4c109376b6c4d7e171cd933a56f45ed508f31a9c3b7aa9d4eb64736f6c63430008090033" + }, + "0x03000000000000000000000000000000000000b5": { + "balance": "0x0", + "code": "0x6080604052600436106100695760003560e01c80635c60da1b116100435780635c60da1b146100d35780638f28397014610111578063f851a4401461013157610078565b80632c6eefd5146100805780633659cfe6146100a05780634f1ef286146100c057610078565b3661007857610076610146565b005b610076610146565b34801561008c57600080fd5b5061007661009b3660046109e4565b610160565b3480156100ac57600080fd5b506100766100bb3660046109e4565b6101f8565b6100766100ce3660046109ff565b610256565b3480156100df57600080fd5b506100e86102e1565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b34801561011d57600080fd5b5061007661012c3660046109e4565b610336565b34801561013d57600080fd5b506100e861037a565b61014e610407565b61015e6101596104f0565b6104fa565b565b600061016a61051e565b73ffffffffffffffffffffffffffffffffffffffff16146101ec576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f616c726561647920696e697469616c697a65640000000000000000000000000060448201526064015b60405180910390fd5b6101f581610528565b50565b610200610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561024e576101f5816040518060200160405280600081525060006105c9565b6101f5610146565b61025e610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102d9576102d48383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506105c9915050565b505050565b6102d4610146565b60006102eb610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561032b576103266104f0565b905090565b610333610146565b90565b61033e610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561024e576101f581610528565b6000610384610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561032b57610326610589565b60606103e48383604051806060016040528060278152602001610b1c602791396105f4565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b61040f610589565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561015e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f7879207461726760648201527f6574000000000000000000000000000000000000000000000000000000000000608482015260a4016101e3565b600061032661071c565b3660008037600080366000845af43d6000803e808015610519573d6000f35b3d6000fd5b6000610326610589565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610551610589565b6040805173ffffffffffffffffffffffffffffffffffffffff928316815291841660208301520160405180910390a16101f581610744565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b5473ffffffffffffffffffffffffffffffffffffffff16919050565b6105d283610850565b6000825111806105df5750805b156102d4576105ee83836103bf565b50505050565b606073ffffffffffffffffffffffffffffffffffffffff84163b61069a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f60448201527f6e7472616374000000000000000000000000000000000000000000000000000060648201526084016101e3565b6000808573ffffffffffffffffffffffffffffffffffffffff16856040516106c29190610aae565b600060405180830381855af49150503d80600081146106fd576040519150601f19603f3d011682016040523d82523d6000602084013e610702565b606091505b509150915061071282828661089d565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6105ad565b73ffffffffffffffffffffffffffffffffffffffff81166107e7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016101e3565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9290921691909117905550565b610859816108f0565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b606083156108ac5750816103e4565b8251156108bc5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101e39190610aca565b73ffffffffffffffffffffffffffffffffffffffff81163b610994576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e74726163740000000000000000000000000000000000000060648201526084016101e3565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61080a565b803573ffffffffffffffffffffffffffffffffffffffff811681146109df57600080fd5b919050565b6000602082840312156109f657600080fd5b6103e4826109bb565b600080600060408486031215610a1457600080fd5b610a1d846109bb565b9250602084013567ffffffffffffffff80821115610a3a57600080fd5b818601915086601f830112610a4e57600080fd5b813581811115610a5d57600080fd5b876020828501011115610a6f57600080fd5b6020830194508093505050509250925092565b60005b83811015610a9d578181015183820152602001610a85565b838111156105ee5750506000910152565b60008251610ac0818460208701610a82565b9190910192915050565b6020815260008251806020840152610ae9816040850160208701610a82565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212208f0bc66b8ee51a4c109376b6c4d7e171cd933a56f45ed508f31a9c3b7aa9d4eb64736f6c63430008090033" } }, "nonce": "0x0", diff --git a/plugin/evm/orderbook/hubbleutils/data_structures.go b/plugin/evm/orderbook/hubbleutils/data_structures.go index 23a48d44ec..97aadd3c42 100644 --- a/plugin/evm/orderbook/hubbleutils/data_structures.go +++ b/plugin/evm/orderbook/hubbleutils/data_structures.go @@ -93,14 +93,6 @@ type IOCOrder struct { ExpireAt *big.Int `json:"expireAt"` } -// IOCOrder type is copy of IOCOrder struct defined in Orderbook contract -type SignedOrder struct { - LimitOrder - OrderType uint8 `json:"orderType"` - ExpireAt *big.Int `json:"expireAt"` - Sig []byte `json:"sig"` -} - // LimitOrder func (order *LimitOrder) EncodeToABIWithoutType() ([]byte, error) { limitOrderType, err := getOrderType("limit") @@ -239,132 +231,6 @@ func (order *IOCOrder) Hash() (hash common.Hash, err error) { return common.BytesToHash(crypto.Keccak256(data)), nil } -// ---------------------------------------------------------------------------- -// SignedOrder - -func (order *SignedOrder) EncodeToABIWithoutType() ([]byte, error) { - signedOrderType, err := getOrderType("signed") - if err != nil { - return nil, err - } - encodedOrder, err := abi.Arguments{{Type: signedOrderType}}.Pack(order) - if err != nil { - return nil, err - } - return encodedOrder, nil -} - -func (order *SignedOrder) EncodeToABI() ([]byte, error) { - encodedSignedOrder, err := order.EncodeToABIWithoutType() - if err != nil { - return nil, fmt.Errorf("failed getting abi type: %w", err) - } - - orderType, _ := abi.NewType("uint8", "uint8", nil) - orderBytesType, _ := abi.NewType("bytes", "bytes", nil) - // 1 means ordertype = IOC/market order - encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(Signed), encodedSignedOrder) - if err != nil { - return nil, fmt.Errorf("order encoding failed: %w", err) - } - - return encodedOrder, nil -} - -func (o *SignedOrder) UnmarshalJSON(data []byte) error { - // Alias types to avoid recursive call to UnmarshalJSON - // type AliasSignedOrder SignedOrder - // type AliasBaseOrder BaseOrder - // type AliasLimitOrder LimitOrder - - // Redefine the structs with simple types for JSON unmarshalling - aux := &struct { - AmmIndex uint64 `json:"ammIndex"` - Trader common.Address `json:"trader"` - BaseAssetQuantity string `json:"baseAssetQuantity"` - Price string `json:"price"` - Salt string `json:"salt"` - ReduceOnly bool `json:"reduceOnly"` - PostOnly bool `json:"postOnly"` - OrderType uint8 `json:"orderType"` - ExpireAt uint64 `json:"expireAt"` - Sig string `json:"sig"` - }{} - - // Perform the unmarshalling - if err := json.Unmarshal(data, aux); err != nil { - return err - } - - // Convert and assign the values to the original struct - o.AmmIndex = new(big.Int).SetUint64(aux.AmmIndex) - - o.Trader = aux.Trader - - o.BaseAssetQuantity = new(big.Int) - o.BaseAssetQuantity.SetString(aux.BaseAssetQuantity, 10) - - o.Price = new(big.Int) - o.Price.SetString(aux.Price, 10) - - o.Salt = new(big.Int) - o.Salt.SetBytes(common.FromHex(aux.Salt)) - - o.ReduceOnly = aux.ReduceOnly - o.PostOnly = aux.PostOnly - o.OrderType = aux.OrderType - - o.ExpireAt = new(big.Int).SetUint64(aux.ExpireAt) - o.Sig = common.FromHex(aux.Sig) - return nil -} - -func (order *SignedOrder) DecodeFromRawOrder(rawOrder interface{}) { - // marshalledOrder, err := json.Marshal(rawOrder) - // if err != nil { - // fmt.Println(err) - // } - order_, ok := rawOrder.(string) - fmt.Println("ok", ok) - - orderJson := []byte(order_) - err := json.Unmarshal(orderJson, &order) - if err != nil { - fmt.Println("err in DecodeFromRawOrder") - fmt.Println(err) - } - // json.Unmarshal(marshalledOrder, &order) -} - -func (order *SignedOrder) Map() map[string]interface{} { - return map[string]interface{}{ - "ammIndex": order.AmmIndex, - "trader": order.Trader, - "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), - "price": utils.BigIntToFloat(order.Price, 6), - "reduceOnly": order.ReduceOnly, - "postOnly": order.PostOnly, - "salt": order.Salt, - "orderType": order.OrderType, - "expireAt": order.ExpireAt, - "sig": order.Sig, - } -} - -func DecodeSignedOrder(encodedOrder []byte) (*SignedOrder, error) { - orderType, err := getOrderType("signed") - if err != nil { - return nil, fmt.Errorf("failed getting abi type: %w", err) - } - order, err := abi.Arguments{{Type: orderType}}.Unpack(encodedOrder) - if err != nil { - return nil, err - } - signedOrder := &SignedOrder{} - signedOrder.DecodeFromRawOrder(order[0]) - return signedOrder, nil -} - // ---------------------------------------------------------------------------- // Helper functions type DecodeStep struct { @@ -420,6 +286,7 @@ func getOrderType(orderType string) (abi.Type, error) { {Name: "salt", Type: "uint256"}, {Name: "reduceOnly", Type: "bool"}, {Name: "postOnly", Type: "bool"}, + {Name: "sig", Type: "string"}, }) } return abi.Type{}, fmt.Errorf("invalid order type") diff --git a/plugin/evm/orderbook/hubbleutils/eip712.go b/plugin/evm/orderbook/hubbleutils/eip712.go index 61ec5e88de..b444484375 100644 --- a/plugin/evm/orderbook/hubbleutils/eip712.go +++ b/plugin/evm/orderbook/hubbleutils/eip712.go @@ -2,41 +2,12 @@ package hubbleutils import ( "fmt" - "strconv" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/signer/core/apitypes" ) -func (o *SignedOrder) Hash() (hash common.Hash, err error) { - message := map[string]interface{}{ - "orderType": strconv.FormatUint(uint64(o.OrderType), 10), - "expireAt": o.ExpireAt.String(), - "ammIndex": o.AmmIndex.String(), - "trader": o.Trader.String(), - "baseAssetQuantity": o.BaseAssetQuantity.String(), - "price": o.Price.String(), - "salt": o.Salt.String(), - "reduceOnly": o.ReduceOnly, - "postOnly": o.PostOnly, - } - domain := apitypes.TypedDataDomain{ - Name: "Hubble", - Version: "2.0", - ChainId: math.NewHexOrDecimal256(321123), // @todo chain id from config - VerifyingContract: common.HexToAddress("0x9d4454B023096f34B160D6B654540c56A1F81688").String(), - } - typedData := apitypes.TypedData{ - Types: Eip712OrderTypes, - PrimaryType: "Order", - Domain: domain, - Message: message, - } - return EncodeForSigning(typedData) -} - // EncodeForSigning - Encoding the typed data func EncodeForSigning(typedData apitypes.TypedData) (hash common.Hash, err error) { domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) diff --git a/plugin/evm/orderbook/hubbleutils/signed_orders.go b/plugin/evm/orderbook/hubbleutils/signed_orders.go new file mode 100644 index 0000000000..af20a275a6 --- /dev/null +++ b/plugin/evm/orderbook/hubbleutils/signed_orders.go @@ -0,0 +1,177 @@ +package hubbleutils + +import ( + "encoding/json" + "fmt" + "math/big" + "strconv" + + "github.com/ava-labs/subnet-evm/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/signer/core/apitypes" +) + +// func getSignedOrderBookAddress() common.Address { +// if +// } + +type SignedOrder struct { + LimitOrder + OrderType uint8 `json:"orderType"` + ExpireAt *big.Int `json:"expireAt"` + Sig []byte `json:"sig"` +} + +func (order *SignedOrder) EncodeToABIWithoutType() ([]byte, error) { + signedOrderType, err := getOrderType("signed") + if err != nil { + return nil, err + } + encodedOrder, err := abi.Arguments{{Type: signedOrderType}}.Pack(order) + if err != nil { + return nil, err + } + return encodedOrder, nil +} + +func (order *SignedOrder) EncodeToABI() ([]byte, error) { + encodedSignedOrder, err := order.EncodeToABIWithoutType() + if err != nil { + return nil, fmt.Errorf("failed getting abi type: %w", err) + } + + orderType, _ := abi.NewType("uint8", "uint8", nil) + orderBytesType, _ := abi.NewType("bytes", "bytes", nil) + encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(Signed), encodedSignedOrder) + if err != nil { + return nil, fmt.Errorf("order encoding failed: %w", err) + } + + return encodedOrder, nil +} + +func (order *SignedOrder) DecodeFromRawOrder(rawOrder interface{}) { + marshalledOrder, _ := json.Marshal(rawOrder) + fmt.Println("marshalledOrder", string(marshalledOrder)) + err := json.Unmarshal(marshalledOrder, &order) + if err != nil { + fmt.Println("err in DecodeFromRawOrder") + fmt.Println(err) + } +} + +// func (order *SignedOrder) Map() map[string]interface{} { +// return map[string]interface{}{ +// "ammIndex": order.AmmIndex, +// "trader": order.Trader, +// "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), +// "price": utils.BigIntToFloat(order.Price, 6), +// "reduceOnly": order.ReduceOnly, +// "postOnly": order.PostOnly, +// "salt": order.Salt, +// "orderType": order.OrderType, +// "expireAt": order.ExpireAt, +// "sig": order.Sig, +// } +// } + +func DecodeSignedOrder(encodedOrder []byte) (*SignedOrder, error) { + orderType, err := getOrderType("signed") + if err != nil { + return nil, fmt.Errorf("failed getting abi type: %w", err) + } + order, err := abi.Arguments{{Type: orderType}}.Unpack(encodedOrder) + if err != nil { + return nil, err + } + signedOrder := &SignedOrder{} + signedOrder.DecodeFromRawOrder(order[0]) + return signedOrder, nil +} + +func (o *SignedOrder) Hash(verifyingContract common.Address) (hash common.Hash, err error) { + message := map[string]interface{}{ + "orderType": strconv.FormatUint(uint64(o.OrderType), 10), + "expireAt": o.ExpireAt.String(), + "ammIndex": o.AmmIndex.String(), + "trader": o.Trader.String(), + "baseAssetQuantity": o.BaseAssetQuantity.String(), + "price": o.Price.String(), + "salt": o.Salt.String(), + "reduceOnly": o.ReduceOnly, + "postOnly": o.PostOnly, + } + domain := apitypes.TypedDataDomain{ + Name: "Hubble", + Version: "2.0", + ChainId: math.NewHexOrDecimal256(321123), // @todo chain id from config + VerifyingContract: common.HexToAddress("0x809d550fca64d94Bd9F66E60752A544199cfAC3D").String(), + } + typedData := apitypes.TypedData{ + Types: Eip712OrderTypes, + PrimaryType: "Order", + Domain: domain, + Message: message, + } + return EncodeForSigning(typedData) +} + +// Trading API methods + +func (o *SignedOrder) UnmarshalJSON(data []byte) error { + // Redefine the structs with simple types for JSON unmarshalling + aux := &struct { + AmmIndex uint64 `json:"ammIndex"` + Trader common.Address `json:"trader"` + BaseAssetQuantity string `json:"baseAssetQuantity"` + Price string `json:"price"` + Salt string `json:"salt"` + ReduceOnly bool `json:"reduceOnly"` + PostOnly bool `json:"postOnly"` + OrderType uint8 `json:"orderType"` + ExpireAt uint64 `json:"expireAt"` + Sig string `json:"sig"` + }{} + + // Perform the unmarshalling + if err := json.Unmarshal(data, aux); err != nil { + return err + } + + // Convert and assign the values to the original struct + o.AmmIndex = new(big.Int).SetUint64(aux.AmmIndex) + + o.Trader = aux.Trader + + o.BaseAssetQuantity = new(big.Int) + o.BaseAssetQuantity.SetString(aux.BaseAssetQuantity, 10) + + o.Price = new(big.Int) + o.Price.SetString(aux.Price, 10) + + o.Salt = new(big.Int) + o.Salt.SetBytes(common.FromHex(aux.Salt)) + + o.ReduceOnly = aux.ReduceOnly + o.PostOnly = aux.PostOnly + o.OrderType = aux.OrderType + + o.ExpireAt = new(big.Int).SetUint64(aux.ExpireAt) + o.Sig = common.FromHex(aux.Sig) + return nil +} + +func (order *SignedOrder) DecodeAPIOrder(rawOrder interface{}) error { + order_, ok := rawOrder.(string) + if !ok { + fmt.Println("invalid data format") + } + + orderJson := []byte(order_) + err := json.Unmarshal(orderJson, &order) + if err != nil { + return err + } + return nil +} diff --git a/plugin/evm/orderbook/hubbleutils/signed_orders_test.go b/plugin/evm/orderbook/hubbleutils/signed_orders_test.go new file mode 100644 index 0000000000..a2e1983dd6 --- /dev/null +++ b/plugin/evm/orderbook/hubbleutils/signed_orders_test.go @@ -0,0 +1,124 @@ +package hubbleutils + +import ( + "encoding/hex" + "fmt" + "math/big" + + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" +) + +func TestDecodeSignedOrder(t *testing.T) { + // t.Run("long order", func(t *testing.T) { + // order := &SignedOrder{ + // LimitOrder: LimitOrder{ + // BaseOrder: BaseOrder{ + // AmmIndex: big.NewInt(0), + // Trader: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), + // BaseAssetQuantity: big.NewInt(5000000000000000000), + // Price: big.NewInt(1000000000), + // Salt: big.NewInt(1688994806105), + // ReduceOnly: false, + // }, + // }, + // OrderType: 2, + // ExpireAt: big.NewInt(1688994854), + // Sig: []byte("0x00"), + // } + // h, err := order.Hash() + // assert.Nil(t, err) + // assert.Equal(t, "0xc989b9a5bf196036dbbae61f56179f31172cc04aa91238bc1b7c828bebf0fe5e", h.Hex()) + + // typeEncodedOrder := strings.TrimPrefix("0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000004563918244f40000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef79590000000000000000000000000000000000000000000000000000000000000000", "0x") + // encodedOrder := strings.TrimPrefix("0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000004563918244f40000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef79590000000000000000000000000000000000000000000000000000000000000000", "0x") + // b, err := order.EncodeToABI() + // assert.Nil(t, err) + // assert.Equal(t, typeEncodedOrder, hex.EncodeToString(b)) + // testDecodeTypeAndEncodedSignedOrder(t, typeEncodedOrder, encodedOrder, IOC, order) + // }) + + t.Run("short order", func(t *testing.T) { + // sig, err := hex.DecodeString() + // assert.Nil(t, err) + sig := "b2704b73b99f2700ecc90a218f514c254d1f5d46af47117f5317f6cc0348ce962dcfb024c7264fdeb1f1513e4564c2a7cd9c1d0be33d7b934cd5a73b96440eaf1c" + order := &SignedOrder{ + LimitOrder: LimitOrder{ + BaseOrder: BaseOrder{ + AmmIndex: big.NewInt(0), + Trader: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), + BaseAssetQuantity: big.NewInt(-5000000000000000000), + Price: big.NewInt(1000000000), + Salt: big.NewInt(1688994806105), + ReduceOnly: false, + }, + PostOnly: true, + }, + OrderType: 2, + ExpireAt: big.NewInt(1688994854), + Sig: sig, + } + h, err := order.Hash() + assert.Nil(t, err) + assert.Equal(t, "0xee4b26ae386d1c88f89eb2f8b4b4205271576742f5ff4e0488633612f7a9a5e7", h.Hex()) + + // typeEncodedOrder := strings.TrimPrefix("0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c0000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef795900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0x") + // encodedOrder := strings.TrimPrefix("0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c0000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef795900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0x") + + // with sig + // typeEncodedOrder := strings.TrimPrefix("0x0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c0000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef79590000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000041b2704b73b99f2700ecc90a218f514c254d1f5d46af47117f5317f6cc0348ce962dcfb024c7264fdeb1f1513e4564c2a7cd9c1d0be33d7b934cd5a73b96440eaf1c00000000000000000000000000000000000000000000000000000000000000", "0x") + // encodedOrder := strings.TrimPrefix("0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c0000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef79590000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000041b2704b73b99f2700ecc90a218f514c254d1f5d46af47117f5317f6cc0348ce962dcfb024c7264fdeb1f1513e4564c2a7cd9c1d0be33d7b934cd5a73b96440eaf1c00000000000000000000000000000000000000000000000000000000000000", "0x") + b, err := order.EncodeToABIWithoutType() + fmt.Println("here 0", hex.EncodeToString(b), err) + assert.NoError(t, err) + result, err := DecodeSignedOrder(b) + assert.NoError(t, err) + fmt.Println("here 1", result, err) + assertSignedOrderEquality(t, order, result) + // b, err := order.EncodeToABI() + // assert.Nil(t, err) + // assert.Equal(t, typeEncodedOrder, hex.EncodeToString(b)) + // testDecodeTypeAndEncodedSignedOrder(t, typeEncodedOrder, encodedOrder, Signed, order) + }) +} + +func testDecodeTypeAndEncodedSignedOrder(t *testing.T, typedEncodedOrder string, encodedOrder string, orderType OrderType, expectedOutput *SignedOrder) { + testData, err := hex.DecodeString(typedEncodedOrder) + assert.Nil(t, err) + + decodeStep, err := DecodeTypeAndEncodedOrder(testData) + assert.Nil(t, err) + + assert.Equal(t, orderType, decodeStep.OrderType) + assert.Equal(t, encodedOrder, hex.EncodeToString(decodeStep.EncodedOrder)) + // yo, err := hex.DecodeString(encodedOrder) + assert.Nil(t, err) + // testDecodeSignedOrder(t, yo, expectedOutput) + testDecodeSignedOrder(t, decodeStep.EncodedOrder, expectedOutput) +} + +func testDecodeSignedOrder(t *testing.T, encodedOrder []byte, expectedOutput *SignedOrder) { + result, err := DecodeSignedOrder(encodedOrder) + assert.NoError(t, err) + fmt.Println("here", result, err) + assert.NotNil(t, result) + assertSignedOrderEquality(t, expectedOutput, result) +} + +func assertSignedOrderEquality(t *testing.T, expected, actual *SignedOrder) { + assert.Equal(t, expected.OrderType, actual.OrderType) + assert.Equal(t, expected.ExpireAt.Int64(), actual.ExpireAt.Int64()) + assert.Equal(t, expected.Sig, actual.Sig) + assertLimitOrderEquality(t, expected.BaseOrder, actual.BaseOrder) +} + +func assertLimitOrderEquality(t *testing.T, expected, actual BaseOrder) { + assert.Equal(t, expected.AmmIndex.Int64(), actual.AmmIndex.Int64()) + assert.Equal(t, expected.Trader, actual.Trader) + assert.Equal(t, expected.BaseAssetQuantity, actual.BaseAssetQuantity) + assert.Equal(t, expected.Price, actual.Price) + assert.Equal(t, expected.Salt, actual.Salt) + assert.Equal(t, expected.ReduceOnly, actual.ReduceOnly) +} diff --git a/plugin/evm/orderbook/trading_apis.go b/plugin/evm/orderbook/trading_apis.go index 40f16c34e9..aef8e1b859 100644 --- a/plugin/evm/orderbook/trading_apis.go +++ b/plugin/evm/orderbook/trading_apis.go @@ -327,11 +327,11 @@ func (api *TradingAPI) PostOrder(ctx context.Context, rawOrder interface{}) (Pla fmt.Println("rawOrder", rawOrder) order := hu.SignedOrder{} - order.DecodeFromRawOrder(rawOrder) + order.DecodeAPIOrder(rawOrder) fmt.Println("PostOrder", order) marketId := int(order.AmmIndex.Int64()) - orderId, err := order.Hash() + orderId, err := order.Hash(api.backend.ChainConfig().ChainID, api.configService.GetVerifyingContract()) if err != nil { return PlaceOrderResponse{Success: false}, err } diff --git a/precompile/contracts/jurorv2/contract.go b/precompile/contracts/jurorv2/contract.go index 6c24aa60e5..13a074530f 100644 --- a/precompile/contracts/jurorv2/contract.go +++ b/precompile/contracts/jurorv2/contract.go @@ -214,6 +214,7 @@ func getNotionalPositionAndMargin(accessibleState contract.AccessibleState, call // CUSTOM CODE STARTS HERE bibliophile := bibliophile.NewBibliophileClient(accessibleState) + accessibleState.GetSnowContext().ChainID output := GetNotionalPositionAndMargin(bibliophile, &inputStruct) packedOutput, err := PackGetNotionalPositionAndMarginOutput(output) if err != nil { diff --git a/precompile/contracts/jurorv2/matching_validation_test.go b/precompile/contracts/jurorv2/matching_validation_test.go index 3abd025bd0..400ed71b68 100644 --- a/precompile/contracts/jurorv2/matching_validation_test.go +++ b/precompile/contracts/jurorv2/matching_validation_test.go @@ -200,6 +200,10 @@ func TestValidateLimitOrderLike(t *testing.T) { }) } +func TestValidateExecuteSignedOrder(t *testing.T) { + +} + func TestValidateExecuteLimitOrder(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() From 1ae038ac9eebb9bbb67b6884681f6a56b7f215dc Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Tue, 26 Dec 2023 08:45:48 +0000 Subject: [PATCH 08/12] Matching works! --- genesis.json | 4 +- plugin/evm/orderbook/config_service.go | 4 + .../orderbook/contract_events_processor.go | 4 +- .../orderbook/hubbleutils/data_structures.go | 8 +- .../evm/orderbook/hubbleutils/hubble_math.go | 7 +- .../orderbook/hubbleutils/hubble_math_test.go | 4 +- .../orderbook/hubbleutils/signed_orders.go | 154 ++++++++++-------- .../hubbleutils/signed_orders_test.go | 54 +++--- .../evm/orderbook/hubbleutils/validations.go | 8 +- plugin/evm/orderbook/matching_pipeline.go | 5 +- plugin/evm/orderbook/memory_database.go | 39 +++-- plugin/evm/orderbook/trading_apis.go | 36 ++-- plugin/evm/orderbook/tx_processor.go | 2 + .../bibliophile/signed_order_book.go | 9 +- precompile/contracts/jurorv2/contract.go | 3 +- .../contracts/jurorv2/matching_validation.go | 5 +- 16 files changed, 203 insertions(+), 143 deletions(-) diff --git a/genesis.json b/genesis.json index 18c1184511..c7e854712a 100644 --- a/genesis.json +++ b/genesis.json @@ -15,12 +15,12 @@ "feeConfig": { "gasLimit": 500000000, "targetBlockRate": 1, - "minBaseFee": 60000000000, + "minBaseFee": 30000000000, "targetGas": 10000000, "baseFeeChangeDenominator": 50, "minBlockGasCost": 0, "maxBlockGasCost": 5000000, - "blockGasCostStep": 10000 + "blockGasCostStep": 0 }, "contractNativeMinterConfig": { "blockTimestamp": 0, diff --git a/plugin/evm/orderbook/config_service.go b/plugin/evm/orderbook/config_service.go index 1a37884f36..4ef0505998 100644 --- a/plugin/evm/orderbook/config_service.go +++ b/plugin/evm/orderbook/config_service.go @@ -110,3 +110,7 @@ func (cs *ConfigService) GetSignedOrderStatus(orderHash common.Hash) int64 { func (cs *ConfigService) IsTradingAuthority(trader, signer common.Address) bool { return bibliophile.IsTradingAuthority(cs.getStateAtCurrentBlock(), trader, signer) } + +func (cs *ConfigService) GetChainIdAndSignedOrderbookContract() (*big.Int, common.Address) { + return cs.blockChain.Config().ChainID, bibliophile.GetSignedOrderBookAddress(cs.getStateAtCurrentBlock()) +} diff --git a/plugin/evm/orderbook/contract_events_processor.go b/plugin/evm/orderbook/contract_events_processor.go index c43b9df865..777bc7b5bc 100644 --- a/plugin/evm/orderbook/contract_events_processor.go +++ b/plugin/evm/orderbook/contract_events_processor.go @@ -327,9 +327,9 @@ func (cep *ContractEventsProcessor) handleIOCOrderBookEvent(event *types.Log) { orderId := event.Topics[2] order := args["order"] if !removed { - log.Info("IOCOrder/OrderRejected", "orderId", orderId.String(), "number", event.BlockNumber, "order", order) + log.Info("IOCOrder/OrderRejected", "orderId", orderId.String(), "number", event.BlockNumber, "order", order, "err", args["err"]) } else { - log.Info("IOCOrder/OrderRejected removed", "orderId", orderId.String(), "number", event.BlockNumber, "order", order) + log.Info("IOCOrder/OrderRejected removed", "orderId", orderId.String(), "number", event.BlockNumber, "order", order, args["err"]) } } } diff --git a/plugin/evm/orderbook/hubbleutils/data_structures.go b/plugin/evm/orderbook/hubbleutils/data_structures.go index 97aadd3c42..0b441b842c 100644 --- a/plugin/evm/orderbook/hubbleutils/data_structures.go +++ b/plugin/evm/orderbook/hubbleutils/data_structures.go @@ -68,7 +68,7 @@ const ( ) func (o OrderType) String() string { - return [...]string{"limit", "ioc"}[o] + return [...]string{"limit", "ioc", "signed"}[o] } type BaseOrder struct { @@ -160,6 +160,11 @@ func (order *LimitOrder) Hash() (common.Hash, error) { return common.BytesToHash(crypto.Keccak256(data)), nil } +func (o *LimitOrder) String() string { + return fmt.Sprintf("LimitOrder{AmmIndex: %v, Trader: %v, BaseAssetQuantity: %v, Price: %v, Salt: %v, ReduceOnly: %v, PostOnly: %v}", + o.AmmIndex, o.Trader, o.BaseAssetQuantity, o.Price, o.Salt, o.ReduceOnly, o.PostOnly) +} + // ---------------------------------------------------------------------------- // IOCOrder @@ -286,7 +291,6 @@ func getOrderType(orderType string) (abi.Type, error) { {Name: "salt", Type: "uint256"}, {Name: "reduceOnly", Type: "bool"}, {Name: "postOnly", Type: "bool"}, - {Name: "sig", Type: "string"}, }) } return abi.Type{}, fmt.Errorf("invalid order type") diff --git a/plugin/evm/orderbook/hubbleutils/hubble_math.go b/plugin/evm/orderbook/hubbleutils/hubble_math.go index 1a9a42fe63..9dc552af98 100644 --- a/plugin/evm/orderbook/hubbleutils/hubble_math.go +++ b/plugin/evm/orderbook/hubbleutils/hubble_math.go @@ -72,7 +72,10 @@ func Unscale(a *big.Int, decimals uint8) *big.Int { return Div(a, new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimals)), nil)) } -func ECRecover(data, sig hexutil.Bytes) (common.Address, error) { +func ECRecover(data, sign hexutil.Bytes) (common.Address, error) { + sig := make([]byte, len(sign)) + copy(sig, sign) + if len(sig) != crypto.SignatureLength { return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength) } @@ -81,11 +84,9 @@ func ECRecover(data, sig hexutil.Bytes) (common.Address, error) { } sig[crypto.RecoveryIDOffset] -= 27 // Transform yellow paper V from 27/28 to 0/1 - // rpk, err := crypto.SigToPub(accounts.TextHash(data), sig) rpk, err := crypto.Ecrecover(data, sig) if err != nil { return common.Address{}, err } return common.BytesToAddress(common.LeftPadBytes(crypto.Keccak256(rpk[1:])[12:], 32)), nil - // return crypto.PubkeyToAddress(*rpk), nil } diff --git a/plugin/evm/orderbook/hubbleutils/hubble_math_test.go b/plugin/evm/orderbook/hubbleutils/hubble_math_test.go index 9ca72fb8de..14c8cb9d59 100644 --- a/plugin/evm/orderbook/hubbleutils/hubble_math_test.go +++ b/plugin/evm/orderbook/hubbleutils/hubble_math_test.go @@ -9,8 +9,8 @@ import ( func TestECRecovers(t *testing.T) { // 1. Test case from - orderHash := "0xc03560e0135777a3e7f155dbe7edbda36b217d6d9817f61ac914ed7f1029b387" - address, err := ECRecover(common.FromHex(orderHash), common.FromHex("0x4f47baaf7e2c447a3eaddb49e50908cea40811841077b2d0c53b7b496384c2ea783d1e53321e23b6f033750c46401b9e0705bde1b319bb461b55c752b9e980cc1c")) + orderHash := "0xee4b26ae386d1c88f89eb2f8b4b4205271576742f5ff4e0488633612f7a9a5e7" + address, err := ECRecover(common.FromHex(orderHash), common.FromHex("0xb2704b73b99f2700ecc90a218f514c254d1f5d46af47117f5317f6cc0348ce962dcfb024c7264fdeb1f1513e4564c2a7cd9c1d0be33d7b934cd5a73b96440eaf1c")) assert.Nil(t, err) assert.Equal(t, "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", address.String()) } diff --git a/plugin/evm/orderbook/hubbleutils/signed_orders.go b/plugin/evm/orderbook/hubbleutils/signed_orders.go index af20a275a6..b5f425b73b 100644 --- a/plugin/evm/orderbook/hubbleutils/signed_orders.go +++ b/plugin/evm/orderbook/hubbleutils/signed_orders.go @@ -1,21 +1,19 @@ package hubbleutils import ( + "encoding/hex" "encoding/json" "fmt" "math/big" "strconv" "github.com/ava-labs/subnet-evm/accounts/abi" + // "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/signer/core/apitypes" ) -// func getSignedOrderBookAddress() common.Address { -// if -// } - type SignedOrder struct { LimitOrder OrderType uint8 `json:"orderType"` @@ -28,7 +26,8 @@ func (order *SignedOrder) EncodeToABIWithoutType() ([]byte, error) { if err != nil { return nil, err } - encodedOrder, err := abi.Arguments{{Type: signedOrderType}}.Pack(order) + bytesTy, _ := abi.NewType("bytes", "bytes", nil) + encodedOrder, err := abi.Arguments{{Type: signedOrderType}, {Type: bytesTy}}.Pack(order, order.Sig) if err != nil { return nil, err } @@ -51,9 +50,26 @@ func (order *SignedOrder) EncodeToABI() ([]byte, error) { return encodedOrder, nil } +func DecodeSignedOrder(encodedOrder []byte) (*SignedOrder, error) { + signedOrderType, err := getOrderType("signed") + if err != nil { + return nil, fmt.Errorf("failed getting abi type: %w", err) + } + bytesTy, _ := abi.NewType("bytes", "bytes", nil) + decodedValues, err := abi.Arguments{{Type: signedOrderType}, {Type: bytesTy}}.Unpack(encodedOrder) + if err != nil { + return nil, err + } + signedOrder := &SignedOrder{ + Sig: decodedValues[1].([]byte), + } + signedOrder.DecodeFromRawOrder(decodedValues[0]) + return signedOrder, nil +} + func (order *SignedOrder) DecodeFromRawOrder(rawOrder interface{}) { marshalledOrder, _ := json.Marshal(rawOrder) - fmt.Println("marshalledOrder", string(marshalledOrder)) + // fmt.Println("marshalledOrder", string(marshalledOrder)) err := json.Unmarshal(marshalledOrder, &order) if err != nil { fmt.Println("err in DecodeFromRawOrder") @@ -76,21 +92,17 @@ func (order *SignedOrder) DecodeFromRawOrder(rawOrder interface{}) { // } // } -func DecodeSignedOrder(encodedOrder []byte) (*SignedOrder, error) { - orderType, err := getOrderType("signed") - if err != nil { - return nil, fmt.Errorf("failed getting abi type: %w", err) - } - order, err := abi.Arguments{{Type: orderType}}.Unpack(encodedOrder) - if err != nil { - return nil, err - } - signedOrder := &SignedOrder{} - signedOrder.DecodeFromRawOrder(order[0]) - return signedOrder, nil +func (o *SignedOrder) String() string { + return fmt.Sprintf( + "Order %s, OrderType: %d, ExpireAt: %d, Sig: %s", + o.LimitOrder.String(), + o.OrderType, + o.ExpireAt, + hex.EncodeToString(o.Sig), + ) } -func (o *SignedOrder) Hash(verifyingContract common.Address) (hash common.Hash, err error) { +func (o *SignedOrder) Hash() (hash common.Hash, err error) { message := map[string]interface{}{ "orderType": strconv.FormatUint(uint64(o.OrderType), 10), "expireAt": o.ExpireAt.String(), @@ -103,10 +115,12 @@ func (o *SignedOrder) Hash(verifyingContract common.Address) (hash common.Hash, "postOnly": o.PostOnly, } domain := apitypes.TypedDataDomain{ - Name: "Hubble", - Version: "2.0", - ChainId: math.NewHexOrDecimal256(321123), // @todo chain id from config - VerifyingContract: common.HexToAddress("0x809d550fca64d94Bd9F66E60752A544199cfAC3D").String(), + Name: "Hubble", + Version: "2.0", + ChainId: math.NewHexOrDecimal256(321123), // @todo chain id from config + // @todo use the correct address + // VerifyingContract: common.HexToAddress("0x809d550fca64d94Bd9F66E60752A544199cfAC3D").String(), // used for unit tests + VerifyingContract: common.HexToAddress("0x5eb3Bc0a489C5A8288765d2336659EbCA68FCd00").String(), // from benchmarks scripts } typedData := apitypes.TypedData{ Types: Eip712OrderTypes, @@ -119,59 +133,59 @@ func (o *SignedOrder) Hash(verifyingContract common.Address) (hash common.Hash, // Trading API methods -func (o *SignedOrder) UnmarshalJSON(data []byte) error { - // Redefine the structs with simple types for JSON unmarshalling - aux := &struct { - AmmIndex uint64 `json:"ammIndex"` - Trader common.Address `json:"trader"` - BaseAssetQuantity string `json:"baseAssetQuantity"` - Price string `json:"price"` - Salt string `json:"salt"` - ReduceOnly bool `json:"reduceOnly"` - PostOnly bool `json:"postOnly"` - OrderType uint8 `json:"orderType"` - ExpireAt uint64 `json:"expireAt"` - Sig string `json:"sig"` - }{} - - // Perform the unmarshalling - if err := json.Unmarshal(data, aux); err != nil { - return err - } +// func (o *SignedOrder) UnmarshalJSON(data []byte) error { +// // Redefine the structs with simple types for JSON unmarshalling +// aux := &struct { +// AmmIndex uint64 `json:"ammIndex"` +// Trader common.Address `json:"trader"` +// BaseAssetQuantity string `json:"baseAssetQuantity"` +// Price string `json:"price"` +// Salt string `json:"salt"` +// ReduceOnly bool `json:"reduceOnly"` +// PostOnly bool `json:"postOnly"` +// OrderType uint8 `json:"orderType"` +// ExpireAt uint64 `json:"expireAt"` +// Sig string `json:"sig"` +// }{} + +// // Perform the unmarshalling +// if err := json.Unmarshal(data, aux); err != nil { +// return err +// } - // Convert and assign the values to the original struct - o.AmmIndex = new(big.Int).SetUint64(aux.AmmIndex) +// // Convert and assign the values to the original struct +// o.AmmIndex = new(big.Int).SetUint64(aux.AmmIndex) - o.Trader = aux.Trader +// o.Trader = aux.Trader - o.BaseAssetQuantity = new(big.Int) - o.BaseAssetQuantity.SetString(aux.BaseAssetQuantity, 10) +// o.BaseAssetQuantity = new(big.Int) +// o.BaseAssetQuantity.SetString(aux.BaseAssetQuantity, 10) - o.Price = new(big.Int) - o.Price.SetString(aux.Price, 10) +// o.Price = new(big.Int) +// o.Price.SetString(aux.Price, 10) - o.Salt = new(big.Int) - o.Salt.SetBytes(common.FromHex(aux.Salt)) +// o.Salt = new(big.Int) +// o.Salt.SetBytes(common.FromHex(aux.Salt)) - o.ReduceOnly = aux.ReduceOnly - o.PostOnly = aux.PostOnly - o.OrderType = aux.OrderType +// o.ReduceOnly = aux.ReduceOnly +// o.PostOnly = aux.PostOnly +// o.OrderType = aux.OrderType - o.ExpireAt = new(big.Int).SetUint64(aux.ExpireAt) - o.Sig = common.FromHex(aux.Sig) - return nil -} +// o.ExpireAt = new(big.Int).SetUint64(aux.ExpireAt) +// o.Sig = common.FromHex(aux.Sig) +// return nil +// } -func (order *SignedOrder) DecodeAPIOrder(rawOrder interface{}) error { - order_, ok := rawOrder.(string) - if !ok { - fmt.Println("invalid data format") - } +// func (order *SignedOrder) DecodeAPIOrder(rawOrder interface{}) error { +// order_, ok := rawOrder.(string) +// if !ok { +// fmt.Println("invalid data format") +// } - orderJson := []byte(order_) - err := json.Unmarshal(orderJson, &order) - if err != nil { - return err - } - return nil -} +// orderJson := []byte(order_) +// err := json.Unmarshal(orderJson, &order) +// if err != nil { +// return err +// } +// return nil +// } diff --git a/plugin/evm/orderbook/hubbleutils/signed_orders_test.go b/plugin/evm/orderbook/hubbleutils/signed_orders_test.go index a2e1983dd6..6d4da00157 100644 --- a/plugin/evm/orderbook/hubbleutils/signed_orders_test.go +++ b/plugin/evm/orderbook/hubbleutils/signed_orders_test.go @@ -3,7 +3,10 @@ package hubbleutils import ( "encoding/hex" "fmt" + + // "fmt" "math/big" + "strings" "testing" @@ -41,9 +44,13 @@ func TestDecodeSignedOrder(t *testing.T) { // }) t.Run("short order", func(t *testing.T) { - // sig, err := hex.DecodeString() - // assert.Nil(t, err) - sig := "b2704b73b99f2700ecc90a218f514c254d1f5d46af47117f5317f6cc0348ce962dcfb024c7264fdeb1f1513e4564c2a7cd9c1d0be33d7b934cd5a73b96440eaf1c" + orderHash := strings.TrimPrefix("0xee4b26ae386d1c88f89eb2f8b4b4205271576742f5ff4e0488633612f7a9a5e7", "0x") + signature := strings.TrimPrefix("0xb2704b73b99f2700ecc90a218f514c254d1f5d46af47117f5317f6cc0348ce962dcfb024c7264fdeb1f1513e4564c2a7cd9c1d0be33d7b934cd5a73b96440eaf1c", "0x") + encodedOrder := strings.TrimPrefix("0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c0000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef79590000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000041b2704b73b99f2700ecc90a218f514c254d1f5d46af47117f5317f6cc0348ce962dcfb024c7264fdeb1f1513e4564c2a7cd9c1d0be33d7b934cd5a73b96440eaf1c00000000000000000000000000000000000000000000000000000000000000", "0x") + typeEncodedOrder := strings.TrimPrefix("0x0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c0000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef79590000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000041b2704b73b99f2700ecc90a218f514c254d1f5d46af47117f5317f6cc0348ce962dcfb024c7264fdeb1f1513e4564c2a7cd9c1d0be33d7b934cd5a73b96440eaf1c00000000000000000000000000000000000000000000000000000000000000", "0x") + + sig, err := hex.DecodeString(signature) + assert.Nil(t, err) order := &SignedOrder{ LimitOrder: LimitOrder{ BaseOrder: BaseOrder{ @@ -62,25 +69,29 @@ func TestDecodeSignedOrder(t *testing.T) { } h, err := order.Hash() assert.Nil(t, err) - assert.Equal(t, "0xee4b26ae386d1c88f89eb2f8b4b4205271576742f5ff4e0488633612f7a9a5e7", h.Hex()) + assert.Equal(t, orderHash, strings.TrimPrefix(h.Hex(), "0x")) - // typeEncodedOrder := strings.TrimPrefix("0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c0000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef795900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0x") - // encodedOrder := strings.TrimPrefix("0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c0000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef795900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0x") - - // with sig - // typeEncodedOrder := strings.TrimPrefix("0x0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c0000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef79590000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000041b2704b73b99f2700ecc90a218f514c254d1f5d46af47117f5317f6cc0348ce962dcfb024c7264fdeb1f1513e4564c2a7cd9c1d0be33d7b934cd5a73b96440eaf1c00000000000000000000000000000000000000000000000000000000000000", "0x") - // encodedOrder := strings.TrimPrefix("0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000064ac0426000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8ffffffffffffffffffffffffffffffffffffffffffffffffba9c6e7dbb0c0000000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000001893fef79590000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000041b2704b73b99f2700ecc90a218f514c254d1f5d46af47117f5317f6cc0348ce962dcfb024c7264fdeb1f1513e4564c2a7cd9c1d0be33d7b934cd5a73b96440eaf1c00000000000000000000000000000000000000000000000000000000000000", "0x") b, err := order.EncodeToABIWithoutType() - fmt.Println("here 0", hex.EncodeToString(b), err) - assert.NoError(t, err) - result, err := DecodeSignedOrder(b) - assert.NoError(t, err) - fmt.Println("here 1", result, err) - assertSignedOrderEquality(t, order, result) - // b, err := order.EncodeToABI() - // assert.Nil(t, err) - // assert.Equal(t, typeEncodedOrder, hex.EncodeToString(b)) - // testDecodeTypeAndEncodedSignedOrder(t, typeEncodedOrder, encodedOrder, Signed, order) + assert.Nil(t, err) + assert.Equal(t, encodedOrder, hex.EncodeToString(b)) + + b, err = order.EncodeToABI() + assert.Nil(t, err) + assert.Equal(t, typeEncodedOrder, hex.EncodeToString(b)) + + testDecodeTypeAndEncodedSignedOrder(t, typeEncodedOrder, encodedOrder, Signed, order) + + data, err := hex.DecodeString(orderHash) + assert.Nil(t, err) + fmt.Println("sig", hex.EncodeToString(sig)) + signer, err := ECRecover(data, sig) + fmt.Println("sig", hex.EncodeToString(sig)) + assert.Nil(t, err) + assert.Equal(t, order.Trader, signer) + + sig_, _ := hex.DecodeString(signature) + assert.Equal(t, sig_, sig) // sig is not changed + assert.Equal(t, sig_, order.Sig) // sig is not changed }) } @@ -93,16 +104,13 @@ func testDecodeTypeAndEncodedSignedOrder(t *testing.T, typedEncodedOrder string, assert.Equal(t, orderType, decodeStep.OrderType) assert.Equal(t, encodedOrder, hex.EncodeToString(decodeStep.EncodedOrder)) - // yo, err := hex.DecodeString(encodedOrder) assert.Nil(t, err) - // testDecodeSignedOrder(t, yo, expectedOutput) testDecodeSignedOrder(t, decodeStep.EncodedOrder, expectedOutput) } func testDecodeSignedOrder(t *testing.T, encodedOrder []byte, expectedOutput *SignedOrder) { result, err := DecodeSignedOrder(encodedOrder) assert.NoError(t, err) - fmt.Println("here", result, err) assert.NotNil(t, result) assertSignedOrderEquality(t, expectedOutput, result) } diff --git a/plugin/evm/orderbook/hubbleutils/validations.go b/plugin/evm/orderbook/hubbleutils/validations.go index 88a4e343f6..0e7bc19a09 100644 --- a/plugin/evm/orderbook/hubbleutils/validations.go +++ b/plugin/evm/orderbook/hubbleutils/validations.go @@ -2,7 +2,7 @@ package hubbleutils import ( "errors" - "fmt" + // "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -90,12 +90,12 @@ func ValidateSignedOrder(order *SignedOrder, fields SignedOrderValidationFields) // 6. caller will perform the check orderHash, err := order.Hash() - fmt.Println("orderHash", orderHash) + // fmt.Println("orderHash", orderHash) if err != nil { return trader, signer, err } - signer, err = ECRecover(orderHash.Bytes(), order.Sig) - fmt.Println("signer", signer) + signer, err = ECRecover(orderHash.Bytes(), order.Sig[:]) + // fmt.Println("signer", signer) if err != nil { return trader, signer, err } diff --git a/plugin/evm/orderbook/matching_pipeline.go b/plugin/evm/orderbook/matching_pipeline.go index 56d015bebb..8e87bc29bf 100644 --- a/plugin/evm/orderbook/matching_pipeline.go +++ b/plugin/evm/orderbook/matching_pipeline.go @@ -87,6 +87,7 @@ func (pipeline *MatchingPipeline) Run(blockNumber *big.Int) bool { orderMap := make(map[Market]*Orders) for _, market := range markets { orderMap[market] = pipeline.fetchOrders(market, hState.OraclePrices[market], cancellableOrderIds, blockNumber) + log.Info("orders fetched", "LongOrders", orderMap[market].longOrders, "ShortOrders", orderMap[market].shortOrders) } pipeline.runLiquidations(liquidablePositions, orderMap, hState.OraclePrices) for _, market := range markets { @@ -118,7 +119,7 @@ func (pipeline *MatchingPipeline) GetActiveMarkets() []Market { func (pipeline *MatchingPipeline) GetUnderlyingPrices() map[Market]*big.Int { prices := pipeline.configService.GetUnderlyingPrices() - log.Info("GetUnderlyingPrices", "prices", prices) + // log.Info("GetUnderlyingPrices", "prices", prices) underlyingPrices := make(map[Market]*big.Int) for market, price := range prices { underlyingPrices[Market(market)] = price @@ -128,7 +129,7 @@ func (pipeline *MatchingPipeline) GetUnderlyingPrices() map[Market]*big.Int { func (pipeline *MatchingPipeline) GetMidPrices() map[Market]*big.Int { prices := pipeline.configService.GetMidPrices() - log.Info("GetMidPrices", "prices", prices) + // log.Info("GetMidPrices", "prices", prices) midPrices := make(map[Market]*big.Int) for market, price := range prices { midPrices[Market(market)] = price diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index e7bce980de..a7a361cf2e 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -150,6 +150,9 @@ func (order Order) getExpireAt() *big.Int { if order.OrderType == IOC { return order.RawOrder.(*IOCOrder).ExpireAt } + if order.OrderType == Signed { + return order.RawOrder.(*hu.SignedOrder).ExpireAt + } return big.NewInt(0) } @@ -159,11 +162,17 @@ func (order Order) isPostOnly() bool { return rawOrder.PostOnly } } + if order.OrderType == Signed { + if rawOrder, ok := order.RawOrder.(*hu.SignedOrder); ok { + return rawOrder.PostOnly + } + } return false } func (order Order) String() string { - return fmt.Sprintf("Order: Id: %s, OrderType: %s, Market: %v, PositionType: %v, UserAddress: %v, BaseAssetQuantity: %s, FilledBaseAssetQuantity: %s, Salt: %v, Price: %s, ReduceOnly: %v, PostOnly: %v, BlockNumber: %s", order.Id, order.OrderType, order.Market, order.PositionType, order.Trader.String(), prettifyScaledBigInt(order.BaseAssetQuantity, 18), prettifyScaledBigInt(order.FilledBaseAssetQuantity, 18), order.Salt, prettifyScaledBigInt(order.Price, 6), order.ReduceOnly, order.isPostOnly(), order.BlockNumber) + t := time.Unix(order.getExpireAt().Int64(), 0) + return fmt.Sprintf("Order: Id: %s, OrderType: %s, Market: %v, PositionType: %v, UserAddress: %v, BaseAssetQuantity: %s, FilledBaseAssetQuantity: %s, Salt: %v, Price: %s, ReduceOnly: %v, PostOnly: %v, expireAt %s, BlockNumber: %s", order.Id, order.OrderType, order.Market, order.PositionType, order.Trader.String(), prettifyScaledBigInt(order.BaseAssetQuantity, 18), prettifyScaledBigInt(order.FilledBaseAssetQuantity, 18), order.Salt, prettifyScaledBigInt(order.Price, 6), order.ReduceOnly, order.isPostOnly(), t.UTC(), order.BlockNumber) } func (order Order) ToOrderMin() OrderMin { @@ -288,7 +297,8 @@ func (db *InMemoryDatabase) Accept(acceptedBlockNumber, blockTimestamp uint64) { for _, longOrder := range longOrders { status := shouldRemove(acceptedBlockNumber, blockTimestamp, longOrder) - if status == CHECK_FOR_MATCHES { + log.Info("Accept", "longOrder", longOrder, "status", status) + if status == KEEP_IF_MATCHEABLE { matchFound := false for _, shortOrder := range shortOrders { if longOrder.Price.Cmp(shortOrder.Price) < 0 { @@ -314,7 +324,8 @@ func (db *InMemoryDatabase) Accept(acceptedBlockNumber, blockTimestamp uint64) { for _, shortOrder := range shortOrders { status := shouldRemove(acceptedBlockNumber, blockTimestamp, shortOrder) - if status == CHECK_FOR_MATCHES { + log.Info("Accept", "shortOrder", shortOrder, "status", status) + if status == KEEP_IF_MATCHEABLE { matchFound := false for _, longOrder := range longOrders { if longOrder.Price.Cmp(shortOrder.Price) < 0 { @@ -346,7 +357,7 @@ type OrderStatus uint8 const ( KEEP OrderStatus = iota REMOVE - CHECK_FOR_MATCHES + KEEP_IF_MATCHEABLE ) func shouldRemove(acceptedBlockNumber, blockTimestamp uint64, order Order) OrderStatus { @@ -357,22 +368,25 @@ func shouldRemove(acceptedBlockNumber, blockTimestamp uint64, order Order) Order return REMOVE } - if order.OrderType != IOC { + if order.OrderType == Limit { return KEEP } - // 2. if the order is expired + // remove if order is expired; valid for both IOC and Signed orders expireAt := order.getExpireAt() if expireAt.Sign() > 0 && expireAt.Int64() < int64(blockTimestamp) { return REMOVE } - // 3. IOC order can not matched with any order that came after it (same block is allowed) + // IOC order can not matched with any order that came after it (same block is allowed) // we can only surely say about orders that came at <= acceptedBlockNumber - if order.BlockNumber.Uint64() > acceptedBlockNumber { - return KEEP + if order.OrderType == IOC { + if order.BlockNumber.Uint64() > acceptedBlockNumber { + return KEEP + } + return KEEP_IF_MATCHEABLE } - return CHECK_FOR_MATCHES + return KEEP } func (db *InMemoryDatabase) SetOrderStatus(orderId common.Hash, status Status, info string, blockNumber uint64) error { @@ -432,6 +446,7 @@ func (db *InMemoryDatabase) Add(order *Order) { db.mu.Lock() defer db.mu.Unlock() + log.Info("Adding order to memdb", "order", order) order.LifecycleList = append(order.LifecycleList, Lifecycle{order.BlockNumber.Uint64(), Placed, ""}) db.AddInSortedArray(order) db.Orders[order.Id] = order @@ -614,6 +629,7 @@ func (db *InMemoryDatabase) getLongOrdersWithoutLock(market Market, lowerbound * var longOrders []Order marketOrders := db.LongOrders[market] + // log.Info("getLongOrdersWithoutLock", "marketOrders", marketOrders, "lowerbound", lowerbound, "blockNumber", blockNumber) for _, order := range marketOrders { if lowerbound != nil && order.Price.Cmp(lowerbound) < 0 { // because the long orders are sorted in descending order of price, there is no point in checking further @@ -659,8 +675,10 @@ func (db *InMemoryDatabase) getShortOrdersWithoutLock(market Market, upperbound } func (db *InMemoryDatabase) getCleanOrder(order *Order, blockNumber *big.Int) *Order { + // log.Info("getCleanOrder", "order", order, "blockNumber", blockNumber) eligibleForExecution := false orderStatus := order.getOrderStatus() + // log.Info("getCleanOrder", "orderStatus", orderStatus) switch orderStatus.Status { case Placed: eligibleForExecution = true @@ -689,6 +707,7 @@ func (db *InMemoryDatabase) getCleanOrder(order *Order, blockNumber *big.Int) *O if expireAt.Sign() == 1 && expireAt.Int64() <= time.Now().Unix() { eligibleForExecution = false } + // log.Info("getCleanOrder", "expireAt", expireAt, "eligibleForExecution", eligibleForExecution) if eligibleForExecution { if order.ReduceOnly { diff --git a/plugin/evm/orderbook/trading_apis.go b/plugin/evm/orderbook/trading_apis.go index aef8e1b859..9a13203489 100644 --- a/plugin/evm/orderbook/trading_apis.go +++ b/plugin/evm/orderbook/trading_apis.go @@ -5,9 +5,9 @@ package orderbook import ( "context" + "encoding/hex" "fmt" "math/big" - "reflect" "strings" "time" @@ -317,26 +317,27 @@ type PlaceOrderResponse struct { Success bool `json:"success"` } -func (api *TradingAPI) PostOrder(ctx context.Context, rawOrder interface{}) (PlaceOrderResponse, error) { - // print type of rawOrder - // order := rawOrder.(hu.SignedOrder) - - fmt.Println("type", reflect.TypeOf(rawOrder)) - - // order := rawOrder.(hu.SignedOrder) - - fmt.Println("rawOrder", rawOrder) - order := hu.SignedOrder{} - order.DecodeAPIOrder(rawOrder) - fmt.Println("PostOrder", order) +func (api *TradingAPI) PostOrder(ctx context.Context, rawOrder string) (PlaceOrderResponse, error) { + // fmt.Println("rawOrder", rawOrder) + testData, err := hex.DecodeString(strings.TrimPrefix(rawOrder, "0x")) + if err != nil { + return PlaceOrderResponse{Success: false}, err + } + // order := &hu.SignedOrder{} + order, err := hu.DecodeSignedOrder(testData) + if err != nil { + return PlaceOrderResponse{Success: false}, err + } + // order.DecodeAPIOrder(rawOrder) + // fmt.Println("PostOrder", order) marketId := int(order.AmmIndex.Int64()) - orderId, err := order.Hash(api.backend.ChainConfig().ChainID, api.configService.GetVerifyingContract()) + orderId, err := order.Hash() if err != nil { return PlaceOrderResponse{Success: false}, err } trader, signer, err := hu.ValidateSignedOrder( - &order, + order, hu.SignedOrderValidationFields{ Now: uint64(time.Now().Unix()), ActiveMarketsCount: api.configService.GetActiveMarketsCount(), @@ -380,13 +381,12 @@ func (api *TradingAPI) PostOrder(ctx context.Context, rawOrder interface{}) (Pla BaseAssetQuantity: order.BaseAssetQuantity, FilledBaseAssetQuantity: big.NewInt(0), Price: order.Price, - RawOrder: &order, Salt: order.Salt, ReduceOnly: order.ReduceOnly, BlockNumber: big.NewInt(0), - OrderType: Limit, + RawOrder: order, + OrderType: Signed, } api.db.Add(limitOrder) - fmt.Println("success", "limitOrder", limitOrder) return PlaceOrderResponse{Success: true}, nil } diff --git a/plugin/evm/orderbook/tx_processor.go b/plugin/evm/orderbook/tx_processor.go index d41d67511e..a5e5804e0f 100644 --- a/plugin/evm/orderbook/tx_processor.go +++ b/plugin/evm/orderbook/tx_processor.go @@ -3,6 +3,7 @@ package orderbook import ( "context" "crypto/ecdsa" + "encoding/hex" "time" // "encoding/hex" @@ -140,6 +141,7 @@ func (lotp *limitOrderTxProcessor) ExecuteMatchedOrdersTx(longOrder Order, short log.Error("EncodeLimitOrder failed for shortOrder", "order", shortOrder, "err", err) return err } + log.Info("ExecuteMatchedOrdersTx", "longOrder", hex.EncodeToString(orders[0]), "shortOrder", hex.EncodeToString(orders[1]), "fillAmount", prettifyScaledBigInt(fillAmount, 18), "err", err) txHash, err := lotp.executeLocalTx(lotp.orderBookContractAddress, lotp.orderBookABI, "executeMatchedOrders", orders, fillAmount) log.Info("ExecuteMatchedOrdersTx", "LongOrder", longOrder, "ShortOrder", shortOrder, "fillAmount", prettifyScaledBigInt(fillAmount, 18), "txHash", txHash.String(), "err", err) diff --git a/precompile/contracts/bibliophile/signed_order_book.go b/precompile/contracts/bibliophile/signed_order_book.go index a29832ab01..611294fc52 100644 --- a/precompile/contracts/bibliophile/signed_order_book.go +++ b/precompile/contracts/bibliophile/signed_order_book.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" ) const ( @@ -16,20 +17,22 @@ const ( // State Reader func GetSignedOrderFilledAmount(stateDB contract.StateDB, orderHash [32]byte) *big.Int { orderInfo := signedOrderInfoMappingStorageSlot(orderHash) - num := stateDB.GetState(signedOrderBookAddress(stateDB), common.BigToHash(orderInfo)).Bytes() + num := stateDB.GetState(GetSignedOrderBookAddress(stateDB), common.BigToHash(orderInfo)).Bytes() return fromTwosComplement(num) } func GetSignedOrderStatus(stateDB contract.StateDB, orderHash [32]byte) int64 { + a := GetSignedOrderBookAddress(stateDB) + log.Info("GetSignedOrderStatus: ", a.Hex()) orderInfo := signedOrderInfoMappingStorageSlot(orderHash) - return new(big.Int).SetBytes(stateDB.GetState(signedOrderBookAddress(stateDB), common.BigToHash(new(big.Int).Add(orderInfo, big.NewInt(1)))).Bytes()).Int64() + return new(big.Int).SetBytes(stateDB.GetState(a, common.BigToHash(new(big.Int).Add(orderInfo, big.NewInt(1)))).Bytes()).Int64() } func signedOrderInfoMappingStorageSlot(orderHash [32]byte) *big.Int { return new(big.Int).SetBytes(crypto.Keccak256(append(orderHash[:], common.LeftPadBytes(big.NewInt(SIGNED_ORDER_INFO_SLOT).Bytes(), 32)...))) } -func signedOrderBookAddress(stateDB contract.StateDB) common.Address { +func GetSignedOrderBookAddress(stateDB contract.StateDB) common.Address { slot := crypto.Keccak256(append(common.LeftPadBytes(big.NewInt(2).Bytes() /* orderType */, 32), common.LeftPadBytes(big.NewInt(ORDER_HANDLER_STORAGE_SLOT).Bytes(), 32)...)) return common.BytesToAddress(stateDB.GetState(common.HexToAddress(ORDERBOOK_GENESIS_ADDRESS), common.BytesToHash(slot)).Bytes()) } diff --git a/precompile/contracts/jurorv2/contract.go b/precompile/contracts/jurorv2/contract.go index 13a074530f..adda041d4a 100644 --- a/precompile/contracts/jurorv2/contract.go +++ b/precompile/contracts/jurorv2/contract.go @@ -16,6 +16,7 @@ import ( _ "embed" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" ) const ( @@ -214,7 +215,7 @@ func getNotionalPositionAndMargin(accessibleState contract.AccessibleState, call // CUSTOM CODE STARTS HERE bibliophile := bibliophile.NewBibliophileClient(accessibleState) - accessibleState.GetSnowContext().ChainID + log.Info("accessibleState.GetSnowContext().ChainID", accessibleState.GetSnowContext().ChainID.String()) output := GetNotionalPositionAndMargin(bibliophile, &inputStruct) packedOutput, err := PackGetNotionalPositionAndMarginOutput(output) if err != nil { diff --git a/precompile/contracts/jurorv2/matching_validation.go b/precompile/contracts/jurorv2/matching_validation.go index 973cf3bcd6..baeb364d1f 100644 --- a/precompile/contracts/jurorv2/matching_validation.go +++ b/precompile/contracts/jurorv2/matching_validation.go @@ -9,6 +9,7 @@ import ( 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" ) type Metadata struct { @@ -378,6 +379,7 @@ func validateExecuteIOCOrder(bibliophile b.BibliophileClient, order *ob.IOCOrder } func validateExecuteSignedOrder(bibliophile b.BibliophileClient, order *hu.SignedOrder, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { + log.Info("validateExecuteSignedOrder", "order", order) orderHash, err := order.Hash() if err != nil { return nil, err @@ -403,11 +405,12 @@ func validateExecuteSignedOrder(bibliophile b.BibliophileClient, order *hu.Signe // M1, M2 orderStatus := OrderStatus(bibliophile.GetSignedOrderStatus(orderHash)) + log.Info("validateExecuteSignedOrder", "orderStatus", orderStatus) if orderStatus == Invalid { // signed orders don't get placed in the contract, so we consider them placed by default orderStatus = Placed } - if err := validateLimitOrderLike(bibliophile, &order.BaseOrder, bibliophile.GetSignedOrderFilledAmount(orderHash), OrderStatus(bibliophile.GetSignedOrderStatus(orderHash)), side, fillAmount); err != nil { + if err := validateLimitOrderLike(bibliophile, &order.BaseOrder, bibliophile.GetSignedOrderFilledAmount(orderHash), orderStatus, side, fillAmount); err != nil { return &Metadata{OrderHash: orderHash}, err } From 10d3f25cdcb09296f57b6b4c003f7f420ded7e6d Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Tue, 26 Dec 2023 21:44:24 +0000 Subject: [PATCH 09/12] chainid and verifying contract --- plugin/evm/orderbook/config_service.go | 5 ++- .../orderbook/hubbleutils/signed_orders.go | 43 ++++++++----------- .../hubbleutils/signed_orders_test.go | 1 + plugin/evm/orderbook/matching_pipeline.go | 3 +- plugin/evm/orderbook/memory_database.go | 3 +- plugin/evm/orderbook/trading_apis.go | 7 +-- .../bibliophile/signed_order_book.go | 2 - precompile/contracts/jurorv2/contract.go | 1 + .../contracts/jurorv2/matching_validation.go | 2 +- 9 files changed, 32 insertions(+), 35 deletions(-) diff --git a/plugin/evm/orderbook/config_service.go b/plugin/evm/orderbook/config_service.go index 4ef0505998..db22a688b2 100644 --- a/plugin/evm/orderbook/config_service.go +++ b/plugin/evm/orderbook/config_service.go @@ -28,6 +28,7 @@ type IConfigService interface { GetSignedOrderStatus(orderHash common.Hash) int64 IsTradingAuthority(trader, signer common.Address) bool + GetChainIdAndSignedOrderbookContract() common.Address } type ConfigService struct { @@ -111,6 +112,6 @@ func (cs *ConfigService) IsTradingAuthority(trader, signer common.Address) bool return bibliophile.IsTradingAuthority(cs.getStateAtCurrentBlock(), trader, signer) } -func (cs *ConfigService) GetChainIdAndSignedOrderbookContract() (*big.Int, common.Address) { - return cs.blockChain.Config().ChainID, bibliophile.GetSignedOrderBookAddress(cs.getStateAtCurrentBlock()) +func (cs *ConfigService) GetChainIdAndSignedOrderbookContract() common.Address { + return bibliophile.GetSignedOrderBookAddress(cs.getStateAtCurrentBlock()) } diff --git a/plugin/evm/orderbook/hubbleutils/signed_orders.go b/plugin/evm/orderbook/hubbleutils/signed_orders.go index b5f425b73b..784d3c138c 100644 --- a/plugin/evm/orderbook/hubbleutils/signed_orders.go +++ b/plugin/evm/orderbook/hubbleutils/signed_orders.go @@ -8,7 +8,6 @@ import ( "strconv" "github.com/ava-labs/subnet-evm/accounts/abi" - // "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/signer/core/apitypes" @@ -21,6 +20,16 @@ type SignedOrder struct { Sig []byte `json:"sig"` } +var ( + ChainId int64 + VerifyingContract string +) + +func SetChainIdAndVerifyingSignedOrdersContract(chainId int64, verifyingContract string) { + ChainId = chainId + VerifyingContract = verifyingContract +} + func (order *SignedOrder) EncodeToABIWithoutType() ([]byte, error) { signedOrderType, err := getOrderType("signed") if err != nil { @@ -40,9 +49,10 @@ func (order *SignedOrder) EncodeToABI() ([]byte, error) { return nil, fmt.Errorf("failed getting abi type: %w", err) } - orderType, _ := abi.NewType("uint8", "uint8", nil) - orderBytesType, _ := abi.NewType("bytes", "bytes", nil) - encodedOrder, err := abi.Arguments{{Type: orderType}, {Type: orderBytesType}}.Pack(uint8(Signed), encodedSignedOrder) + uint8Ty, _ := abi.NewType("uint8", "uint8", nil) + bytesTy, _ := abi.NewType("bytes", "bytes", nil) + + encodedOrder, err := abi.Arguments{{Type: uint8Ty}, {Type: bytesTy}}.Pack(uint8(Signed), encodedSignedOrder) if err != nil { return nil, fmt.Errorf("order encoding failed: %w", err) } @@ -77,21 +87,6 @@ func (order *SignedOrder) DecodeFromRawOrder(rawOrder interface{}) { } } -// func (order *SignedOrder) Map() map[string]interface{} { -// return map[string]interface{}{ -// "ammIndex": order.AmmIndex, -// "trader": order.Trader, -// "baseAssetQuantity": utils.BigIntToFloat(order.BaseAssetQuantity, 18), -// "price": utils.BigIntToFloat(order.Price, 6), -// "reduceOnly": order.ReduceOnly, -// "postOnly": order.PostOnly, -// "salt": order.Salt, -// "orderType": order.OrderType, -// "expireAt": order.ExpireAt, -// "sig": order.Sig, -// } -// } - func (o *SignedOrder) String() string { return fmt.Sprintf( "Order %s, OrderType: %d, ExpireAt: %d, Sig: %s", @@ -115,12 +110,10 @@ func (o *SignedOrder) Hash() (hash common.Hash, err error) { "postOnly": o.PostOnly, } domain := apitypes.TypedDataDomain{ - Name: "Hubble", - Version: "2.0", - ChainId: math.NewHexOrDecimal256(321123), // @todo chain id from config - // @todo use the correct address - // VerifyingContract: common.HexToAddress("0x809d550fca64d94Bd9F66E60752A544199cfAC3D").String(), // used for unit tests - VerifyingContract: common.HexToAddress("0x5eb3Bc0a489C5A8288765d2336659EbCA68FCd00").String(), // from benchmarks scripts + Name: "Hubble", + Version: "2.0", + ChainId: math.NewHexOrDecimal256(ChainId), + VerifyingContract: VerifyingContract, } typedData := apitypes.TypedData{ Types: Eip712OrderTypes, diff --git a/plugin/evm/orderbook/hubbleutils/signed_orders_test.go b/plugin/evm/orderbook/hubbleutils/signed_orders_test.go index 6d4da00157..037406bc98 100644 --- a/plugin/evm/orderbook/hubbleutils/signed_orders_test.go +++ b/plugin/evm/orderbook/hubbleutils/signed_orders_test.go @@ -15,6 +15,7 @@ import ( ) func TestDecodeSignedOrder(t *testing.T) { + SetChainIdAndVerifyingSignedOrdersContract(321123, "0x809d550fca64d94Bd9F66E60752A544199cfAC3D") // t.Run("long order", func(t *testing.T) { // order := &SignedOrder{ // LimitOrder: LimitOrder{ diff --git a/plugin/evm/orderbook/matching_pipeline.go b/plugin/evm/orderbook/matching_pipeline.go index 8e87bc29bf..d90c5e93b7 100644 --- a/plugin/evm/orderbook/matching_pipeline.go +++ b/plugin/evm/orderbook/matching_pipeline.go @@ -87,7 +87,7 @@ func (pipeline *MatchingPipeline) Run(blockNumber *big.Int) bool { orderMap := make(map[Market]*Orders) for _, market := range markets { orderMap[market] = pipeline.fetchOrders(market, hState.OraclePrices[market], cancellableOrderIds, blockNumber) - log.Info("orders fetched", "LongOrders", orderMap[market].longOrders, "ShortOrders", orderMap[market].shortOrders) + log.Info("orders fetched", "market", market, "LongOrders", orderMap[market].longOrders, "ShortOrders", orderMap[market].shortOrders) } pipeline.runLiquidations(liquidablePositions, orderMap, hState.OraclePrices) for _, market := range markets { @@ -96,6 +96,7 @@ func (pipeline *MatchingPipeline) Run(blockNumber *big.Int) bool { } orderBookTxsCount := pipeline.lotp.GetOrderBookTxsCount() + log.Info("MatchingPipeline:Run", "orderBookTxsCount", orderBookTxsCount) if orderBookTxsCount > 0 { pipeline.lotp.SetOrderBookTxsBlockNumber(blockNumber.Uint64()) return true diff --git a/plugin/evm/orderbook/memory_database.go b/plugin/evm/orderbook/memory_database.go index a7a361cf2e..4df088fbc2 100644 --- a/plugin/evm/orderbook/memory_database.go +++ b/plugin/evm/orderbook/memory_database.go @@ -290,6 +290,7 @@ func (db *InMemoryDatabase) Accept(acceptedBlockNumber, blockTimestamp uint64) { db.mu.Lock() defer db.mu.Unlock() + log.Info("Accept", "acceptedBlockNumber", acceptedBlockNumber, "blockTimestamp", blockTimestamp) count := db.configService.GetActiveMarketsCount() for m := int64(0); m < count; m++ { longOrders := db.getLongOrdersWithoutLock(Market(m), nil, nil, false) @@ -297,7 +298,7 @@ func (db *InMemoryDatabase) Accept(acceptedBlockNumber, blockTimestamp uint64) { for _, longOrder := range longOrders { status := shouldRemove(acceptedBlockNumber, blockTimestamp, longOrder) - log.Info("Accept", "longOrder", longOrder, "status", status) + log.Info("evaluating order...", "longOrder", longOrder, "status", status) if status == KEEP_IF_MATCHEABLE { matchFound := false for _, shortOrder := range shortOrders { diff --git a/plugin/evm/orderbook/trading_apis.go b/plugin/evm/orderbook/trading_apis.go index 9a13203489..61e86c0157 100644 --- a/plugin/evm/orderbook/trading_apis.go +++ b/plugin/evm/orderbook/trading_apis.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" ) var traderFeed event.Feed @@ -323,15 +324,16 @@ func (api *TradingAPI) PostOrder(ctx context.Context, rawOrder string) (PlaceOrd if err != nil { return PlaceOrderResponse{Success: false}, err } - // order := &hu.SignedOrder{} order, err := hu.DecodeSignedOrder(testData) if err != nil { return PlaceOrderResponse{Success: false}, err } - // order.DecodeAPIOrder(rawOrder) // fmt.Println("PostOrder", order) marketId := int(order.AmmIndex.Int64()) + if hu.ChainId == 0 { // set once, will need to restart node if we change + hu.SetChainIdAndVerifyingSignedOrdersContract(api.backend.ChainConfig().ChainID.Int64(), api.configService.GetChainIdAndSignedOrderbookContract().String()) + } orderId, err := order.Hash() if err != nil { return PlaceOrderResponse{Success: false}, err @@ -369,7 +371,6 @@ func (api *TradingAPI) PostOrder(ctx context.Context, rawOrder string) (PlaceOrd } } // @todo P5 - // @todo gossip order // add to db diff --git a/precompile/contracts/bibliophile/signed_order_book.go b/precompile/contracts/bibliophile/signed_order_book.go index 611294fc52..5aeb33ffd6 100644 --- a/precompile/contracts/bibliophile/signed_order_book.go +++ b/precompile/contracts/bibliophile/signed_order_book.go @@ -7,7 +7,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" ) const ( @@ -23,7 +22,6 @@ func GetSignedOrderFilledAmount(stateDB contract.StateDB, orderHash [32]byte) *b func GetSignedOrderStatus(stateDB contract.StateDB, orderHash [32]byte) int64 { a := GetSignedOrderBookAddress(stateDB) - log.Info("GetSignedOrderStatus: ", a.Hex()) orderInfo := signedOrderInfoMappingStorageSlot(orderHash) return new(big.Int).SetBytes(stateDB.GetState(a, common.BigToHash(new(big.Int).Add(orderInfo, big.NewInt(1)))).Bytes()).Int64() } diff --git a/precompile/contracts/jurorv2/contract.go b/precompile/contracts/jurorv2/contract.go index adda041d4a..f9bb4f4e37 100644 --- a/precompile/contracts/jurorv2/contract.go +++ b/precompile/contracts/jurorv2/contract.go @@ -359,6 +359,7 @@ func validateOrdersAndDetermineFillPrice(accessibleState contract.AccessibleStat } // CUSTOM CODE STARTS HERE + log.Info("validateOrdersAndDetermineFillPrice", "inputStruct", inputStruct) bibliophile := bibliophile.NewBibliophileClient(accessibleState) output := ValidateOrdersAndDetermineFillPrice(bibliophile, &inputStruct) packedOutput, err := PackValidateOrdersAndDetermineFillPriceOutput(output) diff --git a/precompile/contracts/jurorv2/matching_validation.go b/precompile/contracts/jurorv2/matching_validation.go index baeb364d1f..fe8dc96236 100644 --- a/precompile/contracts/jurorv2/matching_validation.go +++ b/precompile/contracts/jurorv2/matching_validation.go @@ -4,6 +4,7 @@ import ( "errors" "math/big" + "github.com/ava-labs/subnet-evm/accounts/abi" ob "github.com/ava-labs/subnet-evm/plugin/evm/orderbook" hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" b "github.com/ava-labs/subnet-evm/precompile/contracts/bibliophile" @@ -379,7 +380,6 @@ func validateExecuteIOCOrder(bibliophile b.BibliophileClient, order *ob.IOCOrder } func validateExecuteSignedOrder(bibliophile b.BibliophileClient, order *hu.SignedOrder, side Side, fillAmount *big.Int) (metadata *Metadata, err error) { - log.Info("validateExecuteSignedOrder", "order", order) orderHash, err := order.Hash() if err != nil { return nil, err From 374b36abe609705bce8ccaffa73c86a8db289b00 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Wed, 27 Dec 2023 22:11:28 +0000 Subject: [PATCH 10/12] stash --- plugin/evm/orderbook/mocks.go | 4 ++++ plugin/evm/orderbook/trading_apis.go | 1 - precompile/contracts/jurorv2/matching_validation.go | 1 - 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugin/evm/orderbook/mocks.go b/plugin/evm/orderbook/mocks.go index ce5075a921..9419c34dbe 100644 --- a/plugin/evm/orderbook/mocks.go +++ b/plugin/evm/orderbook/mocks.go @@ -310,3 +310,7 @@ func (cs *MockConfigService) IsTradingAuthority(trader, signer common.Address) b func NewMockConfigService() *MockConfigService { return &MockConfigService{} } + +func (cs *MockConfigService) GetChainIdAndSignedOrderbookContract() common.Address { + return common.Address{} +} diff --git a/plugin/evm/orderbook/trading_apis.go b/plugin/evm/orderbook/trading_apis.go index 61e86c0157..442f13999d 100644 --- a/plugin/evm/orderbook/trading_apis.go +++ b/plugin/evm/orderbook/trading_apis.go @@ -17,7 +17,6 @@ import ( "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" ) var traderFeed event.Feed diff --git a/precompile/contracts/jurorv2/matching_validation.go b/precompile/contracts/jurorv2/matching_validation.go index fe8dc96236..0b8d208434 100644 --- a/precompile/contracts/jurorv2/matching_validation.go +++ b/precompile/contracts/jurorv2/matching_validation.go @@ -4,7 +4,6 @@ import ( "errors" "math/big" - "github.com/ava-labs/subnet-evm/accounts/abi" ob "github.com/ava-labs/subnet-evm/plugin/evm/orderbook" hu "github.com/ava-labs/subnet-evm/plugin/evm/orderbook/hubbleutils" b "github.com/ava-labs/subnet-evm/precompile/contracts/bibliophile" From edfd06ef77777b63ae302509a7ff97f2ef66d486 Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Fri, 29 Dec 2023 14:34:57 +0000 Subject: [PATCH 11/12] fix test --- plugin/evm/orderbook/liquidations.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugin/evm/orderbook/liquidations.go b/plugin/evm/orderbook/liquidations.go index 43680fce98..bbcf4cc4e8 100644 --- a/plugin/evm/orderbook/liquidations.go +++ b/plugin/evm/orderbook/liquidations.go @@ -91,6 +91,9 @@ func getPositionMetadata(price *big.Int, openNotional *big.Int, size *big.Int, m } func prettifyScaledBigInt(number *big.Int, precision int8) string { + if number == nil { + return "0" + } return new(big.Float).Quo(new(big.Float).SetInt(number), big.NewFloat(math.Pow10(int(precision)))).String() } From 8179e436e5221d71c8905f71f89fc564c9f6dafb Mon Sep 17 00:00:00 2001 From: atvanguard <3612498+atvanguard@users.noreply.github.com> Date: Fri, 29 Dec 2023 14:42:06 +0000 Subject: [PATCH 12/12] resolve shubhams comments --- plugin/evm/orderbook/config_service.go | 4 ++-- plugin/evm/orderbook/mocks.go | 2 +- plugin/evm/orderbook/trading_apis.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugin/evm/orderbook/config_service.go b/plugin/evm/orderbook/config_service.go index db22a688b2..1fb898be99 100644 --- a/plugin/evm/orderbook/config_service.go +++ b/plugin/evm/orderbook/config_service.go @@ -28,7 +28,7 @@ type IConfigService interface { GetSignedOrderStatus(orderHash common.Hash) int64 IsTradingAuthority(trader, signer common.Address) bool - GetChainIdAndSignedOrderbookContract() common.Address + GetSignedOrderbookContract() common.Address } type ConfigService struct { @@ -112,6 +112,6 @@ func (cs *ConfigService) IsTradingAuthority(trader, signer common.Address) bool return bibliophile.IsTradingAuthority(cs.getStateAtCurrentBlock(), trader, signer) } -func (cs *ConfigService) GetChainIdAndSignedOrderbookContract() common.Address { +func (cs *ConfigService) GetSignedOrderbookContract() common.Address { return bibliophile.GetSignedOrderBookAddress(cs.getStateAtCurrentBlock()) } diff --git a/plugin/evm/orderbook/mocks.go b/plugin/evm/orderbook/mocks.go index 9419c34dbe..cc047ea6cf 100644 --- a/plugin/evm/orderbook/mocks.go +++ b/plugin/evm/orderbook/mocks.go @@ -311,6 +311,6 @@ func NewMockConfigService() *MockConfigService { return &MockConfigService{} } -func (cs *MockConfigService) GetChainIdAndSignedOrderbookContract() common.Address { +func (cs *MockConfigService) GetSignedOrderbookContract() common.Address { return common.Address{} } diff --git a/plugin/evm/orderbook/trading_apis.go b/plugin/evm/orderbook/trading_apis.go index 442f13999d..94ed0b70a7 100644 --- a/plugin/evm/orderbook/trading_apis.go +++ b/plugin/evm/orderbook/trading_apis.go @@ -317,7 +317,7 @@ type PlaceOrderResponse struct { Success bool `json:"success"` } -func (api *TradingAPI) PostOrder(ctx context.Context, rawOrder string) (PlaceOrderResponse, error) { +func (api *TradingAPI) PlaceSignedOrder(ctx context.Context, rawOrder string) (PlaceOrderResponse, error) { // fmt.Println("rawOrder", rawOrder) testData, err := hex.DecodeString(strings.TrimPrefix(rawOrder, "0x")) if err != nil { @@ -331,7 +331,7 @@ func (api *TradingAPI) PostOrder(ctx context.Context, rawOrder string) (PlaceOrd marketId := int(order.AmmIndex.Int64()) if hu.ChainId == 0 { // set once, will need to restart node if we change - hu.SetChainIdAndVerifyingSignedOrdersContract(api.backend.ChainConfig().ChainID.Int64(), api.configService.GetChainIdAndSignedOrderbookContract().String()) + hu.SetChainIdAndVerifyingSignedOrdersContract(api.backend.ChainConfig().ChainID.Int64(), api.configService.GetSignedOrderbookContract().String()) } orderId, err := order.Hash() if err != nil {