Skip to content

Commit

Permalink
misc precompile updates
Browse files Browse the repository at this point in the history
  • Loading branch information
atvanguard committed Sep 19, 2023
1 parent ac44d3e commit aedb263
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 24 deletions.
4 changes: 4 additions & 0 deletions plugin/evm/orderbook/hubbleutils/hubble_math.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ func Mul(a, b *big.Int) *big.Int {
func Div(a, b *big.Int) *big.Int {
return new(big.Int).Div(a, b)
}

func Abs(a *big.Int) *big.Int {
return new(big.Int).Abs(a)
}
6 changes: 6 additions & 0 deletions precompile/contracts/bibliophile/clearing_house.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
MIN_ALLOWABLE_MARGIN_SLOT int64 = 2
TAKER_FEE_SLOT int64 = 3
AMMS_SLOT int64 = 12
REFERRAL_SLOT int64 = 13
)

type MarginMode uint8
Expand Down Expand Up @@ -134,3 +135,8 @@ func getMarketAddressFromMarketID(marketID int64, stateDB contract.StateDB) comm
amm := stateDB.GetState(common.HexToAddress(CLEARING_HOUSE_GENESIS_ADDRESS), common.BigToHash(new(big.Int).Add(baseStorageSlot, big.NewInt(marketID))))
return common.BytesToAddress(amm.Bytes())
}

func getReferralAddress(stateDB contract.StateDB) common.Address {
referral := stateDB.GetState(common.HexToAddress(CLEARING_HOUSE_GENESIS_ADDRESS), common.BigToHash(big.NewInt(REFERRAL_SLOT)))
return common.BytesToAddress(referral.Bytes())
}
5 changes: 5 additions & 0 deletions precompile/contracts/bibliophile/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type BibliophileClient interface {

GetAccessibleState() contract.AccessibleState
GetNotionalPositionAndMargin(trader common.Address, includeFundingPayments bool, mode uint8) (*big.Int, *big.Int)
HasReferrer(trader common.Address) bool
}

// Define a structure that will implement the Bibliophile interface
Expand Down Expand Up @@ -179,3 +180,7 @@ func (b *bibliophileClient) GetNotionalPositionAndMargin(trader common.Address,
output := getNotionalPositionAndMargin(b.accessibleState.GetStateDB(), &GetNotionalPositionAndMarginInput{Trader: trader, IncludeFundingPayments: includeFundingPayments, Mode: mode})
return output.NotionalPosition, output.Margin
}

func (b *bibliophileClient) HasReferrer(trader common.Address) bool {
return hasReferrer(b.accessibleState.GetStateDB(), trader)
}
29 changes: 29 additions & 0 deletions precompile/contracts/bibliophile/referral.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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"
)

const (
TRADER_TO_REFERRER_SLOT int64 = 3
RESTRICTED_INVITES_SLOT int64 = 6
)

func restrictedInvites(stateDB contract.StateDB, referralContract common.Address) bool {
return stateDB.GetState(referralContract, common.BigToHash(big.NewInt(RESTRICTED_INVITES_SLOT))).Big().Uint64() == 1
}

func traderToReferrer(stateDB contract.StateDB, referralContract, trader common.Address) common.Address {
pos := crypto.Keccak256(append(common.LeftPadBytes(trader.Bytes(), 32), common.LeftPadBytes(big.NewInt(TRADER_TO_REFERRER_SLOT).Bytes(), 32)...))
return common.BytesToAddress(stateDB.GetState(referralContract, common.BytesToHash(pos)).Bytes())
}

func hasReferrer(stateDB contract.StateDB, trader common.Address) bool {
referralContract := getReferralAddress(stateDB)
return !restrictedInvites(stateDB, referralContract) || traderToReferrer(stateDB, referralContract, trader) != common.Address{}
}
2 changes: 1 addition & 1 deletion precompile/contracts/juror/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ type ValidatePlaceLimitOrderInput struct {
}

