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 CompletedTransfer
- type CompletedTransfersList
- type CustodialTransferRequest
- type ExecuteRequest
- type ExecuteResponse
- type IncomingTransfer
- type IncomingTransfersList
- type IndexerReader
- type InstrumentedCache
- type OutgoingTransfer
- type OutgoingTransfersList
- type PartyRegistry
- 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) ListCompleted(ctx context.Context, evmAddr string, p indexer.Pagination) (*CompletedTransfersList, error)
- func (s *TransferService) ListIncoming(ctx context.Context, evmAddr string, p indexer.Pagination) (*IncomingTransfersList, error)
- func (s *TransferService) ListOutgoing(ctx context.Context, evmAddr string, status string, p indexer.Pagination) (*OutgoingTransfersList, 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)
- func (s *TransferService) PrepareWithdraw(ctx context.Context, evmAddr, contractID string) (*PrepareResponse, error)
- func (s *TransferService) SendCustodial(ctx context.Context, senderEVMAddr string, req *CustodialTransferRequest) (*ExecuteResponse, error)
- func (s *TransferService) WithdrawCustodial(ctx context.Context, evmAddr, contractID string) (*ExecuteResponse, 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 CompletedTransfer ¶ added in v0.8.0
type CompletedTransfer struct {
ContractID string `json:"contract_id"`
Kind string `json:"kind"` // "direct" | "offer"
Status string `json:"status"` // "completed"
FromPartyID string `json:"from_party_id"`
ToPartyID string `json:"to_party_id"`
Amount string `json:"amount"`
InstrumentAdmin string `json:"instrument_admin"`
InstrumentID string `json:"instrument_id"`
Timestamp string `json:"timestamp"` // RFC3339
TxID string `json:"tx_id,omitempty"` // ledger update id
Symbol string `json:"symbol,omitempty"`
Decimals int `json:"decimals,omitempty"`
Name string `json:"name,omitempty"`
ContractAddress string `json:"contract_address,omitempty"`
}
CompletedTransfer is a single settled transfer in the unified history view, generalized across all tokens (our CIP-56 tokens and external ones like USDCx) and both transfer shapes (direct CIP-56 and offer-based).
type CompletedTransfersList ¶ added in v0.8.0
type CompletedTransfersList struct {
Items []CompletedTransfer `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
HasMore bool `json:"has_more"`
}
CompletedTransfersList is the HTTP response body for GET /api/v2/transfer/completed.
type CustodialTransferRequest ¶ added in v0.8.0
type CustodialTransferRequest struct {
ToPartyID string `json:"to_party_id"` // Recipient Canton party id (<hint>::<fingerprint>)
Amount string `json:"amount"` // Token amount (decimal string)
Token string `json:"token"` // Token symbol
ValiditySeconds int64 `json:"validity_seconds"` // Offer validity window in seconds; must be > 0
}
CustodialTransferRequest is the HTTP request body for the custodial transfer endpoint, which sends a token to an arbitrary recipient party id in a single server-signed call (the middleware holds the custodial user's Canton key).
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 IndexerReader ¶ added in v0.8.0
type IndexerReader interface {
GetTransfers(
ctx context.Context, partyID string, query indexer.TransferQuery, p indexer.Pagination,
) (*indexer.Page[indexer.Transfer], error)
// GetTransfer returns a single transfer by its contract id (used to validate a
// claim-back request directly, without scanning a party's transfer list).
GetTransfer(ctx context.Context, contractID string) (*indexer.Transfer, error)
}
IndexerReader is the slice of indexer/client.Client the transfer service uses to read TransferOffer state. The indexer is the source of truth for the offer lifecycle (incoming/outgoing/expired/accepted), so the service reads from it rather than re-decoding Canton contracts.
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 OutgoingTransfer ¶ added in v0.8.0
type OutgoingTransfer 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"`
Status string `json:"status"`
CreatedAt string `json:"created_at"` // RFC3339; ledger effective time of the offer's creation
ExpiresAt string `json:"expires_at,omitempty"` // RFC3339; omitted when the offer never expires
Symbol string `json:"symbol,omitempty"`
Decimals int `json:"decimals,omitempty"`
Name string `json:"name,omitempty"`
ContractAddress string `json:"contract_address,omitempty"`
}
OutgoingTransfer is a single TransferOffer the queried party sent. Mirrors IncomingTransfer but adds Status (pending/expired/completed/canceled/rejected) and ExpiresAt so callers can track an outbound offer through its lifecycle.
type OutgoingTransfersList ¶ added in v0.8.0
type OutgoingTransfersList struct {
Items []OutgoingTransfer `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
HasMore bool `json:"has_more"`
}
OutgoingTransfersList is the HTTP response body for GET /api/v2/transfer/outgoing.
type PartyRegistry ¶ added in v0.8.0
PartyRegistry checks whether a party id is known to the participant's topology. Implemented by the Canton identity client; used to reject external-token transfers to nonexistent parties before submission.
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,omitempty"` // Recipient EVM address (0x...) of a registered user
ToPartyID string `json:"to_party_id,omitempty"` // Recipient Canton party id (<hint>::<fingerprint>)
Amount string `json:"amount"` // Token amount (decimal string)
Token string `json:"token"` // "DEMO" or "PROMPT"
ValiditySeconds int64 `json:"validity_seconds"` // Offer validity window in seconds; must be > 0
}
PrepareRequest is the HTTP request body for preparing a non-custodial transfer. Exactly one of To (a registered user's EVM address) or ToPartyID (an arbitrary Canton party id, e.g. a party on an external participant node) must be set.
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.
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)
// SendCustodial performs a single-call, server-signed transfer for a custodial
// user to an arbitrary recipient party id (the middleware holds the user's
// Canton key, so there is no client prepare/execute round-trip).
SendCustodial(ctx context.Context, senderEVMAddr string, req *CustodialTransferRequest) (*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)
// ListOutgoing returns one page of the user's outbound transfers filtered
// by status (pending / expired / completed / canceled / rejected / all).
// Like ListIncoming it is unauthenticated and truncates party IDs.
ListOutgoing(ctx context.Context, evmAddr string, status string, p indexer.Pagination) (*OutgoingTransfersList, error)
// ListCompleted returns one page of the user's settled transfers across all
// tokens (TokenTransferEvents and accepted TransferOffers), newest first.
ListCompleted(ctx context.Context, evmAddr string, p indexer.Pagination) (*CompletedTransfersList, 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)
// PrepareWithdraw builds a Canton transaction for a non-custodial sender to claim
// back (withdraw) a pending/expired offer they sent. Complete it via Execute.
PrepareWithdraw(ctx context.Context, evmAddr, contractID string) (*PrepareResponse, error)
// WithdrawCustodial claims back a pending/expired offer for a custodial sender in a
// single server-signed call.
WithdrawCustodial(ctx context.Context, evmAddr, contractID string) (*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 IndexerReader, partyRegistry PartyRegistry, ) *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) ListCompleted ¶ added in v0.8.0
func (s *TransferService) ListCompleted( ctx context.Context, evmAddr string, p indexer.Pagination, ) (*CompletedTransfersList, error)
ListCompleted returns one page of the user's settled transfers across all tokens. Data comes from the indexer's generalized completed-transfers query; party IDs are truncated like the other read endpoints since this is unauthenticated.
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) ListOutgoing ¶ added in v0.8.0
func (s *TransferService) ListOutgoing( ctx context.Context, evmAddr string, status string, p indexer.Pagination, ) (*OutgoingTransfersList, error)
ListOutgoing returns one page of the user's outbound transfers filtered by status. Data comes from the indexer (role=sender); party IDs are truncated like ListIncoming since the endpoint is unauthenticated.
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.
func (*TransferService) PrepareWithdraw ¶ added in v0.8.0
func (s *TransferService) PrepareWithdraw(ctx context.Context, evmAddr, contractID string) (*PrepareResponse, error)
PrepareWithdraw builds a Canton transaction for a non-custodial sender to claim back (withdraw) a pending or expired offer they sent. Returns the hash to sign; complete it via the standard Execute endpoint.
func (*TransferService) SendCustodial ¶ added in v0.8.0
func (s *TransferService) SendCustodial( ctx context.Context, senderEVMAddr string, req *CustodialTransferRequest, ) (*ExecuteResponse, error)
SendCustodial performs a single-call, server-signed transfer for a custodial user to an arbitrary recipient party id. The middleware holds the custodial user's Canton key, so it both prepares and executes the transfer (no client round-trip). The recipient must accept the resulting TransferOffer on their own participant node; this call only creates and submits the offer.
func (*TransferService) WithdrawCustodial ¶ added in v0.8.0
func (s *TransferService) WithdrawCustodial(ctx context.Context, evmAddr, contractID string) (*ExecuteResponse, error)
WithdrawCustodial claims back a pending or expired offer for a custodial sender in a single server-signed call (the middleware holds the user's Canton key).