Skip to content

Commit

Permalink
EX-2684: filter out limit order by allowSenders (#676)
Browse files Browse the repository at this point in the history
* feat: filter out limit order by allowSenders

* feat: add allow senders inventory

* chore: update swap limit interface

* feat: update limit order to filter orders by allowedSenders

* feat: apply allowedSenders in limit order simulation

* chore: early return for filter order ids
  • Loading branch information
lehainam-dev authored Jan 9, 2025
1 parent 9814004 commit 9b9c869
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 0 deletions.
30 changes: 30 additions & 0 deletions pkg/source/limitorder/pool_simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

"github.com/KyberNetwork/logger"
"github.com/ethereum/go-ethereum/common"
"github.com/goccy/go-json"
"github.com/samber/lo"

Expand Down Expand Up @@ -217,6 +218,10 @@ func getMakerRemainingBalance(

func (p *PoolSimulator) calcAmountOutWithSwapInfo(swapSide SwapSide, tokenAmountIn pool.TokenAmount, limit pool.SwapLimit) (*big.Int, SwapInfo, *big.Int, error) {
orderIDs := p.getOrderIDsBySwapSide(swapSide)
if limit != nil {
// EX-2684: Filter out orders that are not in allowedSenders list.
orderIDs = p.filterOrdersByAllowedSenders(orderIDs, limit.GetAllowedSenders())
}
if len(orderIDs) == 0 {
return big.NewInt(0), SwapInfo{}, nil, nil
}
Expand Down Expand Up @@ -409,6 +414,31 @@ func (p *PoolSimulator) getSwapSide(tokenIn string, TokenOut string) SwapSide {
return Buy
}

// filterOrdersByAllowedSenders returns orderIDs that have order.allowedSender
// either in the allowedSenders, or is empty value.
func (p *PoolSimulator) filterOrdersByAllowedSenders(orderIDs []int64, allowedSenders string) []int64 {
allowedSendersSlice := lo.Filter(strings.Split(allowedSenders, ","), func(s string, _ int) bool {
return s != ""
})

if len(allowedSendersSlice) == 0 {
return orderIDs
}

allowedSendersAddress := lo.Map(allowedSendersSlice, func(s string, _ int) common.Address {
return common.HexToAddress(s)
})

return lo.Filter(orderIDs, func(orderID int64, _ int) bool {
order := p.ordersMapping[orderID]
orderAllowedSender := common.HexToAddress(order.AllowedSenders)
// order.AllowedSenders can be multiple, separate by ','.
// We only check for single allowedSenders address for now.

return orderAllowedSender == (common.Address{}) || lo.Contains(allowedSendersAddress, orderAllowedSender)
})
}

func (p *PoolSimulator) GetMetaInfo(_ string, _ string) interface{} {
return p.contractAddress
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/source/limitorder/pool_simulator_calc_amount_in.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ func (p *PoolSimulator) calcAmountIn(

func (p *PoolSimulator) calcAmountInWithSwapInfo(swapSide SwapSide, tokenAmountOut pool.TokenAmount, limit pool.SwapLimit) (*big.Int, SwapInfo, *big.Int, error) {
orderIDs := p.getOrderIDsBySwapSide(swapSide)
if limit != nil {
orderIDs = p.filterOrdersByAllowedSenders(orderIDs, limit.GetAllowedSenders())
}
if len(orderIDs) == 0 {
return big.NewInt(0), SwapInfo{}, nil, nil
}
Expand Down
87 changes: 87 additions & 0 deletions pkg/source/limitorder/pool_simulator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1375,3 +1375,90 @@ func TestPool_CalcAmountOut_TakerAssetFee(t *testing.T) {
})
}
}

func TestPool_FilterAllowedSenders(t *testing.T) {
extra := Extra{
BuyOrders: []*order{
{
ID: 1,
AllowedSenders: "0x0000000000000000000000000000000000000000",
MakerAsset: "A", TakerAsset: "B",
MakingAmount: parseBigInt("1"),
TakingAmount: parseBigInt("1"),
AvailableMakingAmount: parseBigInt("100"),
MakerBalanceAllowance: parseBigInt("100000000000000000000"),
},
{
ID: 2,
AllowedSenders: "0xf081470f5C6FBCCF48cC4e5B82Dd926409DcdD67",
MakerAsset: "A", TakerAsset: "B",
MakingAmount: parseBigInt("1"),
TakingAmount: parseBigInt("1"),
AvailableMakingAmount: parseBigInt("100"),
MakerBalanceAllowance: parseBigInt("100000000000000000000"),
},
{
ID: 3,
AllowedSenders: "0xf081470f5c6fbccf48cc4e5b82dd926409dcdd67",
MakerAsset: "A", TakerAsset: "B",
MakingAmount: parseBigInt("1"),
TakingAmount: parseBigInt("1"),
AvailableMakingAmount: parseBigInt("100"),
MakerBalanceAllowance: parseBigInt("100000000000000000000"),
},
{
ID: 4,
AllowedSenders: "0x11ddD59C33c73C44733b4123a86Ea5ce57F6e854",
MakerAsset: "A", TakerAsset: "B",
MakingAmount: parseBigInt("1"),
TakingAmount: parseBigInt("1"),
AvailableMakingAmount: parseBigInt("100"),
MakerBalanceAllowance: parseBigInt("100000000000000000000"),
},
},
}
sExtra, _ := json.Marshal(extra)
poolEnt := entity.Pool{
Tokens: []*entity.PoolToken{{Address: "A"}, {Address: "B"}},
Reserves: entity.PoolReserves{"10000", "10000"},
StaticExtra: `{"ContractAddress":""}`,
Extra: string(sExtra),
}

p, err := NewPoolSimulator(poolEnt)
assert.NoError(t, err)

// Use all orders if we don't pass swapLimit
res, _ := p.CalcAmountOut(pool.CalcAmountOutParams{
TokenAmountIn: pool.TokenAmount{
Token: "A",
Amount: parseBigInt("400"),
},
TokenOut: "B",
Limit: nil,
})

assert.Equal(t, "400", res.TokenAmountOut.Amount.String())

// Use the first 3 orders when filter allowedSenders
// Use all orders if we don't pass swapLimit
_, err = p.CalcAmountOut(pool.CalcAmountOutParams{
TokenAmountIn: pool.TokenAmount{
Token: "A",
Amount: parseBigInt("400"),
},
TokenOut: "B",
Limit: swaplimit.NewInventoryWithAllowedSenders("", p.CalculateLimit(), "0xf081470f5C6FBCCF48cC4e5B82Dd926409DcdD67"),
})
assert.Equal(t, ErrCannotFulfillAmountIn, err)

res, _ = p.CalcAmountOut(pool.CalcAmountOutParams{
TokenAmountIn: pool.TokenAmount{
Token: "A",
Amount: parseBigInt("300"),
},
TokenOut: "B",
Limit: swaplimit.NewInventoryWithAllowedSenders("", p.CalculateLimit(), "0xf081470f5C6FBCCF48cC4e5B82Dd926409DcdD67"),
})
assert.Equal(t, "300", res.TokenAmountOut.Amount.String())
}
2 changes: 2 additions & 0 deletions pkg/source/pool/swap_limit.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type SwapLimit interface {
GetLimit(key string) *big.Int
// GetSwapped returns the amount has been swapped through pools
GetSwapped() map[string]*big.Int
// GetAllowedSenders returns a list of addresses that are allowed to swap through Limit Order.
GetAllowedSenders() string
// UpdateLimit updates both limits for a trade (assuming each trade is from 1 token to another token)
// It returns the new limits for other purposes
UpdateLimit(decreaseKey, increasedKey string, decreasedDelta, increasedDelta *big.Int) (increasedLimitAfter *big.Int, decreasedLimitAfter *big.Int, err error)
Expand Down
51 changes: 51 additions & 0 deletions pkg/swaplimit/allowed_senders_inventory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package swaplimit

import (
"maps"
"math/big"
"sync"

"github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool"
)

type InventoryWithAllowedSenders struct {
Inventory
AllowedSenders string
}

func NewInventoryWithAllowedSenders(
exchange string,
balance map[string]*big.Int,
allowedSenders string,
) *InventoryWithAllowedSenders {
return &InventoryWithAllowedSenders{
Inventory: Inventory{
exchange: exchange,
lock: &sync.RWMutex{},
balance: balance,
},
AllowedSenders: allowedSenders,
}
}

func (i *InventoryWithAllowedSenders) GetAllowedSenders() string {
return i.AllowedSenders
}

func (i *InventoryWithAllowedSenders) UpdateLimit(
decreaseTokenAddress, increaseTokenAddress string,
decreaseDelta, increaseDelta *big.Int,
) (*big.Int, *big.Int, error) {
return i.Inventory.updateLimit(decreaseTokenAddress, increaseTokenAddress, decreaseDelta, increaseDelta)
}

func (i *InventoryWithAllowedSenders) Clone() pool.SwapLimit {
return &InventoryWithAllowedSenders{
Inventory: Inventory{
exchange: i.exchange,
lock: &sync.RWMutex{},
balance: maps.Clone(i.balance),
},
AllowedSenders: i.AllowedSenders,
}
}
4 changes: 4 additions & 0 deletions pkg/swaplimit/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ func (i *Inventory) GetSwapped() map[string]*big.Int {
return nil
}

func (i *Inventory) GetAllowedSenders() string {
return ""
}

// CheckLimit returns the balance for the token in Inventory. Do not modify the result.
func (i *Inventory) CheckLimit(tokenAddress string, amount *big.Int) error {
i.lock.RLock()
Expand Down
4 changes: 4 additions & 0 deletions pkg/swaplimit/single_swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ func (l *SingleSwapLimit) GetSwapped() map[string]*big.Int {
return nil
}

func (i *SingleSwapLimit) GetAllowedSenders() string {
return ""
}

// UpdateLimit updates the atomic bool to mark swap done
func (l *SingleSwapLimit) UpdateLimit(_, _ string, _, _ *big.Int) (*big.Int, *big.Int, error) {
l.swapped.Store(true)
Expand Down

0 comments on commit 9b9c869

Please sign in to comment.