type ValidatePlaceLimitOrderOutput struct {
Errs string
Err string
Orderhash [32]byte
Res IOrderHandlerPlaceOrderRes
}
Expand Down
24 changes: 21 additions & 3 deletions precompile/contracts/juror/ioc_orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -51,10 +52,27 @@ func ValidatePlaceIOCorder(bibliophile b.BibliophileClient, inputStruct *Validat
response.Err = ErrInvalidOrder.Error()
return
}

if !bibliophile.HasReferrer(order.Trader) {
response.Err = ErrNoReferrer.Error()
}

// this check is sort of redundant because either ways user can circumvent this by placing several reduceOnly order in a single tx/block
// if order.ReduceOnly {
// @todo
// }
if order.ReduceOnly {
ammAddress := bibliophile.GetMarketAddressFromMarketID(order.AmmIndex.Int64())
posSize := bibliophile.GetSize(ammAddress, &trader)
// a reduce only order should reduce position
if !reducesPosition(posSize, order.BaseAssetQuantity) {
response.Err = ErrReduceOnlyBaseAssetQuantityInvalid.Error()
return
}

reduceOnlyAmount := bibliophile.GetReduceOnlyAmount(trader, order.AmmIndex)
if hu.Abs(hu.Add(reduceOnlyAmount, order.BaseAssetQuantity)).Cmp(hu.Abs(posSize)) == 1 {
response.Err = ErrNetReduceOnlyAmountExceeded.Error()
return
}
}
return response
}

