Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 4 additions & 1 deletion cmd/ledgerctl/accounts/aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ func runAggregateVolumes(cmd *cobra.Command, _ []string) error {
{
type jsonVolume struct {
Asset string `json:"asset"`
Color string `json:"color"`
Input string `json:"input"`
Output string `json:"output"`
Balance string `json:"balance"`
Expand All @@ -112,6 +113,7 @@ func runAggregateVolumes(cmd *cobra.Command, _ []string) error {
balance := new(big.Int).Sub(input, output)
volumes = append(volumes, jsonVolume{
Asset: vol.GetAsset(),
Color: vol.GetColor(),
Input: input.String(),
Output: output.String(),
Balance: balance.String(),
Expand All @@ -130,7 +132,7 @@ func runAggregateVolumes(cmd *cobra.Command, _ []string) error {
}

tableData := pterm.TableData{
{"ASSET", "INPUT", "OUTPUT", "BALANCE"},
{"ASSET", "COLOR", "INPUT", "OUTPUT", "BALANCE"},
}

for _, vol := range result.GetVolumes() {
Expand All @@ -139,6 +141,7 @@ func runAggregateVolumes(cmd *cobra.Command, _ []string) error {
balance := new(big.Int).Sub(input, output)
tableData = append(tableData, []string{
vol.GetAsset(),
vol.GetColor(),
input.String(),
output.String(),
formatBalance(balance),
Expand Down
24 changes: 12 additions & 12 deletions cmd/ledgerctl/accounts/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package accounts
import (
"errors"
"fmt"
"sort"

"github.com/pterm/pterm"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -129,27 +128,28 @@ func runGet(cmd *cobra.Command, args []string) error {

if len(account.GetVolumes()) > 0 {
volumesTable := pterm.TableData{
{"ASSET", "INPUT", "OUTPUT", "BALANCE"},
{"ASSET", "COLOR", "INPUT", "OUTPUT", "BALANCE"},
}

assets := make([]string, 0, len(account.GetVolumes()))
for asset := range account.GetVolumes() {
assets = append(assets, asset)
}

sort.Strings(assets)

for _, asset := range assets {
vol := account.GetVolumes()[asset]
// account.GetVolumes() is already sorted by (asset, color) ascending
// server-side, so we just render in-order.
for _, entry := range account.GetVolumes() {
vol := entry.GetVolumes()
balance := vol.GetBalance()

balanceColor := pterm.Green
if balance != "" && balance[0] == '-' {
balanceColor = pterm.Red
}

displayColor := entry.GetColor()
if displayColor == "" {
displayColor = "-"
}

volumesTable = append(volumesTable, []string{
asset,
entry.GetAsset(),
displayColor,
vol.GetInput(),
vol.GetOutput(),
balanceColor(balance),
Expand Down
6 changes: 4 additions & 2 deletions cmd/ledgerctl/queries/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,11 @@ func renderAggregate(cmd *cobra.Command, result *commonpb.AggregateResult) error
}

if len(result.GetVolumes()) > 0 {
tableData := pterm.TableData{{"ASSET", "INPUT", "OUTPUT"}}
tableData := pterm.TableData{{"ASSET", "COLOR", "INPUT", "OUTPUT"}}
for _, v := range result.GetVolumes() {
tableData = append(tableData, []string{
v.GetAsset(),
v.GetColor(),
v.GetInput().ToBigInt().String(),
v.GetOutput().ToBigInt().String(),
})
Expand All @@ -248,10 +249,11 @@ func renderAggregate(cmd *cobra.Command, result *commonpb.AggregateResult) error
pterm.Println()
pterm.Printfln("Group: %s", g.GetPrefix())

tableData := pterm.TableData{{"ASSET", "INPUT", "OUTPUT"}}
tableData := pterm.TableData{{"ASSET", "COLOR", "INPUT", "OUTPUT"}}
for _, v := range g.GetVolumes() {
tableData = append(tableData, []string{
v.GetAsset(),
v.GetColor(),
v.GetInput().ToBigInt().String(),
v.GetOutput().ToBigInt().String(),
})
Expand Down
29 changes: 21 additions & 8 deletions cmd/ledgerctl/transactions/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func NewCreateCommand() *cobra.Command {
Long: `Create a new transaction via gRPC.

Postings can be provided via flag, or use a Numscript file.
Flag format: --posting "source,destination,amount,asset"
Flag format: --posting "source,destination,amount,asset[,color]"

Examples:
ledgerctl transactions create --ledger my-ledger --posting "world,bank,1000,USD"
Expand All @@ -88,7 +88,7 @@ Examples:
}

cmd.Flags().String("ledger", "", "Name of the ledger")
cmd.Flags().StringArray("posting", nil, "Posting in format: source,destination,amount,asset (can be repeated)")
cmd.Flags().StringArray("posting", nil, "Posting in format: source,destination,amount,asset[,color] (can be repeated)")
cmd.Flags().String("script", "", "Path to a Numscript file (mutually exclusive with --posting)")
cmd.Flags().StringArray("var", nil, "Script variable in format: name=value (can be repeated, only with --script)")
cmd.Flags().String("reference", "", "Transaction reference")
Expand Down Expand Up @@ -460,17 +460,22 @@ func runCreate(cmd *cobra.Command, _ []string) error {
pterm.Println("Postings:")

postingsTable := pterm.TableData{
{"#", "SOURCE", "", "DESTINATION", "AMOUNT", "ASSET"},
{"#", "SOURCE", "", "DESTINATION", "AMOUNT", "ASSET", "COLOR"},
}

for i, posting := range tx.GetPostings() {
color := posting.GetColor()
if color == "" {
color = "-"
}
postingsTable = append(postingsTable, []string{
strconv.Itoa(i + 1),
posting.GetSource(),
"→",
posting.GetDestination(),
posting.GetAmount().Dec(),
posting.GetAsset(),
color,
})
}

Expand Down Expand Up @@ -512,28 +517,36 @@ func runCreate(cmd *cobra.Command, _ []string) error {
return nil
}

// parsePosting parses a posting from string format "source,destination,amount,asset".
// parsePosting parses a posting from string format
// - "source,destination,amount,asset" (uncolored, 4 fields)
// - "source,destination,amount,asset,color" (colored, 5 fields)
//
// The color field is optional. An empty fifth field is treated as no color.
func parsePosting(s string) (*commonpb.Posting, error) {
parts := strings.Split(s, ",")
if len(parts) != 4 {
return nil, errors.New("expected format: source,destination,amount,asset")
if len(parts) != 4 && len(parts) != 5 {
return nil, errors.New("expected format: source,destination,amount,asset[,color]")
}

source := strings.TrimSpace(parts[0])
destination := strings.TrimSpace(parts[1])
amountStr := strings.TrimSpace(parts[2])
asset := strings.TrimSpace(parts[3])
color := ""
if len(parts) == 5 {
color = strings.TrimSpace(parts[4])
}

if source == "" || destination == "" || amountStr == "" || asset == "" {
return nil, errors.New("all fields are required")
return nil, errors.New("source, destination, amount and asset are required")
}

amount, ok := new(big.Int).SetString(amountStr, 10)
if !ok {
return nil, fmt.Errorf("invalid amount: %s", amountStr)
}

return commonpb.NewPosting(source, destination, asset, amount), nil
return commonpb.NewColoredPosting(source, destination, asset, color, amount), nil
}

// promptVariable prompts the user for a Numscript variable value based on its type.
Expand Down
27 changes: 12 additions & 15 deletions cmd/ledgerctl/transactions/display_volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
)

// renderPostCommitVolumes displays a PostCommitVolumes table in the CLI output.
// Volumes are listed per (account, asset, color). The "" color is rendered as
// "-" so the uncolored bucket stands out in the table.
func renderPostCommitVolumes(pcv *commonpb.PostCommitVolumes) error {
if len(pcv.GetVolumesByAccount()) == 0 {
return nil
Expand All @@ -18,33 +20,28 @@ func renderPostCommitVolumes(pcv *commonpb.PostCommitVolumes) error {
pterm.Println("Post-Commit Volumes:")

table := pterm.TableData{
{"ACCOUNT", "ASSET", "INPUT", "OUTPUT"},
{"ACCOUNT", "ASSET", "COLOR", "INPUT", "OUTPUT"},
}

// Sort accounts for stable output
accounts := make([]string, 0, len(pcv.GetVolumesByAccount()))
for account := range pcv.GetVolumesByAccount() {
accounts = append(accounts, account)
}

sort.Strings(accounts)

for _, account := range accounts {
vba := pcv.GetVolumesByAccount()[account]

// Sort assets for stable output
assets := make([]string, 0, len(vba.GetVolumes()))
for asset := range vba.GetVolumes() {
assets = append(assets, asset)
}

sort.Strings(assets)

for _, asset := range assets {
v := vba.GetVolumes()[asset]
// VolumesByAssets.Volumes is sorted by (asset, color) server-side.
for _, entry := range vba.GetVolumes() {
v := entry.GetVolumes()
displayColor := entry.GetColor()
if displayColor == "" {
displayColor = "-"
}
table = append(table, []string{
account,
asset,
entry.GetAsset(),
displayColor,
v.GetInput(),
v.GetOutput(),
})
Expand Down
10 changes: 7 additions & 3 deletions cmd/ledgerctl/transactions/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func runGet(cmd *cobra.Command, args []string) error {
pterm.Println("Postings:")

postingsTable := pterm.TableData{
{"#", "SOURCE", "", "DESTINATION", "AMOUNT", "ASSET"},
{"#", "SOURCE", "", "DESTINATION", "AMOUNT", "ASSET", "COLOR"},
}

termWidth := pterm.GetTerminalWidth()
Expand All @@ -167,7 +167,7 @@ func runGet(cmd *cobra.Command, args []string) error {

for line := range maxLines {
src, dst := "", ""
num, arrow, amount, asset := "", "", "", ""
num, arrow, amount, asset, color := "", "", "", "", ""

if line < len(srcLines) {
src = srcLines[line]
Expand All @@ -188,10 +188,14 @@ func runGet(cmd *cobra.Command, args []string) error {
arrow = "→"
amount = posting.GetAmount().Dec()
asset = posting.GetAsset()
color = posting.GetColor()
if color == "" {
color = "-"
}
}

postingsTable = append(postingsTable, []string{
num, src, arrow, dst, amount, asset,
num, src, arrow, dst, amount, asset, color,
})
}
}
Expand Down
7 changes: 6 additions & 1 deletion cmd/ledgerctl/transactions/revert.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,17 +233,22 @@ func runRevert(cmd *cobra.Command, args []string) error {
pterm.Println("Revert Postings:")

postingsTable := pterm.TableData{
{"#", "SOURCE", "", "DESTINATION", "AMOUNT", "ASSET"},
{"#", "SOURCE", "", "DESTINATION", "AMOUNT", "ASSET", "COLOR"},
}

for i, posting := range revertedTx.GetRevertTransaction().GetPostings() {
color := posting.GetColor()
if color == "" {
color = "-"
}
postingsTable = append(postingsTable, []string{
strconv.Itoa(i + 1),
posting.GetSource(),
"→",
posting.GetDestination(),
posting.GetAmount().Dec(),
posting.GetAsset(),
color,
})
}

Expand Down
20 changes: 19 additions & 1 deletion docs/ops/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1285,7 +1285,7 @@ ledgerctl transactions create [flags]
| Flag | Default | Description |
|------|---------|-------------|
| `--ledger` | | Name of the ledger |
| `--posting` | | Posting in format: `source,destination,amount,asset` (can be repeated) |
| `--posting` | | Posting in format: `source,destination,amount,asset[,color]` (can be repeated) |
| `--script` | | Path to a Numscript file (mutually exclusive with `--posting`) |
| `--var` | | Script variable in format: `name=value` (can be repeated, only with `--script`) |
| `--reference` | | Transaction reference |
Expand Down Expand Up @@ -1316,8 +1316,26 @@ ledgerctl transactions create --ledger my-ledger \
ledgerctl transactions create --ledger my-ledger \
--posting "empty-account,destination,1000,USD" \
--force

# Colored postings (segregated balances per (account, asset, color)).
# Color must match ^[A-Z]*$. An empty/missing color is the uncolored bucket
# and is itself segregated from every colored bucket.
ledgerctl transactions create --ledger my-ledger \
--posting "world,treasury,1000,USD/2,GRANTS"

# Mixed colored and uncolored postings in a single transaction
ledgerctl transactions create --ledger my-ledger \
--posting "world,treasury,500,USD/2" \
--posting "world,treasury,500,USD/2,OPS"
```

**Inspecting colored balances:**

`ledgerctl accounts get` lists one row per `(asset, color)` tuple. To sum
every colored bucket of the same asset into a single uncolored entry, pass
`?collapseColors=true` on the HTTP endpoint (the CLI surfaces the raw,
segregated view by design).

**Creating transactions with Numscript:**

```bash
Expand Down
Loading
Loading