Skip to content

Commit

Permalink
fix: 🩹 lo1inch/encode: fix packLO1inch (#123)
Browse files Browse the repository at this point in the history
* fix: 🩹 lo1inch/encode: fix packLO1inch

- add test & simulation results

Signed-off-by: thanhpp <[email protected]>
---------

Signed-off-by: thanhpp <[email protected]>
  • Loading branch information
thanhpp authored Feb 18, 2025
1 parent 2c2d48f commit b89200e
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
uses: golangci/golangci-lint-action@v3
with:
version: v1.62.2
args: --config=.golangci.yml --timeout=10m
args: --config=.golangci.yml --timeout=20m
skip-pkg-cache: true
skip-build-cache: true

Expand Down
6 changes: 6 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ linters:
- perfsprint
- revive
- gochecknoglobals
- gomnd
- godot

linters-settings:
funlen:
Expand All @@ -55,6 +57,10 @@ linters-settings:
# Should ignore tests.
# Default: false
skip-tests: true
gosec:
exclude-generated: false
excludes:
- G115

issues:
exclude-rules:
Expand Down
112 changes: 101 additions & 11 deletions pkg/oneinch/encode/lo1inch.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,31 @@ import (
"github.com/ethereum/go-ethereum/common"
)

const (
MethodIDLength = 4
MethodFillOrderArgs = "fillOrderArgs"
MethodFillContractOrderArgs = "fillContractOrderArgs"
)

// https://github.com/KyberNetwork/aggregator-encoding/blob/v0.37.6/pkg/encode/l1encode/executor/swapdata/lo1inch.go#L19
func PackLO1inch(_ valueobject.ChainID, encodingSwap EncodingSwap) ([][]byte, error) { //nolint:funlen,cyclop
func PackLO1inch(_ valueobject.ChainID, encodingSwap EncodingSwap) ([][]byte, *big.Int, error) { //nolint:funlen,cyclop
// get contract address for LO.
if encodingSwap.PoolExtra == nil {
return nil, fmt.Errorf("[PackLO1inch] PoolExtra is nil")
return nil, nil, fmt.Errorf("[PackLO1inch] PoolExtra is nil")
}

byteData, err := json.Marshal(encodingSwap.Extra)
if err != nil {
return nil, fmt.Errorf("[buildLO1inch] ErrMarshalFailed err :[%w]", err)
return nil, nil, fmt.Errorf("[buildLO1inch] ErrMarshalFailed err :[%w]", err)
}

var swapInfo lo1inch.SwapInfo
if err = json.Unmarshal(byteData, &swapInfo); err != nil {
return nil, fmt.Errorf("[buildLO1inch] ErrUnmarshalFailed err :[%w]", err)
return nil, nil, fmt.Errorf("[buildLO1inch] ErrUnmarshalFailed err :[%w]", err)
}

if len(swapInfo.FilledOrders) == 0 {
return nil, fmt.Errorf("[buildLO1inch] cause by filledOrders is empty")
return nil, nil, fmt.Errorf("[buildLO1inch] cause by filledOrders is empty")
}

encodeds := make([][]byte, 0, len(swapInfo.FilledOrders))
Expand All @@ -49,6 +55,9 @@ func PackLO1inch(_ valueobject.ChainID, encodingSwap EncodingSwap) ([][]byte, er
orderRemainingTakingAmount.Div(orderRemainingTakingAmount, filledOrder.MakingAmount)

takingAmount := orderRemainingTakingAmount.ToBig()
if takingAmount.Sign() == 0 {
continue
}
switch amountIn.Cmp(takingAmount) {
case -1:
takingAmount.Set(amountIn)
Expand All @@ -63,7 +72,7 @@ func PackLO1inch(_ valueobject.ChainID, encodingSwap EncodingSwap) ([][]byte, er

extension, err := helper1inch.DecodeExtension(filledOrder.Extension)
if err != nil {
return nil, fmt.Errorf("decode extension: %w", err)
return nil, nil, fmt.Errorf("decode extension: %w", err)
}

receiver := helper1inch.NewAddress(encodingSwap.Recipient)
Expand Down Expand Up @@ -100,14 +109,14 @@ func PackLO1inch(_ valueobject.ChainID, encodingSwap EncodingSwap) ([][]byte, er
order, signature, filledOrder, takingAmount, takerTraitsEncoded.TakerTraits, takerTraitsEncoded.Args,
)
if err != nil {
return nil, fmt.Errorf("pack fillContractOrderArgs error: %w", err)
return nil, nil, fmt.Errorf("pack fillContractOrderArgs error: %w", err)
}
encodeds = append(encodeds, packed)

case false:
bytesSignature, err := helper1inch.LO1inchParseSignature(filledOrder.Signature)
if err != nil {
return nil, fmt.Errorf("parse lo1inch sig: %w", err)
return nil, nil, fmt.Errorf("parse lo1inch sig: %w", err)
}
r := bytesSignature.R
vs := bytesSignature.GetCompactedSignatureBytes()[len(r):]
Expand All @@ -123,15 +132,96 @@ func PackLO1inch(_ valueobject.ChainID, encodingSwap EncodingSwap) ([][]byte, er
)
external payable returns (uint256 makingAmount, uint256 takingAmount, bytes32 orderHash);
*/

var rArray, vsArray [32]byte
copy(rArray[:], r)
copy(vsArray[:], vs)
packed, err := OneInchAggregationRouterV6ABI.Pack("fillOrderArgs",
order, r, vs, takingAmount, takerTraitsEncoded.TakerTraits, takerTraitsEncoded.Args,
order, rArray, vsArray, takingAmount, takerTraitsEncoded.TakerTraits, takerTraitsEncoded.Args,
)
if err != nil {
return nil, fmt.Errorf("pack fillOrderArgs error: %w", err)
return nil, nil, fmt.Errorf("pack fillOrderArgs error: %w", err)
}
encodeds = append(encodeds, packed)
}
}

return encodeds, nil
return encodeds, amountIn, nil
}

func UnpackLO1inch(decoded []byte) (any, error) {
if len(decoded) < MethodIDLength {
return nil, fmt.Errorf("invalid data length: %d", len(decoded))
}

methodID := decoded[:MethodIDLength]
method, err := OneInchAggregationRouterV6ABI.MethodById(methodID)
if err != nil {
return nil, fmt.Errorf("get method: %w", err)
}

switch method.Name {
case MethodFillOrderArgs:
return UnpackFillOrderArgs(decoded)

case MethodFillContractOrderArgs:
return UnpackFillContractOrderArgs(decoded)

default:
return nil, fmt.Errorf("method not support: %s", method.Name)
}
}

func UnpackFillOrderArgs(decoded []byte) (FillOrderArgs, error) {
if len(decoded) < MethodIDLength {
return FillOrderArgs{}, fmt.Errorf("invalid data length: %d", len(decoded))
}

method, err := OneInchAggregationRouterV6ABI.MethodById(decoded[:MethodIDLength])
if err != nil {
return FillOrderArgs{}, fmt.Errorf("get method: %w", err)
}

if method.Name != MethodFillOrderArgs {
return FillOrderArgs{}, fmt.Errorf("invalid method: %s", method.Name)
}

unpacked, err := method.Inputs.Unpack(decoded[MethodIDLength:])
if err != nil {
return FillOrderArgs{}, fmt.Errorf("unpack fillOrderArgs: %w", err)
}

var args FillOrderArgs
if err := method.Inputs.Copy(&args, unpacked); err != nil {
return FillOrderArgs{}, fmt.Errorf("copy FillOrderArgs: %w", err)
}

return args, nil
}

func UnpackFillContractOrderArgs(decoded []byte) (FillContractOrderArgs, error) {
if len(decoded) < MethodIDLength {
return FillContractOrderArgs{}, fmt.Errorf("invalid data length: %d", len(decoded))
}

method, err := OneInchAggregationRouterV6ABI.MethodById(decoded[:MethodIDLength])
if err != nil {
return FillContractOrderArgs{}, fmt.Errorf("get method: %w", err)
}

if method.Name != MethodFillContractOrderArgs {
return FillContractOrderArgs{}, fmt.Errorf("invalid method: %s", method.Name)
}

unpacked, err := method.Inputs.Unpack(decoded[MethodIDLength:])
if err != nil {
return FillContractOrderArgs{}, fmt.Errorf("unpack fillContractOrderArgs: %w", err)
}

var args FillContractOrderArgs
if err := method.Inputs.Copy(&args, unpacked); err != nil {
return FillContractOrderArgs{}, fmt.Errorf("copy FillOrderArgs: %w", err)
}

return args, nil
}
77 changes: 77 additions & 0 deletions pkg/oneinch/encode/lo1inch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package encode_test

import (
_ "embed"
"encoding/json"
"log"
"math/big"
"testing"

ksent "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity"
"github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool"
"github.com/KyberNetwork/kyberswap-dex-lib/pkg/valueobject"
"github.com/KyberNetwork/tradinglib/pkg/oneinch/encode"
"github.com/KyberNetwork/tradinglib/pkg/poolsimulators"
"github.com/KyberNetwork/tradinglib/pkg/testutil"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/stretchr/testify/require"
)

//go:embed lo1inch_test_pool_data.json
var testLO1inchPoolData string

func TestPackLO1inch(t *testing.T) {
var (
poolEnt ksent.Pool
chainID = 1
)
require.NoError(t, json.Unmarshal([]byte(testLO1inchPoolData), &poolEnt))

pSim, err := poolsimulators.PoolSimulatorFromPool(poolEnt, uint(chainID))
require.NoError(t, err)

out, err := pSim.CalcAmountOut(
pool.CalcAmountOutParams{
TokenAmountIn: pool.TokenAmount{
Token: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
Amount: testutil.NewBig10("271133267321"),
},
TokenOut: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
},
)
require.NoError(t, err)

log.Printf("%+v", out.SwapInfo)

encodingSwap := encode.EncodingSwap{
Pool: poolEnt.Address,
TokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
TokenOut: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
SwapAmount: testutil.NewBig10("271133267321"),
AmountOut: out.TokenAmountOut.Amount,
LimitReturnAmount: big.NewInt(0),
Exchange: valueobject.Exchange(poolEnt.Exchange),
PoolLength: len(poolEnt.Tokens),
PoolType: poolEnt.Type,
PoolExtra: poolEnt.Extra,
Extra: out.SwapInfo,
Recipient: "0x807cf9a772d5a3f9cefbc1192e939d62f0d9bd38 ",
}
data, remain, err := encode.PackLO1inch(valueobject.ChainID(chainID), encodingSwap)
require.NoError(t, err)
t.Log("remain", remain.String())

for _, b := range data {
t.Log(hexutil.Encode(b))
}
// Test result: https://www.tdly.co/shared/simulation/ce0ee2f6-3d5e-4015-894a-700793bd8e84
}

func TestUnpackLO1inch(t *testing.T) {
encoded := "0xf497df7544dd7f63a495d4c57c7b5445eea1735f63a2298f36ce4f48477d58e522e8a3ca000000000000000000000000e22259232b3cf5c74104cf2ded7f878f0201b1980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000028a61ed32d6ae800000000000000000000000000000000000000000000000000000000020bc1d72c0044000000000000000000000000000000000067c7e7530000000000000000000068963745db680beeeede005d48280a7a767b21196999dd17e4d29de8da474715246588864a0628dc0810d84c22a31035a4f79e62ab6163ec0c7eb84a954669c70000000000000000000000000000000000000000000000000000003f20cd5579080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000140807cf9a772d5a3f9cefbc1192e939d62f0d9bd3000000000000000000000000"

unpacked, err := encode.UnpackLO1inch(hexutil.MustDecode(encoded))
require.NoError(t, err)

t.Logf("%+v", unpacked)
}
30 changes: 30 additions & 0 deletions pkg/oneinch/encode/lo1inch_test_pool_data.json

Large diffs are not rendered by default.

41 changes: 41 additions & 0 deletions pkg/oneinch/encode/lo1inch_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,44 @@ type EncodingSwap struct {

Recipient string
}

/*
function fillOrderArgs(
Order calldata order,
bytes32 r,
bytes32 vs,
uint256 amount,
uint256 takerTraits,
bytes calldata args
)
external payable returns (uint256 makingAmount, uint256 takingAmount, bytes32 orderHash);
*/
type FillOrderArgs struct {
Order OneInchV6Order
R [32]byte
Vs [32]byte
Amount *big.Int
TakerTraits *big.Int
Args []byte
}

/*
function fillContractOrderArgs(
Order calldata order,
bytes calldata signature,
uint256 amount,
TakerTraits takerTraits,
bytes calldata args
) external returns(uint256 makingAmount, uint256 takingAmount, bytes32 orderHash);
*/
type FillContractOrderArgs struct {
Order OneInchV6Order
Signature []byte
Amount *big.Int
TakerTraits *big.Int
Args []byte
}
3 changes: 2 additions & 1 deletion pkg/poolsimulators/poolsimulators.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package poolsimulators

import (
"errors"
"fmt"
"math/big"

ksent "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity"
Expand All @@ -17,7 +18,7 @@ var ErrPoolTypeNotSupported = errors.New("pool type is not supported")
func PoolSimulatorFromPool(pool ksent.Pool, chainID uint) (pkgpool.IPoolSimulator, error) {
factoryFn := pkgpool.Factory(pool.Type)
if factoryFn == nil {
return nil, ErrPoolTypeNotSupported
return nil, fmt.Errorf("%w: %s", ErrPoolTypeNotSupported, pool.Type)
}

return factoryFn(pkgpool.FactoryParams{
Expand Down
8 changes: 8 additions & 0 deletions pkg/testutil/testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"maps"
"math/big"
"math/rand"

"github.com/KyberNetwork/tradinglib/pkg/dbutil"
Expand Down Expand Up @@ -92,3 +93,10 @@ func MustJsonify(data interface{}) string {
}
return string(d)
}

func NewBig10(s string) *big.Int {
const base = 10
b, _ := new(big.Int).SetString(s, base)

return b
}

0 comments on commit b89200e

Please sign in to comment.