Skip to content

Commit

Permalink
Implement TakerTraits type for 1inch limit order (#79)
Browse files Browse the repository at this point in the history
* Implement TakerTraits type for 1inch limit order
  • Loading branch information
hiepnv90 authored Aug 29, 2024
1 parent 41eaf08 commit 46ac751
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 9 deletions.
6 changes: 3 additions & 3 deletions pkg/oneinch/limitorder/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ func (e Extension) IsEmpty() bool {
return len(e.getConcatenatedInteractions()) == 0
}

func (e Extension) Encode() string {
func (e Extension) Encode() []byte {
interactionsConcatenated := e.getConcatenatedInteractions()
if len(interactionsConcatenated) == 0 {
return hexutil.Encode(interactionsConcatenated)
return interactionsConcatenated
}

offset := e.getOffsets()
Expand All @@ -54,7 +54,7 @@ func (e Extension) Encode() string {
b.Write(interactionsConcatenated)
b.Write(e.CustomData)

return hexutil.Encode(b.Bytes())
return b.Bytes()
}

func (e Extension) interactionsArray() [totalOffsetSlots][]byte {
Expand Down
3 changes: 2 additions & 1 deletion pkg/oneinch/limitorder/extension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/KyberNetwork/tradinglib/pkg/oneinch/limitorder"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -24,7 +25,7 @@ func TestExtension(t *testing.T) {

encodedExtension := extension.Encode()

decodedExtension, err := limitorder.DecodeExtension(encodedExtension)
decodedExtension, err := limitorder.DecodeExtension(hexutil.Encode(encodedExtension))
require.NoError(t, err)

require.Equal(t, extension, decodedExtension)
Expand Down
6 changes: 3 additions & 3 deletions pkg/oneinch/limitorder/interaction.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package limitorder

import (
"encoding/hex"
"fmt"

"github.com/KyberNetwork/tradinglib/pkg/oneinch/decode"
Expand All @@ -17,8 +16,9 @@ func (i Interaction) IsZero() bool {
return i.Target.String() == common.Address{}.String() && len(i.Data) == 0
}

func (i Interaction) Encode() string {
return i.Target.String() + hex.EncodeToString(i.Data)
func (i Interaction) Encode() []byte {
res := make([]byte, 0, len(i.Target)+len(i.Data))
return append(append(res, i.Target.Bytes()...), i.Data...)
}

func DecodeInteraction(data []byte) (Interaction, error) {
Expand Down
3 changes: 1 addition & 2 deletions pkg/oneinch/limitorder/interaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ func TestInteraction(t *testing.T) {
Data: data,
}

encodedInteraction, err := hexutil.Decode(interaction.Encode())
require.NoError(t, err)
encodedInteraction := interaction.Encode()

decodedInteraction, err := limitorder.DecodeInteraction(encodedInteraction)
require.NoError(t, err)
Expand Down
101 changes: 101 additions & 0 deletions pkg/oneinch/limitorder/taker_traits.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package limitorder

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
)

type AmountMode uint

const (
makerAmountFlag = 255
argsHasReceiver = 251

AmountModeTaker AmountMode = 0
AmountModeMaker AmountMode = 1
)

//nolint:gochecknoglobals,gomnd,mnd
var (
// 224-247 bits `ARGS_EXTENSION_LENGTH` - The length of the extension calldata in the args.
argsExtensionLenMask = newBitMask(224, 248)
// 200-223 bits `ARGS_INTERACTION_LENGTH` - The length of the interaction calldata in the args.
argsInteractionLenMask = newBitMask(200, 224)
// 0-184 bits - The threshold amount.
amountThresholdMask = newBitMask(0, 185)
)

type TakerTraits struct {
flags *big.Int
receiver *common.Address
extension *Extension
interaction *Interaction
}

func NewTakerTraits(
flags *big.Int, receiver *common.Address, extension *Extension, interaction *Interaction,
) *TakerTraits {
return &TakerTraits{
flags: flags,
receiver: receiver,
extension: extension,
interaction: interaction,
}
}

func NewDefaultTakerTraits() *TakerTraits {
return &TakerTraits{
flags: new(big.Int),
}
}

func (t *TakerTraits) SetAmountMode(mode AmountMode) *TakerTraits {
t.flags.SetBit(t.flags, makerAmountFlag, uint(mode))
return t
}

// SetAmountThreshold sets threshold amount.
//
// In taker amount mode: the minimum amount a taker agrees to receive in exchange for a taking amount.
// In maker amount mode: the maximum amount a taker agrees to give in exchange for a making amount.
func (t *TakerTraits) SetAmountThreshold(threshold *big.Int) *TakerTraits {
setMask(t.flags, amountThresholdMask, threshold)
return t
}

// SetExtension sets extension, it is required to provide same extension as in order creation (if any).
func (t *TakerTraits) SetExtension(ext Extension) *TakerTraits {
t.extension = &ext
return t
}

func (t *TakerTraits) Encode() (common.Hash, []byte) {
var extension, interaction []byte
if t.extension != nil {
extension = t.extension.Encode()
}
if t.interaction != nil {
interaction = t.interaction.Encode()
}

flags := new(big.Int).Set(t.flags)
if t.receiver != nil {
flags.SetBit(flags, argsHasReceiver, 1)
}

// Set length for extension and interaction.
setMask(flags, argsExtensionLenMask, big.NewInt(int64(len(extension))))
setMask(flags, argsInteractionLenMask, big.NewInt(int64(len(interaction))))

var args []byte
if t.receiver == nil {
args = make([]byte, 0, len(extension)+len(interaction))
} else {
args = make([]byte, 0, len(t.receiver)+len(extension)+len(interaction))
args = append(args, t.receiver.Bytes()...)
}
args = append(append(args, extension...), interaction...)

return common.BigToHash(flags), args
}
33 changes: 33 additions & 0 deletions pkg/oneinch/limitorder/taker_traits_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//nolint:testpackage
package limitorder

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/assert"
)

//nolint:lll
func TestEncodeTakerTraits(t *testing.T) {
extension := Extension{
MakerAssetSuffix: []byte{0x01},
TakerAssetSuffix: []byte{0x02},
MakingAmountData: []byte{0x03},
TakingAmountData: []byte{0x04},
Predicate: []byte{0x05},
MakerPermit: []byte{0x06},
PreInteraction: []byte{0x07},
PostInteraction: []byte{0x08},
CustomData: []byte{0xff},
}

takerTraits := NewDefaultTakerTraits()
takerTraits.SetExtension(extension).SetAmountMode(AmountModeMaker).SetAmountThreshold(big.NewInt(1))

encodedTakerTraits, args := takerTraits.Encode()
assert.Equal(t, common.HexToHash("0x8000002900000000000000000000000000000000000000000000000000000001"), encodedTakerTraits)
assert.Equal(t, hexutil.Encode(extension.Encode()), hexutil.Encode(args))
}
30 changes: 30 additions & 0 deletions pkg/oneinch/limitorder/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package limitorder

import "math/big"

func newBitMask(start uint, end uint) *big.Int {
mask := big.NewInt(1)
mask.Lsh(mask, end)
mask.Sub(mask, big.NewInt(1))
if start == 0 {
return mask
}

notMask := newBitMask(0, start)
notMask.Not(notMask)
mask.And(mask, notMask)

return mask
}

func setMask(n *big.Int, mask *big.Int, value *big.Int) {
// Clear bits in range.
n.And(n, new(big.Int).Not(mask))

// Shift value to correct position and ensure value fits in mask.
value = new(big.Int).Lsh(value, mask.TrailingZeroBits())
value.And(value, mask)

// Set the bits in range.
n.Or(n, value)
}
71 changes: 71 additions & 0 deletions pkg/oneinch/limitorder/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//nolint:testpackage
package limitorder

import (
"math/big"
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewBitMask(t *testing.T) {
tests := []struct {
start uint
end uint
expect *big.Int
}{
{
start: 0,
end: 10,
expect: big.NewInt(0b1111111111),
},
{
start: 5,
end: 17,
expect: big.NewInt(0b11111111111100000),
},
}

for _, test := range tests {
assert.Equal(t, test.expect, newBitMask(test.start, test.end))
}
}

func TestSetMask(t *testing.T) {
tests := []struct {
n *big.Int
mask *big.Int
value *big.Int
expect *big.Int
}{
{
n: big.NewInt(0b1111111111111110101111111111100111),
mask: newBitMask(0, 10),
value: big.NewInt(0b0011110101),
expect: big.NewInt(0b1111111111111110101111110011110101),
},
{
n: big.NewInt(0b1111111111111110101111111111100111),
mask: newBitMask(0, 10),
value: big.NewInt(0b11110011110101),
expect: big.NewInt(0b1111111111111110101111110011110101),
},
{
n: big.NewInt(0b1111111111111110101111111111100111),
mask: newBitMask(5, 15),
value: big.NewInt(0b0011110101),
expect: big.NewInt(0b1111111111111110101001111010100111),
},
{
n: big.NewInt(0b1111111111111110101111111111100111),
mask: newBitMask(5, 15),
value: big.NewInt(0b11110011110101),
expect: big.NewInt(0b1111111111111110101001111010100111),
},
}

for _, test := range tests {
setMask(test.n, test.mask, test.value)
assert.Equal(t, test.expect, test.n, "expect: %s, actual: %s", test.expect.Text(2), test.n.Text(2))
}
}

0 comments on commit 46ac751

Please sign in to comment.