Files

96 lines
1.9 KiB
Go

package fee
import (
"context"
"fmt"
"math/big"
"github.com/jackc/pgx/v5/pgxpool"
)
// Oracle provides gas price estimates
type Oracle struct {
db *pgxpool.Pool
chainID int
}
// NewOracle creates a new fee oracle
func NewOracle(db *pgxpool.Pool, chainID int) *Oracle {
return &Oracle{
db: db,
chainID: chainID,
}
}
// FeeEstimate represents gas price estimates
type FeeEstimate struct {
Slow string `json:"slow"`
Standard string `json:"standard"`
Fast string `json:"fast"`
Urgent string `json:"urgent"`
}
// GetFeeEstimates gets current fee estimates
func (o *Oracle) GetFeeEstimates(ctx context.Context) (*FeeEstimate, error) {
// Get recent gas prices from last 100 blocks
query := `
SELECT avg_gas_price
FROM gas_price_history
WHERE chain_id = $1
ORDER BY time DESC
LIMIT 100
`
rows, err := o.db.Query(ctx, query, o.chainID)
if err != nil {
return nil, fmt.Errorf("failed to query gas prices: %w", err)
}
defer rows.Close()
var prices []int64
for rows.Next() {
var price int64
if err := rows.Scan(&price); err == nil {
prices = append(prices, price)
}
}
if len(prices) == 0 {
// Return default estimates if no data
return &FeeEstimate{
Slow: "20000000000",
Standard: "30000000000",
Fast: "50000000000",
Urgent: "100000000000",
}, nil
}
// Calculate percentiles
p25 := percentile(prices, 0.25)
p50 := percentile(prices, 0.50)
p75 := percentile(prices, 0.75)
p95 := percentile(prices, 0.95)
return &FeeEstimate{
Slow: big.NewInt(p25).String(),
Standard: big.NewInt(p50).String(),
Fast: big.NewInt(p75).String(),
Urgent: big.NewInt(p95).String(),
}, nil
}
// percentile calculates percentile of sorted slice
func percentile(data []int64, p float64) int64 {
if len(data) == 0 {
return 0
}
index := int(float64(len(data)) * p)
if index >= len(data) {
index = len(data) - 1
}
return data[index]
}