Skip to content

Commit

Permalink
add position cap check in juror
Browse files Browse the repository at this point in the history
  • Loading branch information
0xshinobii committed Mar 13, 2024
1 parent 8b16406 commit 35cf2f2
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 3 deletions.
12 changes: 12 additions & 0 deletions precompile/contracts/bibliophile/amm.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (
ISOLATED_TRADE_MARGIN_FRACTION_SLOT int64 = 30
ISOLATED_LIQUIDATION_MARGIN_FRACTION_SLOT int64 = 31
ACCOUNT_PREFERENCES_SLOT int64 = 33
MAX_POSITION_CAP_SLOT int64 = 34
)

// AMM State
Expand Down Expand Up @@ -212,6 +213,17 @@ func getRequiredMarginForQuote(stateDB contract.StateDB, market common.Address,
return hu.Div1e6(hu.Mul(quote, marginFraction))
}

func getMaxPositionCap(stateDB contract.StateDB, market common.Address) *big.Int {
return stateDB.GetState(market, common.BigToHash(big.NewInt(MAX_POSITION_CAP_SLOT))).Big()
}

func getPositionCap(stateDB contract.StateDB, market int64, trader *common.Address) *big.Int {
marketAddress := GetMarketAddressFromMarketID(market, stateDB)
maxPositionCap := getMaxPositionCap(stateDB, marketAddress)
traderMarginFraction := getTraderMarginFraction(stateDB, marketAddress, trader)
return hu.Div1e6(hu.Mul(maxPositionCap, traderMarginFraction))
}

// Utils

