Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 🩹 lo1inch/encode: fix packLO1inch #123

Merged
merged 7 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}