mirror of
https://github.com/hanzo-fi/payments.git
synced 2026-10-30 20:31:20 +00:00
86 lines
2.9 KiB
Go
86 lines
2.9 KiB
Go
package coinbaseprime
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/types/currency"
|
|
)
|
|
|
|
// ensureAssetsFresh reloads the currencies/networkSymbols maps from
|
|
// GetPortfolio/GetAssets at most once per assetRefreshInterval. Follows the
|
|
// Fireblocks pattern (ee/plugins/fireblocks/assets_sync.go): a fast-path read
|
|
// lock check, then a refresh mutex with a second check to collapse concurrent
|
|
// callers into a single API call.
|
|
//
|
|
// Wallets are not refreshed here — they are populated at Install and then
|
|
// merged incrementally from fetchNextAccounts pagination.
|
|
func (p *Plugin) ensureAssetsFresh(ctx context.Context) error {
|
|
p.assetsMu.RLock()
|
|
needsRefresh := len(p.currencies) == 0 || time.Since(p.assetsLastSync) >= assetRefreshInterval
|
|
p.assetsMu.RUnlock()
|
|
if !needsRefresh {
|
|
return nil
|
|
}
|
|
|
|
p.assetsRefreshMu.Lock()
|
|
defer p.assetsRefreshMu.Unlock()
|
|
|
|
p.assetsMu.RLock()
|
|
needsRefresh = len(p.currencies) == 0 || time.Since(p.assetsLastSync) >= assetRefreshInterval
|
|
p.assetsMu.RUnlock()
|
|
if !needsRefresh {
|
|
return nil
|
|
}
|
|
|
|
return p.loadAssets(ctx)
|
|
}
|
|
|
|
// getAssets guarantees the asset cache is fresh, then returns the current
|
|
// snapshot of currencies and networkSymbols. loadAssets publishes these maps
|
|
// by pointer swap under p.assetsMu's write lock and never mutates a
|
|
// previously-published map, so callers may read the returned snapshots
|
|
// freely without holding the lock.
|
|
func (p *Plugin) getAssets(ctx context.Context) (map[string]int, map[string]string, error) {
|
|
if err := p.ensureAssetsFresh(ctx); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
p.assetsMu.RLock()
|
|
defer p.assetsMu.RUnlock()
|
|
return p.currencies, p.networkSymbols, nil
|
|
}
|
|
|
|
// resolveAssetAndPrecision obtains a fresh snapshot of the asset cache
|
|
// (refreshing via ensureAssetsFresh if the TTL has expired) and resolves the
|
|
// given Coinbase Prime symbol to a Formance asset string and its precision.
|
|
// Network-scoped symbols (e.g. "BASEUSDC") are folded to their base symbol
|
|
// ("USDC") via the networkSymbols map.
|
|
//
|
|
// Returns (asset, precision, true, nil) on a hit, ("", 0, false, nil) when
|
|
// the symbol is unknown in the current snapshot, and (_, _, false, err) if
|
|
// the freshness refresh itself failed (caller should propagate the error).
|
|
//
|
|
// Lives here (rather than per-capability files) because payments, orders,
|
|
// and conversions all share this resolution path.
|
|
func (p *Plugin) resolveAssetAndPrecision(ctx context.Context, symbol string) (string, int, bool, error) {
|
|
currencies, networkSymbols, err := p.getAssets(ctx)
|
|
if err != nil {
|
|
return "", 0, false, err
|
|
}
|
|
|
|
symbol = strings.ToUpper(strings.TrimSpace(symbol))
|
|
|
|
// Resolve network-scoped symbols (e.g. "BASEUSDC") to their base symbol ("USDC")
|
|
if base, ok := networkSymbols[symbol]; ok {
|
|
symbol = base
|
|
}
|
|
|
|
precision, err := currency.GetPrecision(currencies, symbol)
|
|
if err != nil {
|
|
return "", 0, false, nil
|
|
}
|
|
|
|
return currency.FormatAsset(currencies, symbol), precision, true, nil
|
|
}
|