Skip to content

Commit

Permalink
Merge pull request #33 from taikoxyz/ritsu
Browse files Browse the repository at this point in the history
feat: index ritsu LP token transfers
  • Loading branch information
bennettyong authored Aug 16, 2024
2 parents 89dee33 + c474a4b commit aa1fc0c
Show file tree
Hide file tree
Showing 13 changed files with 4,184 additions and 1 deletion.
15 changes: 15 additions & 0 deletions abigen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#/bin/sh

names=("erc20", "ritsu")


for (( i = 0; i < ${#names[@]}; ++i ));
do
lower=$(echo "${names[i]}" | tr '[:upper:]' '[:lower:]')
abigen --abi ${names[i]}.json \
--pkg $lower \
--type ${names[i]} \
--out adapters/contracts/$lower/${names[i]}.go
done

exit 0
14 changes: 14 additions & 0 deletions adapters/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package adapters

import (
"context"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
Expand All @@ -22,3 +23,16 @@ type Whitelist struct {
Time uint64
BlockNumber uint64
}

type LPTransfer struct {
From common.Address
To common.Address
Token0Amount *big.Int
Token0Decimals uint8
Token0 common.Address
Token1Amount *big.Int
Token1Decimals uint8
Token1 common.Address
Time uint64
BlockNumber uint64
}
759 changes: 759 additions & 0 deletions adapters/contracts/erc20/erc20.go

Large diffs are not rendered by default.

2,021 changes: 2,021 additions & 0 deletions adapters/contracts/ritsu/ritsu.go

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions adapters/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ type TransferLogsIndexer interface {
IndexLogs(ctx context.Context, chainID *big.Int, client *ethclient.Client, logs []types.Log) ([]Whitelist, error)
ProcessLog(ctx context.Context, chainID *big.Int, client *ethclient.Client, vLog types.Log) (*Whitelist, error)
}

type LPLogsIndexer interface {
Address() common.Address
IndexLogs(ctx context.Context, chainID *big.Int, client *ethclient.Client, logs []types.Log) ([]LPTransfer, error)
ProcessLog(ctx context.Context, chainID *big.Int, client *ethclient.Client, vLog types.Log) (*LPTransfer, error)
}
178 changes: 178 additions & 0 deletions adapters/projects/ritsu/transfer_event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package ritsu

import (
"context"
"math/big"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/taikoxyz/trailblazer-adapters/adapters"
"github.com/taikoxyz/trailblazer-adapters/adapters/contracts/erc20"
"github.com/taikoxyz/trailblazer-adapters/adapters/contracts/ritsu"
)

var (
logTransferSigHash = crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))
)

// TransferIndexer is an implementation of LogsIndexer for ERC20 transfer logs.
type TransferIndexer struct {
token common.Address
}

// NewTransferIndexer creates a new TransferIndexer.
func NewTransferIndexer(token common.Address) *TransferIndexer {
return &TransferIndexer{
token: token,
}
}

func (indexer *TransferIndexer) Address() common.Address {
return indexer.token
}

// IndexLogs processes logs for ERC20 transfers.
func (indexer *TransferIndexer) IndexLogs(ctx context.Context, chainID *big.Int, client *ethclient.Client, logs []types.Log) ([]adapters.LPTransfer, error) {
var result []adapters.LPTransfer
for _, vLog := range logs {
if !isERC20Transfer(vLog) {
continue
}
transferData, err := indexer.ProcessLog(ctx, chainID, client, vLog)
if err != nil {
return nil, err
}
result = append(result, *transferData)
}
return result, nil
}

func isERC20Transfer(vLog types.Log) bool {
return len(vLog.Topics) == 3 && vLog.Topics[0].Hex() == logTransferSigHash.Hex()
}

func (indexer *TransferIndexer) ProcessLog(ctx context.Context, chainID *big.Int, client *ethclient.Client, vLog types.Log) (*adapters.LPTransfer, error) {
// Extract "from" and "to" addresses from the log
to := common.BytesToAddress(vLog.Topics[2].Bytes()[12:])
from := common.BytesToAddress(vLog.Topics[1].Bytes()[12:])

// Unpack the transfer event
var transferEvent struct {
Value *big.Int
}
if err := unpackTransferEvent(vLog, &transferEvent); err != nil {
return nil, err
}

// Fetch the block details
block, err := client.BlockByNumber(ctx, big.NewInt(int64(vLog.BlockNumber)))
if err != nil {
return nil, err
}

// Initialize the LP token caller
token, err := ritsu.NewRitsuCaller(indexer.token, client)
if err != nil {
return nil, err
}

// Fetch reserve balances and token addresses
reserves, token0Address, token1Address, err := fetchReservesAndTokens(token, block.Number())
if err != nil {
return nil, err
}

// Calculate user's share of the pool
shareOfPool, err := calculateShareOfPool(transferEvent.Value, token, block.Number())
if err != nil {
return nil, err
}

// Fetch token details (decimals)
_, token0Decimals, err := fetchTokenDetails(token0Address, client)
if err != nil {
return nil, err
}

_, token1Decimals, err := fetchTokenDetails(token1Address, client)
if err != nil {
return nil, err
}

// Calculate the user's share of each token in the pool
token0Share := calculateTokenShare(shareOfPool, reserves.Reserve0)
token1Share := calculateTokenShare(shareOfPool, reserves.Reserve1)

// Return the LPTransfer struct with calculated values
return &adapters.LPTransfer{
From: from,
To: to,
Token0Amount: token0Share,
Token0Decimals: token0Decimals,
Token0: token0Address,
Token1Amount: token1Share,
Token1Decimals: token1Decimals,
Token1: token1Address,
Time: block.Time(),
BlockNumber: block.Number().Uint64(),
}, nil
}

// Helper function to unpack the transfer event from the log
func unpackTransferEvent(vLog types.Log, transferEvent *struct{ Value *big.Int }) error {
ritsuABI, err := abi.JSON(strings.NewReader(erc20.Erc20ABI))
if err != nil {
return err
}
return ritsuABI.UnpackIntoInterface(transferEvent, "Transfer", vLog.Data)
}

// Helper function to fetch reserves and token addresses from the LP contract
func fetchReservesAndTokens(token *ritsu.RitsuCaller, blockNumber *big.Int) (reserves struct {
Reserve0 *big.Int
Reserve1 *big.Int
}, token0Address, token1Address common.Address, err error) {
reserves, err = token.GetReserves(nil)
if err != nil {
return
}

token0Address, err = token.Token0(&bind.CallOpts{BlockNumber: blockNumber})
if err != nil {
return
}

token1Address, err = token.Token1(&bind.CallOpts{BlockNumber: blockNumber})
return
}

// Helper function to calculate the user's share of the pool
func calculateShareOfPool(transferValue *big.Int, token *ritsu.RitsuCaller, blockNumber *big.Int) (*big.Rat, error) {
totalSupply, err := token.TotalSupply(&bind.CallOpts{BlockNumber: blockNumber})
if err != nil {
return nil, err
}
return new(big.Rat).SetFrac(transferValue, totalSupply), nil
}

// Helper function to fetch token details (caller and decimals)
func fetchTokenDetails(tokenAddress common.Address, client *ethclient.Client) (token *erc20.Erc20Caller, decimals uint8, err error) {
token, err = erc20.NewErc20Caller(tokenAddress, client)
if err != nil {
return
}

decimals, err = token.Decimals(nil)
return
}

// Helper function to calculate the share of a specific token in the pool
func calculateTokenShare(shareOfPool *big.Rat, reserve *big.Int) *big.Int {
tokenShare := new(big.Int).Mul(shareOfPool.Num(), reserve)
return tokenShare.Div(tokenShare, shareOfPool.Denom())
}
27 changes: 27 additions & 0 deletions cmd/process_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"math/big"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/taikoxyz/trailblazer-adapters/adapters"
)
Expand Down Expand Up @@ -36,3 +37,29 @@ func processLogIndexer(client *ethclient.Client, processor adapters.TransferLogs
fmt.Printf("Senders: %v\n", senders)
return nil
}

func processLPLogIndexer(client *ethclient.Client, processor adapters.LPLogsIndexer, blockNumber int64) error {
chainID, err := client.ChainID(context.Background())
if err != nil {
log.Fatalf("Failed to fetch the chain ID: %v", err)
return err
}
query := ethereum.FilterQuery{
Addresses: []common.Address{processor.Address()},
FromBlock: big.NewInt(blockNumber),
ToBlock: big.NewInt(blockNumber),
}
logs, err := client.FilterLogs(context.Background(), query)
if err != nil {
log.Fatalf("Failed to fetch the logs: %v", err)
return err
}
senders, err := processor.IndexLogs(context.Background(), chainID, client, logs)
if err != nil {
log.Fatalf("Failed to process the logs: %v", err)
return err
}

fmt.Printf("Senders: %v\n", senders)
return nil
}
12 changes: 12 additions & 0 deletions cmd/ritsu_lp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cmd

import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/taikoxyz/trailblazer-adapters/adapters/projects/ritsu"
)

func processRitsuLPIndexer(client *ethclient.Client, blockNumber int64) error {
processor := ritsu.NewTransferIndexer(common.HexToAddress("0x7c38E9389B27668280E5aaAc372eBCb2ECc1c5E0"))
return processLPLogIndexer(client, processor, blockNumber)
}
4 changes: 3 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func init() {

func promptUser() error {
var adapterOptions = []string{
"NewTransactionSender", "NftDeployed", "GamingWhitelist", "DotTaikoIndexer",
"RitsuLP", "NewTransactionSender", "NftDeployed", "GamingWhitelist", "DotTaikoIndexer",
"OrderFulfilledIndexer", "NewSaleIndexer", "ContractDeployed",
"CollectionCreated", "TokenSold",
}
Expand Down Expand Up @@ -78,6 +78,8 @@ func executeCommand() error {
}

switch adapter {
case "RitsuLP":
return processRitsuLPIndexer(client, blockNumber)
case "NewTransactionSender":
return processNewTransactionSender(client, blockNumber)
case "NftDeployed":
Expand Down
Loading

0 comments on commit aa1fc0c

Please sign in to comment.