Expand Down
60 changes: 40 additions & 20 deletions precompile/contracts/juror/limit_orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,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/ethereum/go-ethereum/common"
)
Expand All @@ -18,32 +19,32 @@ func ValidatePlaceLimitOrder(bibliophile b.BibliophileClient, inputStruct *Valid
response.Orderhash = orderHash

if err != nil {
response.Errs = err.Error()
response.Err = err.Error()
return
}
if order.Price.Sign() != 1 {
response.Errs = ErrInvalidPrice.Error()
response.Err = ErrInvalidPrice.Error()
return
}
trader := order.Trader
if trader != sender && !bibliophile.IsTradingAuthority(trader, sender) {
response.Errs = ErrNoTradingAuthority.Error()
response.Err = ErrNoTradingAuthority.Error()
return
}
ammAddress := bibliophile.GetMarketAddressFromMarketID(order.AmmIndex.Int64())
response.Res.Amm = ammAddress
if order.BaseAssetQuantity.Sign() == 0 {
response.Errs = ErrBaseAssetQuantityZero.Error()
response.Err = ErrBaseAssetQuantityZero.Error()
return
}
minSize := bibliophile.GetMinSizeRequirement(order.AmmIndex.Int64())
if new(big.Int).Mod(order.BaseAssetQuantity, minSize).Sign() != 0 {
response.Errs = ErrNotMultiple.Error()
response.Err = ErrNotMultiple.Error()
return
}
status := OrderStatus(bibliophile.GetOrderStatus(orderHash))
if status != Invalid {
response.Errs = ErrOrderAlreadyExists.Error()
response.Err = ErrOrderAlreadyExists.Error()
return
}

Expand All @@ -52,7 +53,7 @@ func ValidatePlaceLimitOrder(bibliophile b.BibliophileClient, inputStruct *Valid
// this should only happen when a trader with open reduce only orders was liquidated
if (posSize.Sign() == 0 && reduceOnlyAmount.Sign() != 0) || (posSize.Sign() != 0 && new(big.Int).Mul(posSize, reduceOnlyAmount).Sign() == 1) {
// if position is non-zero then reduceOnlyAmount should be zero or have the opposite sign as posSize
response.Errs = ErrStaleReduceOnlyOrders.Error()
response.Err = ErrStaleReduceOnlyOrders.Error()
return
}

Expand All @@ -61,41 +62,60 @@ func ValidatePlaceLimitOrder(bibliophile b.BibliophileClient, inputStruct *Valid
orderSide = Side(Short)
}
if order.ReduceOnly {
// a reduce only order should reduce position
if !reducesPosition(posSize, order.BaseAssetQuantity) {
response.Errs = ErrReduceOnlyBaseAssetQuantityInvalid.Error()
response.Err = ErrReduceOnlyBaseAssetQuantityInvalid.Error()
return
}
longOrdersAmount := bibliophile.GetLongOpenOrdersAmount(trader, order.AmmIndex)
shortOrdersAmount := bibliophile.GetShortOpenOrdersAmount(trader, order.AmmIndex)
if (orderSide == Side(Long) && longOrdersAmount.Sign() != 0) || (orderSide == Side(Short) && shortOrdersAmount.Sign() != 0) {
response.Errs = ErrOpenOrders.Error()

// if the trader is placing a reduceOnly long that means they have a short position
// we allow only 1 kind of order in the opposite direction of the position
// otherwise we run the risk of having stale reduceOnly orders (orders that are not actually reducing the position)
if (orderSide == Side(Long) && longOrdersAmount.Sign() != 0) ||
(orderSide == Side(Short) && shortOrdersAmount.Sign() != 0) {
response.Err = ErrOpenOrders.Error()
return
}
if big.NewInt(0).Abs(big.NewInt(0).Add(reduceOnlyAmount, order.BaseAssetQuantity)).Cmp(big.NewInt(0).Abs(posSize)) == 1 {
response.Errs = ErrNetReduceOnlyAmountExceeded.Error()
if hu.Abs(hu.Add(reduceOnlyAmount, order.BaseAssetQuantity)).Cmp(hu.Abs(posSize)) == 1 {
response.Err = ErrNetReduceOnlyAmountExceeded.Error()
return
}
} else {
if reduceOnlyAmount.Sign() != 0 && order.BaseAssetQuantity.Sign() != posSize.Sign() {
response.Errs = ErrOpenReduceOnlyOrders.Error()
// we allow only 1 kind of order in the opposite direction of the position
if order.BaseAssetQuantity.Sign() != posSize.Sign() && reduceOnlyAmount.Sign() != 0 {
response.Err = ErrOpenReduceOnlyOrders.Error()
return
}
availableMargin := bibliophile.GetAvailableMargin(trader)
requiredMargin := getRequiredMargin(bibliophile, order)
if availableMargin.Cmp(requiredMargin) == -1 {
response.Errs = ErrInsufficientMargin.Error()
response.Err = ErrInsufficientMargin.Error()
return
}
response.Res.ReserveAmount = requiredMargin
}

if order.PostOnly {
asksHead := bibliophile.GetAsksHead(ammAddress)
bidsHead := bibliophile.GetBidsHead(ammAddress)
if (orderSide == Side(Short) && bidsHead.Sign() != 0 && order.Price.Cmp(bidsHead) != 1) || (orderSide == Side(Long) && asksHead.Sign() != 0 && order.Price.Cmp(asksHead) != -1) {
response.Errs = ErrCrossingMarket.Error()
return
if orderSide == Side(Short) {
bidsHead := bibliophile.GetBidsHead(ammAddress)
if bidsHead.Sign() != 0 && order.Price.Cmp(bidsHead) != 1 {
response.Err = ErrCrossingMarket.Error()
return
}
} else if orderSide == Side(Long) {
asksHead := bibliophile.GetAsksHead(ammAddress)
if asksHead.Sign() != 0 && order.Price.Cmp(asksHead) != -1 {
response.Err = ErrCrossingMarket.Error()
return
}
}
}

if !bibliophile.HasReferrer(order.Trader) {
response.Err = ErrNoReferrer.Error()
}
return response
}

Expand Down
1 change: 1 addition & 0 deletions precompile/contracts/juror/matching_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ var (
ErrOpenOrders = errors.New("open orders")
ErrOpenReduceOnlyOrders = errors.New("open reduce only orders")
ErrNoTradingAuthority = errors.New("no trading authority")
ErrNoReferrer = errors.New("no referrer")
)

type BadElement uint8
Expand Down

0 comments on commit aedb263

Please sign in to comment.