mirror of
https://github.com/galacticship/terra.git
synced 2024-11-09 01:37:51 +00:00
91 lines
2.2 KiB
Go
91 lines
2.2 KiB
Go
|
package terra
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
"github.com/shopspring/decimal"
|
||
|
)
|
||
|
|
||
|
type PriceService struct {
|
||
|
routers []Router
|
||
|
cache map[string]cachedPrice
|
||
|
cacheMutex *sync.Mutex
|
||
|
cacheTimeout time.Duration
|
||
|
maxRouteLength int
|
||
|
}
|
||
|
|
||
|
type cachedPrice struct {
|
||
|
price decimal.Decimal
|
||
|
updatedTime time.Time
|
||
|
}
|
||
|
|
||
|
type PriceServiceOption func(s *PriceService) *PriceService
|
||
|
|
||
|
func WithCacheTimeout(timeout time.Duration) PriceServiceOption {
|
||
|
return func(s *PriceService) *PriceService {
|
||
|
s.cacheTimeout = timeout
|
||
|
return s
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func WithMaxRouteLength(maxRouteLenght int) PriceServiceOption {
|
||
|
return func(s *PriceService) *PriceService {
|
||
|
s.maxRouteLength = maxRouteLenght
|
||
|
return s
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NewPriceService(options ...PriceServiceOption) *PriceService {
|
||
|
p := &PriceService{
|
||
|
routers: nil,
|
||
|
cache: make(map[string]cachedPrice),
|
||
|
cacheMutex: &sync.Mutex{},
|
||
|
cacheTimeout: 30 * time.Second,
|
||
|
maxRouteLength: 2,
|
||
|
}
|
||
|
for _, option := range options {
|
||
|
p = option(p)
|
||
|
}
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
func (s *PriceService) AddRouter(router Router) {
|
||
|
s.routers = append(s.routers, router)
|
||
|
}
|
||
|
|
||
|
func (s *PriceService) GetPriceCached(ctx context.Context, token Token) (decimal.Decimal, error) {
|
||
|
s.cacheMutex.Lock()
|
||
|
defer s.cacheMutex.Unlock()
|
||
|
if p, ok := s.cache[token.Id()]; ok && p.updatedTime.Add(s.cacheTimeout).After(time.Now()) {
|
||
|
return p.price, nil
|
||
|
}
|
||
|
p, err := s.getCurrentPrice(ctx, token)
|
||
|
if err != nil {
|
||
|
return decimal.Zero, errors.Wrap(err, "getting current price")
|
||
|
}
|
||
|
s.cache[token.Id()] = cachedPrice{
|
||
|
price: p,
|
||
|
updatedTime: time.Now(),
|
||
|
}
|
||
|
return p, nil
|
||
|
}
|
||
|
|
||
|
func (s *PriceService) getCurrentPrice(ctx context.Context, token Token) (decimal.Decimal, error) {
|
||
|
var bestprice decimal.Decimal
|
||
|
var bestroute Route
|
||
|
for _, router := range s.routers {
|
||
|
newprice, newroute, err := router.SimulateSwap(ctx, token, UST, decimal.NewFromInt(1), s.maxRouteLength)
|
||
|
if err != nil && err != ErrNoRouteFund {
|
||
|
return decimal.Zero, errors.Wrapf(err, "simulating swap with router %s", router)
|
||
|
}
|
||
|
if newprice.GreaterThan(bestprice) || (newprice.Equals(bestprice) && len(newroute) < len(bestroute)) {
|
||
|
bestprice = newprice
|
||
|
bestroute = newroute
|
||
|
}
|
||
|
}
|
||
|
return bestprice, nil
|
||
|
}
|