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

feat(worker/polymarket): add polymarket worker #540

Merged
merged 26 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dc16776
fix: re-add paraswap content
FrankLi123 Sep 6, 2024
ab4991b
Merge branch 'worker-dev' of https://github.com/RSS3-Network/Node int…
FrankLi123 Sep 7, 2024
ff7017c
Merge branch 'worker-dev' of https://github.com/RSS3-Network/Node int…
FrankLi123 Sep 7, 2024
f53d90d
Merge branch 'worker-dev' of https://github.com/RSS3-Network/Node int…
FrankLi123 Sep 7, 2024
ad06163
Merge branch 'worker-dev' of https://github.com/RSS3-Network/Node int…
FrankLi123 Sep 7, 2024
2fe275b
Merge branch 'worker-dev' of https://github.com/RSS3-Network/Node int…
FrankLi123 Sep 11, 2024
71c2817
feat(worker/polymarket): add new polymarket worker
FrankLi123 Sep 12, 2024
7386c4f
feat: generated contract go files
FrankLi123 Sep 12, 2024
91e3d62
fix: modify action names with updated protocol-go action enums
FrankLi123 Sep 12, 2024
758be3f
fix: remove duplicated function declaration
FrankLi123 Sep 12, 2024
72302f1
fix: add go.sum
FrankLi123 Sep 12, 2024
6de41ce
fix: remove conflicted contract generation file and its use from work…
FrankLi123 Sep 12, 2024
ff18d68
fix: add generated contract go files
FrankLi123 Sep 12, 2024
7b24ccf
fix: add generated contract go files #2
FrankLi123 Sep 12, 2024
8eae8cc
fix: fix typo
FrankLi123 Sep 12, 2024
c9bea4b
fix: fix one logical error in generating metadata of new actions
FrankLi123 Sep 12, 2024
7c5da21
feat: add a test case for batch offer match finalizations
FrankLi123 Sep 12, 2024
bd811b0
fix: fix a typo in one Offer Match test case
FrankLi123 Sep 12, 2024
ac9a47c
fix: addressed issues from PR review, such as modifying function stru…
FrankLi123 Sep 12, 2024
dd97a52
fix: add network config params for polymarket
FrankLi123 Sep 12, 2024
8c9febc
fix: add go.sum
FrankLi123 Sep 12, 2024
b9a115e
fix: fix redeclaration issue in generated file
FrankLi123 Sep 12, 2024
537fad9
fix: fix typo
FrankLi123 Sep 12, 2024
0085061
feat: add a new test case for Neg Risk CTF Exchanges
FrankLi123 Sep 13, 2024
17c6acd
fix:merge branch 'worker-dev' of https://github.com/RSS3-Network/Node…
FrankLi123 Sep 13, 2024
889ba3e
fix: merge with updated worker-dev branch
FrankLi123 Sep 13, 2024
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
220 changes: 220 additions & 0 deletions internal/engine/worker/decentralized/contract/polymarket/worker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package polymarket

import (
"context"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/rss3-network/node/config"
"github.com/rss3-network/node/internal/engine"
source "github.com/rss3-network/node/internal/engine/source/ethereum"
"github.com/rss3-network/node/provider/ethereum"
"github.com/rss3-network/node/provider/ethereum/contract"
"github.com/rss3-network/node/provider/ethereum/contract/polymarket"
"github.com/rss3-network/node/provider/ethereum/token"
"github.com/rss3-network/node/schema/worker/decentralized"
"github.com/rss3-network/protocol-go/schema"
activityx "github.com/rss3-network/protocol-go/schema/activity"
"github.com/rss3-network/protocol-go/schema/metadata"
"github.com/rss3-network/protocol-go/schema/network"
"github.com/rss3-network/protocol-go/schema/tag"
"github.com/rss3-network/protocol-go/schema/typex"
"github.com/samber/lo"
"github.com/shopspring/decimal"
"go.uber.org/zap"
)

var _ engine.Worker = (*worker)(nil)

type worker struct {
ethereumClient ethereum.Client
tokenClient token.Client
ctfExchange *polymarket.CTFExchangeFilterer
}

func (w *worker) Name() string {
return decentralized.Polymarket.String()
}

func (w *worker) Platform() string {
return decentralized.PlatformPolymarket.String()
}

func (w *worker) Network() []network.Network {
return []network.Network{
network.Polygon,
}
}

func (w *worker) Tags() []tag.Tag {
return []tag.Tag{
tag.Collectible,
}
}

func (w *worker) Types() []schema.Type {
return []schema.Type{
typex.CollectibleTrade,
}
}

func (w *worker) Filter() engine.DataSourceFilter {
return &source.Filter{
LogAddresses: []common.Address{
polymarket.AddressPolyMarketCTFExchange,
polymarket.AddressPolyMarketNegRiskCTFExchange,
pseudoyu marked this conversation as resolved.
Show resolved Hide resolved
},
LogTopics: []common.Hash{
polymarket.EventOrderFinalized,
},
}
}

