Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 46 additions & 9 deletions pkg/cantonsdk/token/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const (

spliceTransferModule = "Splice.Api.Token.TransferInstructionV1"
spliceTransferFactory = "TransferFactory"

spliceHoldingModule = "Splice.Api.Token.HoldingV1"
spliceHoldingEntity = "Holding"
)

// Token defines CIP-56 token operations.
Expand All @@ -51,10 +54,17 @@ type Token interface {
// Burn burns tokens using TokenConfig.IssuerBurn.
Burn(ctx context.Context, req *BurnRequest) error

// GetHoldings returns all CIP56Holding contracts for the owner and token symbol.
// GetHoldings returns holdings for the owner and token symbol.
// Delegates to GetHoldingsByParty using the Splice HoldingV1 interface.
GetHoldings(ctx context.Context, ownerParty string, tokenSymbol string) ([]*Holding, error)

// GetAllHoldings GetHoldings returns all CIP56Holding contracts.
// GetHoldingsByParty queries all Splice HoldingV1 holdings visible to the given party,
// optionally filtered by instrumentID (empty string returns all instruments).
// This is the unified query path for all Splice-compliant tokens (CIP-56 and external).
GetHoldingsByParty(ctx context.Context, ownerParty, instrumentID string) ([]*Holding, error)

// GetAllHoldings returns all CIP56Holding contracts queried as IssuerParty.
// Used by the indexer and totalSupply — does NOT use the unified HoldingV1 path.
GetAllHoldings(ctx context.Context) ([]*Holding, error) // TODO: use pagination

// GetBalanceByFingerprint returns the owner's total balance (sum of holdings) for the token symbol.
Expand Down Expand Up @@ -259,21 +269,48 @@ func (c *Client) GetHoldings(ctx context.Context, ownerParty string, tokenSymbol
if tokenSymbol == "" {
return nil, fmt.Errorf("token symbol is required")
}
return c.GetHoldingsByParty(ctx, ownerParty, tokenSymbol)
}

allHoldings, err := c.GetAllHoldings(ctx)
// GetHoldingsByParty queries all Splice HoldingV1 holdings visible to the given party.
// This is the unified query path for all Splice-compliant tokens (CIP-56 and external like USDCx).
// If instrumentID is non-empty, results are filtered to that instrument.
func (c *Client) GetHoldingsByParty(ctx context.Context, ownerParty, instrumentID string) ([]*Holding, error) {
if ownerParty == "" {
return nil, fmt.Errorf("owner party is required")
}

end, err := c.ledger.GetLedgerEnd(ctx)
if err != nil {
return nil, err
}
if end == 0 {
return []*Holding{}, nil
}

validHoldings := make([]*Holding, 0)
for _, h := range allHoldings {
if h.Owner != ownerParty || h.Symbol != tokenSymbol {
iid := &lapiv2.Identifier{
PackageId: c.cfg.SpliceHoldingPackageID,
ModuleName: spliceHoldingModule,
EntityName: spliceHoldingEntity,
}

events, err := c.ledger.GetActiveContractsByInterface(ctx, end, []string{ownerParty}, iid)
if err != nil {
return nil, fmt.Errorf("query holdings by party: %w", err)
}

out := make([]*Holding, 0, len(events))
for _, ce := range events {
h := decodeHolding(ce)
Comment thread
salindne marked this conversation as resolved.
if h.Owner != ownerParty {
continue
}
if instrumentID != "" && h.InstrumentID != instrumentID {
continue
}
validHoldings = append(validHoldings, h)
out = append(out, h)
}

return validHoldings, nil
return out, nil
}

func (c *Client) GetAllHoldings(ctx context.Context) ([]*Holding, error) {
Expand Down
60 changes: 60 additions & 0 deletions pkg/token/mocks/mock_canton_token.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions pkg/transfer/mocks/mock_canton_token.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading