Skip to content
Closed
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
57 changes: 56 additions & 1 deletion OpenApi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,6 @@ components:
ProviderDetail:
properties:
fee:
deprecated: true
type: integer
feePercentage:
type: number
Expand Down Expand Up @@ -684,6 +683,34 @@ components:
rsk:
type: string
type: object
SummariesResponse:
properties:
peginSummary:
$ref: '#/components/schemas/SummaryData'
type: object
pegoutSummary:
$ref: '#/components/schemas/SummaryData'
type: object
type: object
SummaryData:
properties:
acceptedQuotesCount:
type: integer
lpEarnings:
type: string
refundedQuotesCount:
type: integer
totalAcceptedQuotedAmount:
type: string
totalFeesCollected:
type: string
totalPenaltyAmount:
type: string
totalQuotedAmount:
type: string
totalQuotesCount:
type: integer
type: object
pkg.AcceptQuoteRequest:
properties:
quoteHash:
Expand Down Expand Up @@ -1034,6 +1061,34 @@ paths:
"204":
description: ""
summary: Withdraw PegIn Collateral
/report/summaries:
get:
description: ' Returns financial data for a given period'
parameters:
- description: Start date in YYYY-MM-DD format
in: query
name: startDate
required: true
schema:
description: Start date in YYYY-MM-DD format
format: string
type: string
- description: End date in YYYY-MM-DD format
in: query
name: endDate
required: true
schema:
description: End date in YYYY-MM-DD format
format: string
type: string
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/SummariesResponse'
description: Financial data for the given period
summary: Summaries
/userQuotes:
get:
description: ' Returns user quotes for address.'
Expand Down
11 changes: 6 additions & 5 deletions cmd/utils/register_pegin/register_pegin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ package main
import (
"encoding/hex"
"flag"
"math/big"
"os"
"testing"
"time"

"github.com/rsksmart/liquidity-provider-server/cmd/utils/scripts"
"github.com/rsksmart/liquidity-provider-server/internal/entities"
"github.com/rsksmart/liquidity-provider-server/internal/entities/blockchain"
Expand All @@ -13,10 +18,6 @@ import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"golang.org/x/term"
"math/big"
"os"
"testing"
"time"
)

// nolint:funlen
Expand Down Expand Up @@ -173,7 +174,7 @@ func TestRegisterPegInScriptInput_ToEnv(t *testing.T) {
})
}

