package graph import ( "context" "fmt" ethCore "github.com/ethereum/go-ethereum/core" "github.com/synapsecns/sanguine/services/scribe/backend" "math/big" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ipfs/go-log" "github.com/jpillora/backoff" "github.com/synapsecns/sanguine/services/scribe/db" "github.com/synapsecns/sanguine/services/scribe/graphql/server/graph/model" ) var logger = log.Logger("scribe-graph") func (r Resolver) receiptsToModelReceipts(receipts []types.Receipt, chainID uint32) []*model.Receipt { modelReceipts := make([]*model.Receipt, len(receipts)) for i, receipt := range receipts { modelReceipts[i] = r.receiptToModelReceipt(receipt, chainID) } return modelReceipts } func (r Resolver) receiptToModelReceipt(receipt types.Receipt, chainID uint32) *model.Receipt { return &model.Receipt{ ChainID: int(chainID), Type: int(receipt.Type), PostState: string(receipt.PostState), Status: int(receipt.Status), CumulativeGasUsed: int(receipt.CumulativeGasUsed), Bloom: common.Bytes2Hex(receipt.Bloom.Bytes()), TxHash: receipt.TxHash.String(), ContractAddress: receipt.ContractAddress.String(), GasUsed: int(receipt.GasUsed), BlockNumber: int(receipt.BlockNumber.Int64()), TransactionIndex: int(receipt.TransactionIndex), } } func (r Resolver) logsToModelLogs(logs []*types.Log, chainID uint32) []*model.Log { modelLogs := make([]*model.Log, len(logs)) for i, log := range logs { modelLogs[i] = r.logToModelLog(log, chainID) } return modelLogs } func (r Resolver) logToModelLog(log *types.Log, chainID uint32) *model.Log { topicsList := make([]string, len(log.Topics)) for i, topic := range log.Topics { topicsList[i] = topic.String() } return &model.Log{ ContractAddress: log.Address.String(), ChainID: int(chainID), Topics: topicsList, Data: common.Bytes2Hex(log.Data), BlockNumber: int(log.BlockNumber), TxHash: log.TxHash.String(), TxIndex: int(log.TxIndex), BlockHash: log.BlockHash.String(), Index: int(log.Index), Removed: log.Removed, } } func (r Resolver) ethTxsToModelTransactions(ctx context.Context, ethTxs []db.TxWithBlockNumber, chainID uint32) []*model.Transaction { modelTxs := make([]*model.Transaction, len(ethTxs)) for i := range ethTxs { ethTx := ethTxs[i] modelTxs[i] = r.ethTxToModelTransaction(ethTx.Tx, chainID) // Return empty sender if that this operation errors (will only occur in tests or invalid txs). msgFrom, _ := ethCore.TransactionToMessage(ðTx.Tx, types.LatestSignerForChainID(ethTx.Tx.ChainId()), big.NewInt(1)) modelTxs[i].Sender = msgFrom.From.String() timestamp, err := r.DB.RetrieveBlockTime(ctx, chainID, ethTx.BlockNumber) if err != nil || timestamp == 0 { newBlockTime, err := r.getBlockTime(ctx, chainID, ethTx.BlockNumber) if err != nil { continue } timestamp = *newBlockTime err = r.DB.StoreBlockTime(ctx, chainID, ethTx.BlockNumber, *newBlockTime) if err != nil { continue } } modelTxs[i].Timestamp = int(timestamp) } return modelTxs } func (r Resolver) ethTxToModelTransaction(ethTx types.Transaction, chainID uint32) *model.Transaction { protected := ethTx.Protected() return &model.Transaction{ ChainID: int(chainID), TxHash: ethTx.Hash().String(), Protected: protected, Type: int(ethTx.Type()), Data: common.Bytes2Hex(ethTx.Data()), Gas: int(ethTx.Gas()), GasPrice: int(ethTx.GasPrice().Uint64()), GasTipCap: ethTx.GasFeeCap().String(), GasFeeCap: ethTx.GasTipCap().String(), Value: ethTx.Value().String(), Nonce: int(ethTx.Nonce()), To: ethTx.To().String(), } } // getBlockTime retrieves a singular blocktime. // //nolint:gocognit,cyclop func (r Resolver) getBlockTime(ctx context.Context, chainID uint32, blockNumber uint64) (*uint64, error) { b := &backoff.Backoff{ Factor: 2, Jitter: true, Min: 30 * time.Millisecond, Max: 5 * time.Second, } timeout := time.Duration(0) var backendClient backend.ScribeBackend backendClient, err := backend.DialBackend(ctx, fmt.Sprintf("%s/%d", r.OmniRPCURL, chainID), r.Metrics) if err != nil { return nil, fmt.Errorf("could not create backend client: %w", err) } for { select { case <-ctx.Done(): return nil, fmt.Errorf("context canceled: %w", ctx.Err()) case <-time.After(timeout): block, err := backendClient.HeaderByNumber(ctx, big.NewInt(int64(blockNumber))) if err != nil { timeout = b.Duration() fmt.Println("TESTING--", fmt.Sprintf("%s/%d", r.OmniRPCURL, chainID), err) continue } blockTime := block.Time return &blockTime, nil } } }