mirror of
https://github.com/hanzo-fi/payments.git
synced 2026-10-30 20:31:20 +00:00
102 lines
2.9 KiB
Go
102 lines
2.9 KiB
Go
package fireblocks
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/big"
|
|
"slices"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hanzo-fi/go-libs/v5/pkg/types/currency"
|
|
"github.com/hanzo-fi/payments/pkg/domain/models"
|
|
errorsutils "github.com/hanzo-fi/payments/pkg/domain/errors"
|
|
)
|
|
|
|
// aggregatedBalance accumulates same-canonical-asset VaultAssets within a
|
|
// single vault. Multiple Fireblocks legacyIds (e.g. USDT_ERC20 + USDT_TRX)
|
|
// collapse onto one canonical asset (USDT/6); we sum their `Available` and
|
|
// keep the source legacyIds for the aggregation log line.
|
|
type aggregatedBalance struct {
|
|
info assetInfo
|
|
amount *big.Int
|
|
legacyIDs []string
|
|
}
|
|
|
|
func (p *Plugin) fetchNextBalances(ctx context.Context, req models.FetchNextBalancesRequest) (models.FetchNextBalancesResponse, error) {
|
|
var from models.PSPAccount
|
|
if req.FromPayload == nil {
|
|
return models.FetchNextBalancesResponse{}, errorsutils.NewWrappedError(
|
|
fmt.Errorf("from payload is required"),
|
|
models.ErrInvalidRequest,
|
|
)
|
|
}
|
|
if err := json.Unmarshal(req.FromPayload, &from); err != nil {
|
|
return models.FetchNextBalancesResponse{}, err
|
|
}
|
|
|
|
vaultAccount, err := p.client.GetVaultAccount(ctx, from.Reference)
|
|
if err != nil {
|
|
return models.FetchNextBalancesResponse{}, fmt.Errorf("failed to get vault account %s: %w", from.Reference, err)
|
|
}
|
|
|
|
now := time.Now()
|
|
agg := map[string]*aggregatedBalance{}
|
|
for _, a := range vaultAccount.Assets {
|
|
info, ok := p.lookupAsset(a.ID)
|
|
if !ok {
|
|
p.logger.Infof("skipping balance: unknown asset %q on account %s", a.ID, from.Reference)
|
|
continue
|
|
}
|
|
|
|
amount, err := currency.GetAmountWithPrecisionFromString(a.Available, info.Precision)
|
|
if err != nil {
|
|
p.logger.Infof("skipping balance: asset %q on account %s, unparseable available %q",
|
|
a.ID, from.Reference, a.Available)
|
|
continue
|
|
}
|
|
|
|
entry, exists := agg[info.Asset]
|
|
if !exists {
|
|
entry = &aggregatedBalance{info: info, amount: new(big.Int)}
|
|
agg[info.Asset] = entry
|
|
}
|
|
entry.amount.Add(entry.amount, amount)
|
|
entry.legacyIDs = appendUnique(entry.legacyIDs, info.LegacyID)
|
|
}
|
|
|
|
balances := make([]models.PSPBalance, 0, len(agg))
|
|
for _, entry := range agg {
|
|
if len(entry.legacyIDs) > 1 {
|
|
p.logger.Infof("aggregated %d fireblocks legacyIds [%s] into %s on account %s",
|
|
len(entry.legacyIDs), strings.Join(entry.legacyIDs, ","),
|
|
entry.info.Asset, from.Reference)
|
|
}
|
|
balance := models.PSPBalance{
|
|
AccountReference: from.Reference,
|
|
CreatedAt: now,
|
|
Amount: entry.amount,
|
|
Asset: entry.info.Asset,
|
|
}
|
|
if err := balance.Validate(); err != nil {
|
|
p.logger.Infof("dropping invalid balance for account %s asset %s: %s",
|
|
from.Reference, entry.info.Asset, err)
|
|
continue
|
|
}
|
|
balances = append(balances, balance)
|
|
}
|
|
|
|
return models.FetchNextBalancesResponse{
|
|
Balances: balances,
|
|
HasMore: false,
|
|
}, nil
|
|
}
|
|
|
|
func appendUnique(s []string, v string) []string {
|
|
if v == "" || slices.Contains(s, v) {
|
|
return s
|
|
}
|
|
return append(s, v)
|
|
}
|