func TestParseRegisterPegInScriptInput(t *testing.T) {
func TestParseRegisterPegInScriptInput(t *testing.T) { //nolint:funlen
parse := func() { /* mock function to prevent calling flag.Parse inside a test */ }
input := RegisterPegInScriptInput{
BaseInput: scripts.BaseInput{
Expand Down
226 changes: 225 additions & 1 deletion internal/adapters/dataproviders/database/mongo/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ package mongo

import (
"context"
log "github.com/sirupsen/logrus"
"fmt"
"time"

log "github.com/sirupsen/logrus"

"go.mongodb.org/mongo-driver/bson"
)

const (
Expand Down Expand Up @@ -74,3 +78,223 @@ func (c *Connection) CheckConnection(ctx context.Context) bool {
}
return err == nil
}

// QuoteResult holds the result of a quote listing operation
type QuoteResult[Q any, R QuoteHashProvider] struct {
Quotes []Q
RetainedQuotes []R
Error error
}

// QuoteQuery represents parameters for querying quotes
type QuoteQuery struct {
Ctx context.Context
Conn *Connection
StartDate time.Time
EndDate time.Time
QuoteCollection string
RetainedCollection string
}

// ListQuotesByDateRange retrieves quotes and retained quotes within a date range
func ListQuotesByDateRange[Q any, R QuoteHashProvider](
query QuoteQuery,
mapper func(bson.D) Q,
) QuoteResult[Q, R] {
dbCtx, cancel := context.WithTimeout(query.Ctx, query.Conn.timeout)
defer cancel()

// Step 1: Fetch quotes by date range
quotes, quoteHashes, err := fetchQuotesByDateRange(dbCtx, query.Conn, query.StartDate, query.EndDate, query.QuoteCollection, mapper)
if err != nil {
return QuoteResult[Q, R]{Error: err}
}

// Step 2: Fetch retained quotes
retainedQuotes, additionalHashes, err := fetchRetainedQuotes[R](dbCtx, query.Conn, query.StartDate, query.EndDate, query.RetainedCollection, quoteHashes)
if err != nil {
return QuoteResult[Q, R]{Error: err}
}

// Step 3: Fetch any additional quotes referenced by retained quotes
if len(additionalHashes) > 0 {
additionalQuotes, err := fetchAdditionalQuotes(dbCtx, query.Conn, query.QuoteCollection, additionalHashes, mapper)
if err != nil {
log.Errorf("Error processing additional quotes: %v", err)
} else {
quotes = append(quotes, additionalQuotes...)
}
}

logDbInteraction(Read, fmt.Sprintf("Found %d quotes and %d retained quotes in date range",
len(quotes), len(retainedQuotes)))

return QuoteResult[Q, R]{
Quotes: quotes,
RetainedQuotes: retainedQuotes,
Error: nil,
}
}

// fetchQuotesByDateRange retrieves quotes from the database within the specified date range
func fetchQuotesByDateRange[Q any](
ctx context.Context,
conn *Connection,
startDate, endDate time.Time,
collectionName string,
mapper func(bson.D) Q,
) ([]Q, []string, error) {
quoteFilter := bson.D{
{Key: "agreement_timestamp", Value: bson.D{
{Key: "$gte", Value: startDate.Unix()},
{Key: "$lte", Value: endDate.Unix()},
}},
}

var storedQuotes []bson.D
quoteCursor, err := conn.Collection(collectionName).Find(ctx, quoteFilter)
if err != nil {
return nil, nil, err
}

if err = quoteCursor.All(ctx, &storedQuotes); err != nil {
return nil, nil, err
}

quoteHashes := make([]string, 0, len(storedQuotes))
quotes := make([]Q, 0, len(storedQuotes))

for _, stored := range storedQuotes {
quoteObj := mapper(stored)
quotes = append(quotes, quoteObj)

// Extract hash from the BSON document
hashValue, ok := getStringValueFromBSON(stored, "hash")
if ok {
quoteHashes = append(quoteHashes, hashValue)
}
}

return quotes, quoteHashes, nil
}

// getStringValueFromBSON extracts a string value from a BSON document by key
func getStringValueFromBSON(doc bson.D, key string) (string, bool) {
// Convert bson.D to bson.Raw for direct lookup
data, err := bson.Marshal(doc)
if err != nil {
return "", false
}

// Use the Raw.Lookup method to find the value
rawValue := bson.Raw(data).Lookup(key)

// Extract string value if possible
return rawValue.StringValueOK()
}

// QuoteHashProvider defines an interface for objects that can provide a hash
type QuoteHashProvider interface {
GetQuoteHash() string
}

// fetchRetainedQuotes retrieves retained quotes and identifies any additional quote hashes to fetch
func fetchRetainedQuotes[R QuoteHashProvider](
ctx context.Context,
conn *Connection,
startDate, endDate time.Time,
collectionName string,
existingQuoteHashes []string,
) ([]R, []string, error) {
retainedFilter := createRetainedFilter(startDate, endDate, existingQuoteHashes)

var retainedQuotes []R
retainedCursor, err := conn.Collection(collectionName).Find(ctx, retainedFilter)
if err != nil {
return nil, nil, err
}

if err = retainedCursor.All(ctx, &retainedQuotes); err != nil {
return nil, nil, err
}

additionalHashes := findAdditionalQuoteHashes(retainedQuotes, existingQuoteHashes)
return retainedQuotes, additionalHashes, nil
}

// createRetainedFilter creates a filter for retained quotes
func createRetainedFilter(startDate, endDate time.Time, quoteHashes []string) bson.D {
return bson.D{
{Key: "$or", Value: bson.A{
bson.D{{Key: "quote_hash", Value: bson.D{
{Key: "$in", Value: quoteHashes},
}}},
bson.D{
{Key: "created_at", Value: bson.D{
{Key: "$gte", Value: startDate.Unix()},
{Key: "$lte", Value: endDate.Unix()},
}},
},
}},
}
}

// findAdditionalQuoteHashes identifies quote hashes in retained quotes that are not in existingQuoteHashes
func findAdditionalQuoteHashes[R QuoteHashProvider](retainedQuotes []R, existingQuoteHashes []string) []string {
// Create a map for faster lookup
existingMap := make(map[string]bool, len(existingQuoteHashes))
for _, hash := range existingQuoteHashes {
existingMap[hash] = true
}

// Store additional hashes
additionalMap := make(map[string]bool)

for i := range retainedQuotes {
hash := retainedQuotes[i].GetQuoteHash()
if !existingMap[hash] {
additionalMap[hash] = true
}
}

// Convert the map to a slice
additionalHashes := make([]string, 0, len(additionalMap))
for hash := range additionalMap {
additionalHashes = append(additionalHashes, hash)
}

return additionalHashes
}

// fetchAdditionalQuotes retrieves quotes by their hash
func fetchAdditionalQuotes[Q any](
ctx context.Context,
conn *Connection,
collectionName string,
hashes []string,
mapper func(bson.D) Q,
) ([]Q, error) {
quoteFilter := bson.D{
{Key: "hash", Value: bson.D{
{Key: "$in", Value: hashes},
}},
}

var storedQuotes []bson.D
quoteCursor, err := conn.Collection(collectionName).Find(ctx, quoteFilter)
if err != nil {
return nil, err
}

if err = quoteCursor.All(ctx, &storedQuotes); err != nil {
return nil, err
}

quotes := make([]Q, 0, len(storedQuotes))
for _, stored := range storedQuotes {
quoteObj := mapper(stored)
quotes = append(quotes, quoteObj)
}

return quotes, nil
}
Loading
Loading