Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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