Documentation
¶
Overview ¶
Package transfer implements the non-custodial prepare/execute transfer API.
Index ¶
- Variables
- func RegisterRoutes(r chi.Router, svc Service, logger *zap.Logger)
- type CacheMetrics
- type ExecuteRequest
- type ExecuteResponse
- type IncomingTransfer
- type IncomingTransfersList
- type InstrumentedCache
- type PendingOfferLister
- type PrepareAcceptRequest
- type PrepareRequest
- type PrepareResponse
- type PreparedTransferCache
- type Service
- type TransferCache
- type TransferService
- func (s *TransferService) Execute(ctx context.Context, senderEVMAddr string, req *ExecuteRequest) (*ExecuteResponse, error)
- func (s *TransferService) ExecuteAccept(ctx context.Context, evmAddr string, req *ExecuteRequest) (*ExecuteResponse, error)
- func (s *TransferService) ListIncoming(ctx context.Context, evmAddr string, p indexer.Pagination) (*IncomingTransfersList, error)
- func (s *TransferService) Prepare(ctx context.Context, senderEVMAddr string, req *PrepareRequest) (*PrepareResponse, error)
- func (s *TransferService) PrepareAccept(ctx context.Context, evmAddr, contractID string, req *PrepareAcceptRequest) (*PrepareResponse, error)
- type UserStore
Constants ¶
This section is empty.
Variables ¶
Functions ¶
Types ¶
type CacheMetrics ¶
type CacheMetrics struct {
// PutsTotal counts Put calls by result: "ok" or "full".
PutsTotal *prometheus.CounterVec
// GetsTotal counts GetAndDelete calls by result: "ok", "not_found", or "expired".
GetsTotal *prometheus.CounterVec
}
CacheMetrics holds Prometheus collectors for the prepared-transfer cache.
func NewCacheMetrics ¶
func NewCacheMetrics(reg sharedmetrics.NamespacedRegisterer) *CacheMetrics
NewCacheMetrics registers transfer cache metrics against the given registerer.
func NewNopCacheMetrics ¶
func NewNopCacheMetrics() *CacheMetrics
NewNopCacheMetrics returns a CacheMetrics instance backed by a throwaway registry. Use in tests where metric values are not asserted.
type ExecuteRequest ¶
type ExecuteRequest struct {
TransferID string `json:"transfer_id"`
Signature string `json:"signature"` // hex-encoded DER signature
SignedBy string `json:"signed_by"` // Canton multihash fingerprint
}
ExecuteRequest is the HTTP request body for executing a prepared transfer.
type ExecuteResponse ¶
type ExecuteResponse struct {
Status string `json:"status"` // "completed"
}
ExecuteResponse is the HTTP response body for a completed transfer.
type IncomingTransfer ¶
type IncomingTransfer struct {
ContractID string `json:"contract_id"`
SenderPartyID string `json:"sender_party_id"`
ReceiverPartyID string `json:"receiver_party_id"`
Amount string `json:"amount"`
InstrumentAdmin string `json:"instrument_admin"`
InstrumentID string `json:"instrument_id"`
Symbol string `json:"symbol,omitempty"`
Decimals int `json:"decimals,omitempty"`
Name string `json:"name,omitempty"`
ContractAddress string `json:"contract_address,omitempty"`
}
IncomingTransfer represents a single pending inbound transfer offer. Fields downstream of the on-ledger TransferOffer are always populated; token-metadata fields (Symbol, Decimals, ContractAddress, Name) are populated when the instrument is in the api-server's supported_tokens config and omitted otherwise.
type IncomingTransfersList ¶
type IncomingTransfersList struct {
Items []IncomingTransfer `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
HasMore bool `json:"has_more"`
}
IncomingTransfersList is the HTTP response body for GET /api/v2/transfer/incoming. Pagination is page/limit-based to match the indexer's underlying envelope (`pkg/indexer.Page[T]`): callers ask for a specific page rather than carrying an opaque cursor, since the indexer is keyed by ledger_offset and a stable numeric offset is the natural cursor. HasMore is derived from page*limit < total so clients can stop iterating without needing arithmetic.
type InstrumentedCache ¶
type InstrumentedCache struct {
// contains filtered or unexported fields
}
InstrumentedCache wraps a TransferCache and records Prometheus metrics for every Put and GetAndDelete call.
func NewInstrumentedCache ¶
func NewInstrumentedCache(inner TransferCache, metrics *CacheMetrics) *InstrumentedCache
NewInstrumentedCache returns a metrics-instrumented wrapper around the given TransferCache.
func (*InstrumentedCache) GetAndDelete ¶
func (c *InstrumentedCache) GetAndDelete(transferID string) (*token.PreparedTransfer, error)
func (*InstrumentedCache) Put ¶
func (c *InstrumentedCache) Put(transfer *token.PreparedTransfer) error
type PendingOfferLister ¶
type PendingOfferLister interface {
GetPendingOffersForParty(
ctx context.Context, partyID string, p indexer.Pagination,
) (*indexer.Page[indexer.PendingOffer], error)
}
PendingOfferLister is the narrow slice of indexer/client.Client used by ListIncoming. The transfer service treats the indexer as the source of truth for pending TransferOffer state instead of querying Canton directly — the indexer already maintains `indexer_pending_offers` with all the fields we need (sender, amount, instrument), so going through it avoids duplicate decode logic in cantonsdk/token.
type PrepareAcceptRequest ¶
type PrepareAcceptRequest struct {
InstrumentAdmin string `json:"instrument_admin"` // Canton party ID of the instrument admin
}
PrepareAcceptRequest is the HTTP request body for preparing a non-custodial accept.
type PrepareRequest ¶
type PrepareRequest struct {
To string `json:"to"` // Recipient EVM address (0x...)
Amount string `json:"amount"` // Token amount (decimal string)
Token string `json:"token"` // "DEMO" or "PROMPT"
}
PrepareRequest is the HTTP request body for preparing a non-custodial transfer.
type PrepareResponse ¶
type PrepareResponse struct {
TransferID string `json:"transfer_id"`
TransactionHash string `json:"transaction_hash"` // hex-encoded hash to sign
PartyID string `json:"party_id"`
ExpiresAt string `json:"expires_at"` // RFC3339
}
PrepareResponse is the HTTP response body for a prepared transfer.
type PreparedTransferCache ¶
type PreparedTransferCache struct {
// contains filtered or unexported fields
}
PreparedTransferCache is an in-memory cache for prepared transfers awaiting external signatures.
func NewPreparedTransferCache ¶
func NewPreparedTransferCache(ttl time.Duration, maxSize int) *PreparedTransferCache
NewPreparedTransferCache creates a new cache with the given TTL and a maximum number of entries.
func (*PreparedTransferCache) GetAndDelete ¶
func (c *PreparedTransferCache) GetAndDelete(transferID string) (*token.PreparedTransfer, error)
GetAndDelete atomically retrieves and removes a prepared transfer. Returns ErrTransferNotFound if the ID doesn't exist, ErrTransferExpired if past TTL.
func (*PreparedTransferCache) Put ¶
func (c *PreparedTransferCache) Put(transfer *token.PreparedTransfer) error
Put stores a prepared transfer in the cache. It sets ExpiresAt from the cache TTL. Returns ErrCacheFull if the maximum number of entries has been reached.
func (*PreparedTransferCache) Start ¶
func (c *PreparedTransferCache) Start(ctx context.Context)
Start runs a background goroutine that periodically removes expired entries. It stops when the context is canceled.
type Service ¶
type Service interface {
Prepare(ctx context.Context, senderEVMAddr string, req *PrepareRequest) (*PrepareResponse, error)
Execute(ctx context.Context, senderEVMAddr string, req *ExecuteRequest) (*ExecuteResponse, error)
// ListIncoming returns one page of pending inbound TransferOffer details for the
// user with the given EVM address. This call is unauthenticated — anyone can
// query any address's pending offers; the response is intentionally minimized
// (party IDs truncated) to keep that from leaking counterparties.
ListIncoming(ctx context.Context, evmAddr string, p indexer.Pagination) (*IncomingTransfersList, error)
// PrepareAccept builds a Canton transaction for accepting an inbound offer.
PrepareAccept(
ctx context.Context, evmAddr, contractID string, req *PrepareAcceptRequest,
) (*PrepareResponse, error)
// ExecuteAccept completes a previously prepared accept using the client's DER signature.
ExecuteAccept(ctx context.Context, evmAddr string, req *ExecuteRequest) (*ExecuteResponse, error)
}
Service is the interface for the non-custodial prepare/execute transfer flow.
type TransferCache ¶
type TransferCache interface {
Put(transfer *token.PreparedTransfer) error
GetAndDelete(transferID string) (*token.PreparedTransfer, error)
}
TransferCache is the interface for caching prepared transfers.
type TransferService ¶
type TransferService struct {
// contains filtered or unexported fields
}
TransferService implements the non-custodial prepare/execute transfer flow.
func NewTransferService ¶
func NewTransferService( cantonToken token.Token, userStore UserStore, cache TransferCache, tokenCfg *pkgtoken.Config, offerLister PendingOfferLister, ) *TransferService
NewTransferService creates a new TransferService. tokenCfg supplies the list of allowed token symbols (used by Prepare) and the instrument→EVM-contract mapping (used to enrich ListIncoming responses). offerLister is required — the api-server now wires every service to the same indexer client at startup, so ListIncoming relies on it being non-nil.
func (*TransferService) Execute ¶
func (s *TransferService) Execute(ctx context.Context, senderEVMAddr string, req *ExecuteRequest) (*ExecuteResponse, error)
Execute completes a previously prepared transfer using the client's DER signature.
func (*TransferService) ExecuteAccept ¶
func (s *TransferService) ExecuteAccept(ctx context.Context, evmAddr string, req *ExecuteRequest) (*ExecuteResponse, error)
ExecuteAccept completes a previously prepared accept using the client's DER signature.
func (*TransferService) ListIncoming ¶
func (s *TransferService) ListIncoming(ctx context.Context, evmAddr string, p indexer.Pagination) (*IncomingTransfersList, error)
ListIncoming returns one page of pending inbound TransferOffer details for the user with the given EVM address. Unauthenticated: callers do not need to prove ownership of evmAddr. Data comes from the indexer's `indexer_pending_offers` table (already filtered to status=PENDING at the SQL level), so a single indexer call serves a single client page — no buffering, no re-aggregation.
func (*TransferService) Prepare ¶
func (s *TransferService) Prepare(ctx context.Context, senderEVMAddr string, req *PrepareRequest) (*PrepareResponse, error)
Prepare builds a Canton transaction and returns the hash for external signing.
func (*TransferService) PrepareAccept ¶
func (s *TransferService) PrepareAccept( ctx context.Context, evmAddr, contractID string, req *PrepareAcceptRequest, ) (*PrepareResponse, error)
PrepareAccept builds a Canton transaction for accepting an inbound offer.