Skip to content

Commit

Permalink
add parser for uniswapx v1 (#63)
Browse files Browse the repository at this point in the history
* add parser for uniswapx v1

* add abi file

* remove old log
  • Loading branch information
ngocthanh1389 authored Aug 12, 2024
1 parent 8453cd5 commit 2a5449b
Show file tree
Hide file tree
Showing 19 changed files with 1,316 additions and 45 deletions.
2 changes: 2 additions & 0 deletions cmd/tradelogs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/KyberNetwork/tradelogs/pkg/parser/bebop"
"github.com/KyberNetwork/tradelogs/pkg/parser/oneinchv6"
"github.com/KyberNetwork/tradelogs/pkg/parser/uniswapx"
uniswapxv1 "github.com/KyberNetwork/tradelogs/pkg/parser/uniswapx_v1"
"github.com/KyberNetwork/tradelogs/pkg/parser/zxrfqv3"
"github.com/KyberNetwork/tradelogs/pkg/pricefiller"
"github.com/KyberNetwork/tradelogs/pkg/rpcnode"
Expand Down Expand Up @@ -103,6 +104,7 @@ func run(c *cli.Context) error {
kyberswaprfq.MustNewParser(),
hashflowv3.MustNewParser(),
oneinchv6.MustNewParser(traceCalls),
uniswapxv1.MustNewParser(traceCalls),
uniswapx.MustNewParser(traceCalls),
bebop.MustNewParser(traceCalls),
zxrfqv3.MustNewParserWithDeployer(traceCalls, ethClient, common.HexToAddress(parser.Deployer0xV3)),
Expand Down
19 changes: 17 additions & 2 deletions internal/worker/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import (
"go.uber.org/zap"
)

const (
RetryInterval = 4 * time.Second
RemoveInterval = time.Hour
)

type Worker struct {
listener *evmlistenerclient.Client
l *zap.SugaredLogger
Expand All @@ -38,14 +43,19 @@ func New(l *zap.SugaredLogger, s *storage.Storage, listener *evmlistenerclient.C
}

func (w *Worker) Run(ctx context.Context) error {
retryTimer := time.NewTicker(evmlistenerclient.RetryTime)
retryTimer := time.NewTicker(RetryInterval)
defer retryTimer.Stop()
removeTimer := time.NewTicker(RemoveInterval)
defer removeTimer.Stop()
for {
select {
case <-retryTimer.C:
if err := w.retryParseLog(); err != nil {
w.l.Errorw("error when retry parse log", "err", err)
return err
}
case <-removeTimer.C:
if err := w.removeOldErrorLog(); err != nil {
w.l.Errorw("error when remove old error log", "err", err)
}
default:
}
Expand Down Expand Up @@ -184,3 +194,8 @@ func (w *Worker) findMatchingParser(log ethTypes.Log) parser.Parser {
}
return ps
}

func (w *Worker) removeOldErrorLog() error {
w.l.Info("start to remove old error log")
return w.s.RemoveLogUtil(time.Now().Add(-time.Hour * 24).Unix())
}
1 change: 0 additions & 1 deletion pkg/evmlistenerclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const (

var (
BlockTime = 12 * time.Second
RetryTime = 2 * time.Second
)

type Config struct {
Expand Down
9 changes: 2 additions & 7 deletions pkg/parser/bebop/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ const (
OrderParam = "order"
)

var (
ErrTradeTopic = errors.New("invalid trade topic")
ErrNotFoundLog = errors.New("not found log")
)

type SingleOrder struct {
Expiry *big.Int `json:"expiry"`
TakerAddress string `json:"taker_address"`
Expand Down Expand Up @@ -146,7 +141,7 @@ func (p *Parser) searchTradeLog(order storage.TradeLog, traceCall types.CallFram
}
}
traceData, _ := json.Marshal(traceCall)
return order, fmt.Errorf("%w %s", ErrNotFoundLog, string(traceData))
return order, fmt.Errorf("%w %s", parser.ErrNotFoundTrade, string(traceData))
}

func (p *Parser) checkBebopTrade(traceCall types.CallFrame, orderHash string) bool {
Expand Down Expand Up @@ -253,7 +248,7 @@ func (p *Parser) UseTraceCall() bool {

func (p *Parser) buildOrderByLog(log ethereumTypes.Log, blockTime uint64) (storage.TradeLog, error) {
if len(log.Topics) > 0 && log.Topics[0].Hex() != p.eventHash {
return storage.TradeLog{}, ErrTradeTopic
return storage.TradeLog{}, parser.ErrInvalidTopic
}
o, err := p.ps.ParseBebopOrder(log)
if err != nil {
Expand Down
4 changes: 1 addition & 3 deletions pkg/parser/hashflow_v3/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ const (
paramName = "quote"
)

var ErrTradeTopic = errors.New("invalid Trade topic")

type Parser struct {
abi *abi.ABI
ps *Hashflowv3Filterer
Expand Down Expand Up @@ -57,7 +55,7 @@ func (p *Parser) Topics() []string {

func (p *Parser) Parse(log ethereumTypes.Log, blockTime uint64) (storage.TradeLog, error) {
if len(log.Topics) > 0 && log.Topics[0].Hex() != p.eventHash {
return storage.TradeLog{}, ErrTradeTopic
return storage.TradeLog{}, parser.ErrInvalidTopic
}
o, err := p.ps.ParseTrade(log)
if err != nil {
Expand Down
5 changes: 1 addition & 4 deletions pkg/parser/kyberswap/parser.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package kyberswap

import (
"errors"
"strings"

tradingTypes "github.com/KyberNetwork/tradinglib/pkg/types"
Expand All @@ -18,8 +17,6 @@ const (
SwappedEvent = "Swapped"
)

var ErrInvalidKSSwappedTopic = errors.New("invalid KS Swapped topic")

type Parser struct {
abi *abi.ABI
ps *SwappedFilterer
Expand Down Expand Up @@ -54,7 +51,7 @@ func (p *Parser) Topics() []string {

func (p *Parser) Parse(log ethereumTypes.Log, blockTime uint64) (storage.TradeLog, error) {
if len(log.Topics) > 0 && log.Topics[0].Hex() != p.eventHash {
return storage.TradeLog{}, ErrInvalidKSSwappedTopic
return storage.TradeLog{}, parser.ErrInvalidTopic
}
e, err := p.ps.ParseSwapped(log)
if err != nil {
Expand Down
4 changes: 1 addition & 3 deletions pkg/parser/kyberswap_rfq/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ const (
paramName = "order"
)

var ErrInvalidKSFilledTopic = errors.New("invalid KS order filled topic")

type Parser struct {
abi *abi.ABI
ps *KyberswaprfqFilterer
Expand Down Expand Up @@ -59,7 +57,7 @@ func (p *Parser) Topics() []string {

func (p *Parser) Parse(log ethereumTypes.Log, blockTime uint64) (storage.TradeLog, error) {
if len(log.Topics) > 0 && log.Topics[0].Hex() != p.eventHash {
return storage.TradeLog{}, ErrInvalidKSFilledTopic
return storage.TradeLog{}, parser.ErrInvalidTopic
}
e, err := p.ps.ParseOrderFilledRFQ(log)
if err != nil {
Expand Down
8 changes: 3 additions & 5 deletions pkg/parser/oneinchv6/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ const (
)

var (
ErrInvalidOneInchFilledTopic = errors.New("invalid oneinch order filled topic")
ErrNotFoundLog = errors.New("not found log")
RFQOrderOutputArgument abi.Arguments
RFQOrderOutputArgument abi.Arguments
)

func init() {
Expand Down Expand Up @@ -93,7 +91,7 @@ func (p *Parser) Parse(log ethereumTypes.Log, blockTime uint64) (storage.TradeLo

func (p *Parser) buildOrderByLog(log ethereumTypes.Log) (storage.TradeLog, error) {
if len(log.Topics) > 0 && log.Topics[0].Hex() != p.eventHash {
return storage.TradeLog{}, ErrInvalidOneInchFilledTopic
return storage.TradeLog{}, parser.ErrInvalidTopic
}
e, err := p.ps.ParseOrderFilled(log)
if err != nil {
Expand Down Expand Up @@ -175,7 +173,7 @@ func (p *Parser) recursiveDetectOneInchRFQTrades(tradeLog storage.TradeLog, trac
}

traceData, _ := json.Marshal(traceCall)
return tradeLog, fmt.Errorf("%w %s", ErrNotFoundLog, string(traceData))
return tradeLog, fmt.Errorf("%w %s", parser.ErrNotFoundTrade, string(traceData))
}

func (p *Parser) isOneInchRFQTrades(txHash, orderHash string, traceCall types.CallFrame, count *int) bool {
Expand Down
4 changes: 1 addition & 3 deletions pkg/parser/paraswap/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ const (
paramName = "order"
)

var ErrInvalidRFQTopic = errors.New("invalid RfqFilled topic")

type Parser struct {
abi *abi.ABI
ps *OrderFilledFilterer
Expand Down Expand Up @@ -57,7 +55,7 @@ func (p *Parser) Topics() []string {

func (p *Parser) Parse(log ethereumTypes.Log, blockTime uint64) (storage.TradeLog, error) {
if len(log.Topics) > 0 && log.Topics[0].Hex() != p.eventHash {
return storage.TradeLog{}, ErrInvalidRFQTopic
return storage.TradeLog{}, parser.ErrInvalidTopic
}
o, err := p.ps.ParseOrderFilled(log)
if err != nil {
Expand Down
9 changes: 9 additions & 0 deletions pkg/parser/parser.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package parser

import (
"errors"

"github.com/KyberNetwork/tradelogs/pkg/storage"
tradingTypes "github.com/KyberNetwork/tradinglib/pkg/types"
ethereumTypes "github.com/ethereum/go-ethereum/core/types"
Expand All @@ -22,6 +24,7 @@ const (
ExUniswapX = "uniswapx"
ExNative = "native"
ExBebop = "bebop"
ExUniswapXV1 = "uniswapxV1"

Addr1InchV6 = "0x111111125421cA6dc452d289314280a0f8842A65"
AddrBebop = "0xbbbbbBB520d69a9775E85b458C58c648259FAD5F"
Expand All @@ -30,10 +33,16 @@ const (
AddrKyberswapRFQ = "0x7A819Fa46734a49D0112796f9377E024c350FB26"
AddrParaswap = "0xe92b586627ccA7a83dC919cc7127196d70f55a06"
AddrUniswapX = "0x00000011F84B9aa48e5f8aA8B9897600006289Be"
AddrUniswapXV1 = "0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4"
Addr0x = "0xDef1C0ded9bec7F1a1670819833240f027b25EfF"
Deployer0xV3 = "0x00000000000004533Fe15556B1E086BB1A72cEae"
)

var (
ErrInvalidTopic = errors.New("invalid order topic")
ErrNotFoundTrade = errors.New("not found log")
)

type Parser interface {
Parse(log ethereumTypes.Log, blockTime uint64) (storage.TradeLog, error)
Topics() []string
Expand Down
28 changes: 17 additions & 11 deletions pkg/parser/uniswapx/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import (
)

const (
FilledEvent = "Fill"
TransferEvent = "Transfer"
FilledEvent = "Fill"
)

type ResolvedOrder struct {
Expand Down Expand Up @@ -58,11 +57,6 @@ type ResolvedOrder struct {
Cosignature []uint8 "json:\"cosignature\""
}

var (
ErrInvalidOneInchFilledTopic = errors.New("invalid uniswapx order filled topic")
ErrNotFoundLog = errors.New("not found log")
)

type Parser struct {
abi *abi.ABI
ps *UniswapxFilterer
Expand Down Expand Up @@ -151,7 +145,7 @@ func (p *Parser) Parse(log ethereumTypes.Log, blockTime uint64) (storage.TradeLo

func (p *Parser) buildOrderByLog(log ethereumTypes.Log, blockTime uint64) (storage.TradeLog, error) {
if len(log.Topics) > 0 && log.Topics[0].Hex() != p.eventHash {
return storage.TradeLog{}, ErrInvalidOneInchFilledTopic
return storage.TradeLog{}, parser.ErrInvalidTopic
}
e, err := p.ps.ParseFill(log)
if err != nil {
Expand Down Expand Up @@ -232,7 +226,7 @@ func (p *Parser) recursiveDetectRFQTrades(order storage.TradeLog, call types.Cal
}
}
traceData, _ := json.Marshal(call)
return order, fmt.Errorf("%w %s", ErrNotFoundLog, string(traceData))
return order, fmt.Errorf("%w %s", parser.ErrNotFoundTrade, string(traceData))
}

func (p *Parser) updateOrder(internal storage.TradeLog, parsed []interface{}) (storage.TradeLog, error) {
Expand Down Expand Up @@ -280,7 +274,9 @@ func (p *Parser) updateOrder(internal storage.TradeLog, parsed []interface{}) (s
func decay(startAmount, endAmount, decayStartTime, decayEndTime, blockTime *big.Int) (decayedAmount *big.Int) {
decayedAmount = new(big.Int)

if decayEndTime.Cmp(blockTime) <= 0 {
if startAmount.Cmp(endAmount) == 0 {
return startAmount
} else if decayEndTime.Cmp(blockTime) <= 0 {
decayedAmount.Set(endAmount)
} else if decayStartTime.Cmp(blockTime) >= 0 {
decayedAmount.Set(startAmount)
Expand All @@ -293,7 +289,7 @@ func decay(startAmount, endAmount, decayStartTime, decayEndTime, blockTime *big.
decayedAmount = new(big.Int).Sub(startAmount, mulDiv(diff, elapsed, duration))
} else {
diff := new(big.Int).Sub(endAmount, startAmount)
decayedAmount = new(big.Int).Add(startAmount, mulDiv(diff, elapsed, duration))
decayedAmount = new(big.Int).Add(startAmount, mulDivUp(diff, elapsed, duration))
}
}
return
Expand All @@ -307,6 +303,16 @@ func mulDiv(a, b, c *big.Int) *big.Int {
return new(big.Int).Div(new(big.Int).Mul(a, b), c)
}

func mulDivUp(x, y, denominator *big.Int) *big.Int {
product := new(big.Int).Mul(x, y)
remainder := new(big.Int).Mod(product, denominator)
result := new(big.Int).Div(product, denominator)
if remainder.Cmp(big.NewInt(0)) > 0 {
result.Add(result, big.NewInt(1))
}
return result
}

// func checkExclusivity(exclusive, sender common.Address, exclusivityEndTime, ts uint64) bool {
// return exclusive == common.Address{} || ts > exclusivityEndTime || exclusive == sender
// }
Expand Down
4 changes: 1 addition & 3 deletions pkg/parser/uniswapx/uniswap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ func TestFetchEvent(t *testing.T) {
traceCalls := tracecall.NewCache(rpcnode.NewClient(ethClient))
p := MustNewParser(traceCalls)
require.Equal(t, p.abi.Events[FilledEvent].ID, common.HexToHash("0x78ad7ec0e9f89e74012afa58738b6b661c024cb0fd185ee2f616c0a28924bd66"))
client, err := ethclient.Dial("https://ethereum.kyberengineering.io")
require.NoError(t, err)
logs, err := client.FilterLogs(context.Background(), ethereum.FilterQuery{
logs, err := ethClient.FilterLogs(context.Background(), ethereum.FilterQuery{
BlockHash: nil,
FromBlock: big.NewInt(19719300),
ToBlock: big.NewInt(19719300),
Expand Down
1 change: 1 addition & 0 deletions pkg/parser/uniswapx_v1/abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs":[{"internalType":"contract IPermit2","name":"_permit2","type":"address"},{"internalType":"address","name":"_protocolFeeOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DeadlineBeforeEndTime","type":"error"},{"inputs":[],"name":"DeadlinePassed","type":"error"},{"inputs":[{"internalType":"address","name":"duplicateToken","type":"address"}],"name":"DuplicateFeeOutput","type":"error"},{"inputs":[],"name":"EndTimeBeforeStartTime","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"FeeTooLarge","type":"error"},{"inputs":[],"name":"IncorrectAmounts","type":"error"},{"inputs":[],"name":"InputAndOutputDecay","type":"error"},{"inputs":[],"name":"InsufficientEth","type":"error"},{"inputs":[{"internalType":"address","name":"feeToken","type":"address"}],"name":"InvalidFeeToken","type":"error"},{"inputs":[],"name":"InvalidReactor","type":"error"},{"inputs":[],"name":"NativeTransferFailed","type":"error"},{"inputs":[],"name":"NoExclusiveOverride","type":"error"},{"inputs":[],"name":"OrderEndTimeBeforeStartTime","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"filler","type":"address"},{"indexed":true,"internalType":"address","name":"swapper","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"Fill","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldFeeController","type":"address"},{"indexed":false,"internalType":"address","name":"newFeeController","type":"address"}],"name":"ProtocolFeeControllerSet","type":"event"},{"inputs":[{"components":[{"internalType":"bytes","name":"order","type":"bytes"},{"internalType":"bytes","name":"sig","type":"bytes"}],"internalType":"struct SignedOrder","name":"order","type":"tuple"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"order","type":"bytes"},{"internalType":"bytes","name":"sig","type":"bytes"}],"internalType":"struct SignedOrder[]","name":"orders","type":"tuple[]"}],"name":"executeBatch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"order","type":"bytes"},{"internalType":"bytes","name":"sig","type":"bytes"}],"internalType":"struct SignedOrder[]","name":"orders","type":"tuple[]"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"name":"executeBatchWithCallback","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"order","type":"bytes"},{"internalType":"bytes","name":"sig","type":"bytes"}],"internalType":"struct SignedOrder","name":"order","type":"tuple"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"name":"executeWithCallback","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"feeController","outputs":[{"internalType":"contract IProtocolFeeController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permit2","outputs":[{"internalType":"contract IPermit2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newFeeController","type":"address"}],"name":"setProtocolFeeController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
1 change: 1 addition & 0 deletions pkg/parser/uniswapx_v1/gen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
abigen --abi=abi.json --pkg=uniswapxv1 --out=uniswapx.go
Loading

0 comments on commit 2a5449b

Please sign in to comment.