func (w *worker) Transform(ctx context.Context, task engine.Task) (*activityx.Activity, error) {
polygonTask, ok := task.(*source.Task)
if !ok {
return nil, fmt.Errorf("invalid task type %T", task)
}

activity, err := task.BuildActivity(activityx.WithActivityPlatform(w.Platform()))
if err != nil {
return nil, fmt.Errorf("build activity: %w", err)
}

for _, log := range polygonTask.Receipt.Logs {
if len(log.Topics) == 0 {
continue
}

var (
actions []*activityx.Action
err error
)

switch {
case w.matchOrderFinalizedLog(polygonTask, log):
actions, err = w.transformOrderFinalizedLog(ctx, polygonTask, log)

default:
zap.L().Warn("unsupported log", zap.String("task", polygonTask.ID()), zap.Uint("topic.index", log.Index))
continue
}

if err != nil {
zap.L().Warn("handle polymarket order transaction", zap.Error(err), zap.String("worker", w.Name()), zap.String("task", polygonTask.ID()))

return nil, err
}

activity.Actions = append(activity.Actions, actions...)
}

activity.Type = typex.CollectibleTrade

return activity, nil
}

func (w *worker) matchOrderFinalizedLog(_ *source.Task, log *ethereum.Log) bool {
return contract.MatchEventHashes(log.Topics[0], polymarket.EventOrderFinalized) &&
contract.MatchAddresses(log.Address, polymarket.AddressPolyMarketCTFExchange, polymarket.AddressPolyMarketNegRiskCTFExchange)
pseudoyu marked this conversation as resolved.
Show resolved Hide resolved
}

func (w *worker) transformOrderFinalizedLog(ctx context.Context, task *source.Task, log *ethereum.Log) ([]*activityx.Action, error) {
var err error

// CTF and NegRiskCTF shares the same OrderFilled struct
event, err := w.ctfExchange.ParseOrderFilled(log.Export())

if err != nil {
return nil, fmt.Errorf("parse OrderFilled event: %w", err)
}

buyAction, sellAction, err := w.buildMarketTradeAction(ctx, task, task.ChainID, event.Maker, event.Taker, event.MakerAssetId, event.TakerAssetId, event.OrderHash, event.MakerAmountFilled, event.TakerAmountFilled)
if err != nil {
return nil, fmt.Errorf("build market trade action: %w", err)
}

return []*activityx.Action{buyAction, sellAction}, nil
}

func (w *worker) buildMarketTradeAction(ctx context.Context, _ *source.Task, chainID uint64, maker, taker common.Address, makerAssetID, takerAssetID *big.Int, _ [32]byte, makerAmountFilled, takerAmountFilled *big.Int) (*activityx.Action, *activityx.Action, error) {
makerAmountFilledDecimal := decimal.NewFromBigInt(makerAmountFilled, 0)
takerAmountFilledDecimal := decimal.NewFromBigInt(takerAmountFilled, 0)

var takerTokenAddress *common.Address
if takerAssetID.Cmp(big.NewInt(0)) == 0 {
takerTokenAddress = nil
} else {
address := common.HexToAddress(polymarket.AddressPolyMarketConditionTokens.String())
takerTokenAddress = &address
}

takerToken, err := w.tokenClient.Lookup(ctx, chainID, takerTokenAddress, takerAssetID, nil)

if err != nil {
return nil, nil, fmt.Errorf("lookup taker token: %w", err)
}

takerToken.Value = &takerAmountFilledDecimal

var makerTokenAddress *common.Address

if makerAssetID.Cmp(big.NewInt(0)) == 0 {
makerTokenAddress = nil
} else {
address := common.HexToAddress(polymarket.AddressPolyMarketConditionTokens.String())
makerTokenAddress = &address
}

makerToken, err := w.tokenClient.Lookup(ctx, chainID, makerTokenAddress, makerAssetID, nil)
if err != nil {
return nil, nil, fmt.Errorf("lookup maker token: %w", err)
}

makerToken.Value = &makerAmountFilledDecimal

buyAction := &activityx.Action{
Type: typex.CollectibleTrade,
Platform: w.Platform(),
From: taker.String(),
To: maker.String(),
Metadata: metadata.CollectibleTrade{
Action: metadata.ActionCollectibleTradeBuy,
Token: *takerToken,
Cost: makerToken,
},
}

// Sell action (from the maker's perspective)
sellAction := &activityx.Action{
Type: typex.CollectibleTrade,
Platform: w.Platform(),
From: maker.String(),
To: taker.String(),
Metadata: metadata.CollectibleTrade{
Action: metadata.ActionCollectibleTradeSell,
Token: *takerToken,
Cost: makerToken,
},
}

return buyAction, sellAction, nil
}

func NewWorker(config *config.Module) (engine.Worker, error) {
instance := worker{
ctfExchange: lo.Must(polymarket.NewCTFExchangeFilterer(ethereum.AddressGenesis, nil)),
// negRiskCTF: lo.Must(polymarket.NewNegRiskCTFExchangeFilterer(ethereum.AddressGenesis, nil)),
}

var err error

if instance.ethereumClient, err = ethereum.Dial(context.Background(), config.Endpoint.URL, config.Endpoint.BuildEthereumOptions()...); err != nil {
return nil, fmt.Errorf("initialize ethereum client: %w", err)
}

instance.tokenClient = token.NewClient(instance.ethereumClient)

return &instance, nil
}
Loading
Loading