func getPendingFundingPayment(stateDB contract.StateDB, market common.Address, trader *common.Address) *big.Int {
Expand Down
10 changes: 10 additions & 0 deletions precompile/contracts/bibliophile/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type BibliophileClient interface {
GetBlockPlaced(orderHash [32]byte) *big.Int
GetOrderFilledAmount(orderHash [32]byte) *big.Int
GetOrderStatus(orderHash [32]byte) int64
GetPrecompileVersion(precompileAddress common.Address) *big.Int

// IOC Order
IOC_GetBlockPlaced(orderHash [32]byte) *big.Int
Expand All @@ -51,6 +52,7 @@ type BibliophileClient interface {
GetPriceMultiplier(market common.Address) *big.Int
GetUpperAndLowerBoundForMarket(marketId int64) (*big.Int, *big.Int)
GetAcceptableBoundsForLiquidation(marketId int64) (*big.Int, *big.Int)
GetPositionCap(marketId int64, trader common.Address) *big.Int

GetTimeStamp() uint64
GetNotionalPositionAndMargin(trader common.Address, includeFundingPayments bool, mode uint8, upgradeVersion hu.UpgradeVersion) (*big.Int, *big.Int)
Expand Down Expand Up @@ -243,3 +245,11 @@ func (b *bibliophileClient) GetRequiredMargin(baseAsset *big.Int, price *big.Int
func (b *bibliophileClient) HasReferrer(trader common.Address) bool {
return HasReferrer(b.accessibleState.GetStateDB(), trader)
}

func (b *bibliophileClient) GetPositionCap(marketId int64, trader common.Address) *big.Int {
return getPositionCap(b.accessibleState.GetStateDB(), marketId, &trader)
}

func (b *bibliophileClient) GetPrecompileVersion(precompileAddress common.Address) *big.Int {
return getPrecompileVersion(b.accessibleState.GetStateDB(), precompileAddress)
}
6 changes: 6 additions & 0 deletions precompile/contracts/bibliophile/limit_order_book.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
REDUCE_ONLY_AMOUNT_SLOT int64 = 2
LONG_OPEN_ORDERS_SLOT int64 = 4
SHORT_OPEN_ORDERS_SLOT int64 = 5
PRECOMPILE_VERSION_SLOT int64 = 8
)

func getOrderFilledAmount(stateDB contract.StateDB, orderHash [32]byte) *big.Int {
Expand Down Expand Up @@ -53,3 +54,8 @@ func getBlockPlaced(stateDB contract.StateDB, orderHash [32]byte) *big.Int {
orderInfo := orderInfoMappingStorageSlot(orderHash)
return new(big.Int).SetBytes(stateDB.GetState(common.HexToAddress(LIMIT_ORDERBOOK_GENESIS_ADDRESS), common.BigToHash(orderInfo)).Bytes())
}

func getPrecompileVersion(stateDB contract.StateDB, precompileAddress common.Address) *big.Int {
slot := new(big.Int).SetBytes(crypto.Keccak256(append(common.LeftPadBytes(precompileAddress.Bytes(), 32), common.LeftPadBytes(big.NewInt(PRECOMPILE_VERSION_SLOT).Bytes(), 32)...)))
return stateDB.GetState(common.HexToAddress(LIMIT_ORDERBOOK_GENESIS_ADDRESS), common.BigToHash(slot)).Big()
}
11 changes: 10 additions & 1 deletion precompile/contracts/juror/ioc_orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ func ValidatePlaceIOCorder(bibliophile b.BibliophileClient, inputStruct *Validat
}

// this check is sort of redundant because either ways user can circumvent this by placing several reduceOnly order in a single tx/block
posSize := bibliophile.GetSize(ammAddress, &trader)
if order.ReduceOnly {
posSize := bibliophile.GetSize(ammAddress, &trader)
// a reduce only order should reduce position
if !reducesPosition(posSize, order.BaseAssetQuantity) {
response.Err = ErrReduceOnlyBaseAssetQuantityInvalid.Error()
Expand All @@ -91,6 +91,15 @@ func ValidatePlaceIOCorder(bibliophile b.BibliophileClient, inputStruct *Validat
response.Err = ErrPricePrecision.Error()
return
}

if bibliophile.GetPrecompileVersion(common.HexToAddress(SelfAddress)).Cmp(big.NewInt(1)) >= 0 {
posCap := bibliophile.GetPositionCap(order.AmmIndex.Int64(), trader)
if hu.Abs(hu.Add(posSize, order.BaseAssetQuantity)).Cmp(posCap) == 1 {
response.Err = ErrOverPositionCap.Error()
return
}
}

return response
}

Expand Down
21 changes: 21 additions & 0 deletions precompile/contracts/juror/limit_orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
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"
"github.com/ethereum/go-ethereum/common/math"
)

func ValidatePlaceLimitOrder(bibliophile b.BibliophileClient, inputStruct *ValidatePlaceLimitOrderInput) (response ValidatePlaceLimitOrderOutput) {
Expand Down Expand Up @@ -62,6 +63,13 @@ func ValidatePlaceLimitOrder(bibliophile b.BibliophileClient, inputStruct *Valid
return
}

if bibliophile.GetPrecompileVersion(common.HexToAddress(SelfAddress)).Cmp(big.NewInt(1)) >= 0 {
if isOverPositionCap(bibliophile, trader, order.AmmIndex, order.BaseAssetQuantity) {
response.Err = ErrOverPositionCap.Error()
return
}
}

var orderSide Side = Side(Long)
if order.BaseAssetQuantity.Sign() == -1 {
orderSide = Side(Short)
Expand Down Expand Up @@ -181,3 +189,16 @@ func ILimitOrderBookOrderToLimitOrder(o *ILimitOrderBookOrder) *ob.LimitOrder {
func GetLimitOrderHashFromContractStruct(o *ILimitOrderBookOrder) (common.Hash, error) {
return ILimitOrderBookOrderToLimitOrder(o).Hash()
}

func isOverPositionCap(bibliophile b.BibliophileClient, trader common.Address, ammIndex *big.Int, baseAssetQuantity *big.Int) bool {
posSize := bibliophile.GetSize(bibliophile.GetMarketAddressFromMarketID(ammIndex.Int64()), &trader)
posCap := bibliophile.GetPositionCap(ammIndex.Int64(), trader)
longOpenOrdersAmount := bibliophile.GetLongOpenOrdersAmount(trader, ammIndex)
if baseAssetQuantity.Sign() == 1 {
posSize = math.BigMax(posSize, big.NewInt(0))
return baseAssetQuantity.Cmp(hu.Sub(posCap, hu.Add(posSize, longOpenOrdersAmount))) == 1
}
shortOpenOrdersAmount := bibliophile.GetShortOpenOrdersAmount(trader, ammIndex)
posSize = math.BigMax(hu.Neg(posSize), big.NewInt(0))
return hu.Neg(baseAssetQuantity).Cmp(hu.Sub(posCap, hu.Add(posSize, shortOpenOrdersAmount))) == 1
}
10 changes: 9 additions & 1 deletion precompile/contracts/juror/matching_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ var (
ErrOpenReduceOnlyOrders = errors.New("open reduce only orders")
ErrNoTradingAuthority = errors.New("no trading authority")
ErrNoReferrer = errors.New("no referrer")
ErrOverPositionCap = errors.New("position size over max cap")
)

type BadElement uint8
Expand Down Expand Up @@ -379,6 +380,7 @@ func validateLimitOrderLike(bibliophile b.BibliophileClient, order *hu.BaseOrder
}

market := bibliophile.GetMarketAddressFromMarketID(order.AmmIndex.Int64())
posSize := bibliophile.GetSize(market, &order.Trader)
if side == Long {
if order.BaseAssetQuantity.Sign() <= 0 {
return ErrNotLongOrder
Expand All @@ -390,7 +392,6 @@ func validateLimitOrderLike(bibliophile b.BibliophileClient, order *hu.BaseOrder
return ErrOverFill
}
if order.ReduceOnly {
posSize := bibliophile.GetSize(market, &order.Trader)
// posSize should be closed to continue to be Short
// this also returns err if posSize >= 0, which should not happen because we are executing a long reduceOnly order on this account
if new(big.Int).Add(posSize, fillAmount).Sign() > 0 {
Expand Down Expand Up @@ -418,6 +419,13 @@ func validateLimitOrderLike(bibliophile b.BibliophileClient, order *hu.BaseOrder
} else {
return errors.New("invalid side")
}

if bibliophile.GetPrecompileVersion(common.HexToAddress(SelfAddress)).Cmp(big.NewInt(1)) >= 0 {
posCap := bibliophile.GetPositionCap(order.AmmIndex.Int64(), order.Trader)
if hu.Abs(hu.Add(posSize, order.BaseAssetQuantity)).Cmp(posCap) == 1 {
return ErrOverPositionCap
}
}
return nil
}

Expand Down
3 changes: 2 additions & 1 deletion precompile/contracts/juror/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,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 SelfAddress = "0x03000000000000000000000000000000000000a0"

// 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(SelfAddress) // SET A SUITABLE HEX ADDRESS HERE

// Module is the precompile module. It is used to register the precompile contract.
var Module = modules.Module{
Expand Down

0 comments on commit 35cf2f2

Please sign in to comment.