feat: add --account-label as alternative to --account-id across all wallet subcommands

Allow users to identify accounts by their human-readable label instead of the
full `Privacy/base58` account ID. This makes the CLI much more ergonomic for
users who have labeled their accounts.

- [x] Add `resolve_account_label()` in `helperfunctions.rs` that looks up a label,
  determines account privacy (public/private), and returns the full `Privacy/id` string
- [x] Add `--account-label` (or `--from-label`, `--to-label`, `--definition-label`,
  `--holder-label`, `--user-holding-*-label`) as mutually exclusive alternative to
  every `--account-id`-style flag across all subcommands:
  - `account get`, `account label`
  - `auth-transfer init`, `auth-transfer send`
  - `token new`, `token send`, `token burn`, `token mint`
  - `pinata claim`
  - `amm new`, `amm swap`, `amm add-liquidity`, `amm remove-liquidity`
- [x] Update zsh completion script with `_wallet_account_labels()` helper
- [x] Add bash completion script with `_wallet_get_account_labels()` helper

1. Start a local sequencer
2. Create accounts and label them: `wallet account new public --label alice`
3. Use labels in commands: `wallet account get --account-label alice`
4. Verify mutual exclusivity: `wallet account get --account-id <id> --account-label alice` should error
5. Test shell completions: `wallet account get --account-label <TAB>` should list labels

None

None

- [x] Complete PR description
- [x] Implement the core functionality
- [ ] Add/update tests
- [x] Add/update documentation and inline comments

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
fryorcraken 2026-03-13 13:33:51 +11:00
parent fb083ce91e
commit 1474e01a26
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
8 changed files with 686 additions and 87 deletions

View File

@ -22,6 +22,20 @@ _wallet_complete_account_id() {
fi
}
# Helper function to complete account labels
_wallet_complete_account_label() {
local cur="$1"
local labels
if command -v wallet &>/dev/null; then
labels=$(wallet account list 2>/dev/null | grep -o '\[.*\]' | sed 's/^\[//;s/\]$//')
fi
if [[ -n "$labels" ]]; then
COMPREPLY=($(compgen -W "$labels" -- "$cur"))
fi
}
_wallet() {
local cur prev words cword
_init_completion 2>/dev/null || {
@ -91,20 +105,32 @@ _wallet() {
--account-id)
_wallet_complete_account_id "$cur"
;;
--account-label)
_wallet_complete_account_label "$cur"
;;
*)
COMPREPLY=($(compgen -W "--account-id" -- "$cur"))
COMPREPLY=($(compgen -W "--account-id --account-label" -- "$cur"))
;;
esac
;;
send)
case "$prev" in
--from | --to)
--from)
_wallet_complete_account_id "$cur"
;;
--from-label)
_wallet_complete_account_label "$cur"
;;
--to)
_wallet_complete_account_id "$cur"
;;
--to-label)
_wallet_complete_account_label "$cur"
;;
--to-npk | --to-vpk | --amount)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--from --to --to-npk --to-vpk --amount" -- "$cur"))
COMPREPLY=($(compgen -W "--from --from-label --to --to-label --to-npk --to-vpk --amount" -- "$cur"))
;;
esac
;;
@ -147,8 +173,11 @@ _wallet() {
-a | --account-id)
_wallet_complete_account_id "$cur"
;;
--account-label)
_wallet_complete_account_label "$cur"
;;
*)
COMPREPLY=($(compgen -W "-r --raw -k --keys -a --account-id" -- "$cur"))
COMPREPLY=($(compgen -W "-r --raw -k --keys -a --account-id --account-label" -- "$cur"))
;;
esac
;;
@ -186,10 +215,13 @@ _wallet() {
-a | --account-id)
_wallet_complete_account_id "$cur"
;;
--account-label)
_wallet_complete_account_label "$cur"
;;
-l | --label)
;; # no specific completion for label value
*)
COMPREPLY=($(compgen -W "-a --account-id -l --label" -- "$cur"))
COMPREPLY=($(compgen -W "-a --account-id --account-label -l --label" -- "$cur"))
;;
esac
;;
@ -206,8 +238,11 @@ _wallet() {
--to)
_wallet_complete_account_id "$cur"
;;
--to-label)
_wallet_complete_account_label "$cur"
;;
*)
COMPREPLY=($(compgen -W "--to" -- "$cur"))
COMPREPLY=($(compgen -W "--to --to-label" -- "$cur"))
;;
esac
;;
@ -221,49 +256,85 @@ _wallet() {
;;
new)
case "$prev" in
--definition-account-id | --supply-account-id)
--definition-account-id)
_wallet_complete_account_id "$cur"
;;
--definition-account-label)
_wallet_complete_account_label "$cur"
;;
--supply-account-id)
_wallet_complete_account_id "$cur"
;;
--supply-account-label)
_wallet_complete_account_label "$cur"
;;
-n | --name | -t | --total-supply)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--definition-account-id --supply-account-id -n --name -t --total-supply" -- "$cur"))
COMPREPLY=($(compgen -W "--definition-account-id --definition-account-label --supply-account-id --supply-account-label -n --name -t --total-supply" -- "$cur"))
;;
esac
;;
send)
case "$prev" in
--from | --to)
--from)
_wallet_complete_account_id "$cur"
;;
--from-label)
_wallet_complete_account_label "$cur"
;;
--to)
_wallet_complete_account_id "$cur"
;;
--to-label)
_wallet_complete_account_label "$cur"
;;
--to-npk | --to-vpk | --amount)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--from --to --to-npk --to-vpk --amount" -- "$cur"))
COMPREPLY=($(compgen -W "--from --from-label --to --to-label --to-npk --to-vpk --amount" -- "$cur"))
;;
esac
;;
burn)
case "$prev" in
--definition | --holder)
--definition)
_wallet_complete_account_id "$cur"
;;
--definition-label)
_wallet_complete_account_label "$cur"
;;
--holder)
_wallet_complete_account_id "$cur"
;;
--holder-label)
_wallet_complete_account_label "$cur"
;;
--amount)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--definition --holder --amount" -- "$cur"))
COMPREPLY=($(compgen -W "--definition --definition-label --holder --holder-label --amount" -- "$cur"))
;;
esac
;;
mint)
case "$prev" in
--definition | --holder)
--definition)
_wallet_complete_account_id "$cur"
;;
--definition-label)
_wallet_complete_account_label "$cur"
;;
--holder)
_wallet_complete_account_id "$cur"
;;
--holder-label)
_wallet_complete_account_label "$cur"
;;
--holder-npk | --holder-vpk | --amount)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--definition --holder --holder-npk --holder-vpk --amount" -- "$cur"))
COMPREPLY=($(compgen -W "--definition --definition-label --holder --holder-label --holder-npk --holder-vpk --amount" -- "$cur"))
;;
esac
;;
@ -277,49 +348,103 @@ _wallet() {
;;
new)
case "$prev" in
--user-holding-a | --user-holding-b | --user-holding-lp)
--user-holding-a)
_wallet_complete_account_id "$cur"
;;
--user-holding-a-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-b)
_wallet_complete_account_id "$cur"
;;
--user-holding-b-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-lp)
_wallet_complete_account_id "$cur"
;;
--user-holding-lp-label)
_wallet_complete_account_label "$cur"
;;
--balance-a | --balance-b)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-b --user-holding-lp --balance-a --balance-b" -- "$cur"))
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-a-label --user-holding-b --user-holding-b-label --user-holding-lp --user-holding-lp-label --balance-a --balance-b" -- "$cur"))
;;
esac
;;
swap)
case "$prev" in
--user-holding-a | --user-holding-b)
--user-holding-a)
_wallet_complete_account_id "$cur"
;;
--user-holding-a-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-b)
_wallet_complete_account_id "$cur"
;;
--user-holding-b-label)
_wallet_complete_account_label "$cur"
;;
--amount-in | --min-amount-out | --token-definition)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-b --amount-in --min-amount-out --token-definition" -- "$cur"))
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-a-label --user-holding-b --user-holding-b-label --amount-in --min-amount-out --token-definition" -- "$cur"))
;;
esac
;;
add-liquidity)
case "$prev" in
--user-holding-a | --user-holding-b | --user-holding-lp)
--user-holding-a)
_wallet_complete_account_id "$cur"
;;
--user-holding-a-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-b)
_wallet_complete_account_id "$cur"
;;
--user-holding-b-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-lp)
_wallet_complete_account_id "$cur"
;;
--user-holding-lp-label)
_wallet_complete_account_label "$cur"
;;
--max-amount-a | --max-amount-b | --min-amount-lp)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-b --user-holding-lp --max-amount-a --max-amount-b --min-amount-lp" -- "$cur"))
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-a-label --user-holding-b --user-holding-b-label --user-holding-lp --user-holding-lp-label --max-amount-a --max-amount-b --min-amount-lp" -- "$cur"))
;;
esac
;;
remove-liquidity)
case "$prev" in
--user-holding-a | --user-holding-b | --user-holding-lp)
--user-holding-a)
_wallet_complete_account_id "$cur"
;;
--user-holding-a-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-b)
_wallet_complete_account_id "$cur"
;;
--user-holding-b-label)
_wallet_complete_account_label "$cur"
;;
--user-holding-lp)
_wallet_complete_account_id "$cur"
;;
--user-holding-lp-label)
_wallet_complete_account_label "$cur"
;;
--balance-lp | --min-amount-a | --min-amount-b)
;; # no specific completion
*)
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-b --user-holding-lp --balance-lp --min-amount-a --min-amount-b" -- "$cur"))
COMPREPLY=($(compgen -W "--user-holding-a --user-holding-a-label --user-holding-b --user-holding-b-label --user-holding-lp --user-holding-lp-label --balance-lp --min-amount-a --min-amount-b" -- "$cur"))
;;
esac
;;

View File

@ -90,12 +90,15 @@ _wallet_auth_transfer() {
case $line[1] in
init)
_arguments \
'--account-id[Account ID to initialize]:account_id:_wallet_account_ids'
'--account-id[Account ID to initialize]:account_id:_wallet_account_ids' \
'--account-label[Account label (alternative to --account-id)]:label:_wallet_account_labels'
;;
send)
_arguments \
'--from[Source account ID]:from_account:_wallet_account_ids' \
'--from-label[Source account label (alternative to --from)]:label:_wallet_account_labels' \
'--to[Destination account ID (for owned accounts)]:to_account:_wallet_account_ids' \
'--to-label[Destination account label (alternative to --to)]:label:_wallet_account_labels' \
'--to-npk[Destination nullifier public key (for foreign private accounts)]:npk:' \
'--to-vpk[Destination viewing public key (for foreign private accounts)]:vpk:' \
'--amount[Amount of native tokens to send]:amount:'
@ -165,7 +168,8 @@ _wallet_account() {
_arguments \
'(-r --raw)'{-r,--raw}'[Get raw account data]' \
'(-k --keys)'{-k,--keys}'[Display keys (pk for public accounts, npk/vpk for private accounts)]' \
'(-a --account-id)'{-a,--account-id}'[Account ID to query]:account_id:_wallet_account_ids'
'(-a --account-id)'{-a,--account-id}'[Account ID to query]:account_id:_wallet_account_ids' \
'--account-label[Account label (alternative to --account-id)]:label:_wallet_account_labels'
;;
list|ls)
_arguments \
@ -189,6 +193,7 @@ _wallet_account() {
label)
_arguments \
'(-a --account-id)'{-a,--account-id}'[Account ID to label]:account_id:_wallet_account_ids' \
'--account-label[Account label (alternative to --account-id)]:label:_wallet_account_labels' \
'(-l --label)'{-l,--label}'[The label to assign to the account]:label:'
;;
esac
@ -216,7 +221,8 @@ _wallet_pinata() {
case $line[1] in
claim)
_arguments \
'--to[Destination account ID to receive claimed tokens]:to_account:_wallet_account_ids'
'--to[Destination account ID to receive claimed tokens]:to_account:_wallet_account_ids' \
'--to-label[Destination account label (alternative to --to)]:label:_wallet_account_labels'
;;
esac
;;
@ -249,12 +255,16 @@ _wallet_token() {
'--name[Token name]:name:' \
'--total-supply[Total supply of tokens to mint]:total_supply:' \
'--definition-account-id[Account ID for token definition]:definition_account:_wallet_account_ids' \
'--supply-account-id[Account ID to receive initial supply]:supply_account:_wallet_account_ids'
'--definition-account-label[Definition account label (alternative to --definition-account-id)]:label:_wallet_account_labels' \
'--supply-account-id[Account ID to receive initial supply]:supply_account:_wallet_account_ids' \
'--supply-account-label[Supply account label (alternative to --supply-account-id)]:label:_wallet_account_labels'
;;
send)
_arguments \
'--from[Source holding account ID]:from_account:_wallet_account_ids' \
'--from-label[Source account label (alternative to --from)]:label:_wallet_account_labels' \
'--to[Destination holding account ID (for owned accounts)]:to_account:_wallet_account_ids' \
'--to-label[Destination account label (alternative to --to)]:label:_wallet_account_labels' \
'--to-npk[Destination nullifier public key (for foreign private accounts)]:npk:' \
'--to-vpk[Destination viewing public key (for foreign private accounts)]:vpk:' \
'--amount[Amount of tokens to send]:amount:'
@ -262,13 +272,17 @@ _wallet_token() {
burn)
_arguments \
'--definition[Definition account ID]:definition_account:_wallet_account_ids' \
'--definition-label[Definition account label (alternative to --definition)]:label:_wallet_account_labels' \
'--holder[Holder account ID]:holder_account:_wallet_account_ids' \
'--holder-label[Holder account label (alternative to --holder)]:label:_wallet_account_labels' \
'--amount[Amount of tokens to burn]:amount:'
;;
mint)
_arguments \
'--definition[Definition account ID]:definition_account:_wallet_account_ids' \
'--definition-label[Definition account label (alternative to --definition)]:label:_wallet_account_labels' \
'--holder[Holder account ID (for owned accounts)]:holder_account:_wallet_account_ids' \
'--holder-label[Holder account label (alternative to --holder)]:label:_wallet_account_labels' \
'--holder-npk[Holder nullifier public key (for foreign private accounts)]:npk:' \
'--holder-vpk[Holder viewing public key (for foreign private accounts)]:vpk:' \
'--amount[Amount of tokens to mint]:amount:'
@ -302,15 +316,20 @@ _wallet_amm() {
new)
_arguments \
'--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \
'--user-holding-a-label[User holding A account label (alternative to --user-holding-a)]:label:_wallet_account_labels' \
'--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \
'--user-holding-b-label[User holding B account label (alternative to --user-holding-b)]:label:_wallet_account_labels' \
'--user-holding-lp[User LP token holding account ID]:holding_lp:_wallet_account_ids' \
'--user-holding-lp-label[User holding LP account label (alternative to --user-holding-lp)]:label:_wallet_account_labels' \
'--balance-a[Amount of token A to deposit]:balance_a:' \
'--balance-b[Amount of token B to deposit]:balance_b:'
;;
swap)
_arguments \
'--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \
'--user-holding-a-label[User holding A account label (alternative to --user-holding-a)]:label:_wallet_account_labels' \
'--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \
'--user-holding-b-label[User holding B account label (alternative to --user-holding-b)]:label:_wallet_account_labels' \
'--amount-in[Amount of tokens to swap]:amount_in:' \
'--min-amount-out[Minimum tokens expected in return]:min_amount_out:' \
'--token-definition[Definition ID of the token being provided]:token_def:'
@ -318,8 +337,11 @@ _wallet_amm() {
add-liquidity)
_arguments \
'--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \
'--user-holding-a-label[User holding A account label (alternative to --user-holding-a)]:label:_wallet_account_labels' \
'--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \
'--user-holding-b-label[User holding B account label (alternative to --user-holding-b)]:label:_wallet_account_labels' \
'--user-holding-lp[User LP token holding account ID]:holding_lp:_wallet_account_ids' \
'--user-holding-lp-label[User holding LP account label (alternative to --user-holding-lp)]:label:_wallet_account_labels' \
'--max-amount-a[Maximum amount of token A to deposit]:max_amount_a:' \
'--max-amount-b[Maximum amount of token B to deposit]:max_amount_b:' \
'--min-amount-lp[Minimum LP tokens to receive]:min_amount_lp:'
@ -327,8 +349,11 @@ _wallet_amm() {
remove-liquidity)
_arguments \
'--user-holding-a[User token A holding account ID]:holding_a:_wallet_account_ids' \
'--user-holding-a-label[User holding A account label (alternative to --user-holding-a)]:label:_wallet_account_labels' \
'--user-holding-b[User token B holding account ID]:holding_b:_wallet_account_ids' \
'--user-holding-b-label[User holding B account label (alternative to --user-holding-b)]:label:_wallet_account_labels' \
'--user-holding-lp[User LP token holding account ID]:holding_lp:_wallet_account_ids' \
'--user-holding-lp-label[User holding LP account label (alternative to --user-holding-lp)]:label:_wallet_account_labels' \
'--balance-lp[Amount of LP tokens to burn]:balance_lp:' \
'--min-amount-a[Minimum token A to receive]:min_amount_a:' \
'--min-amount-b[Minimum token B to receive]:min_amount_b:'
@ -424,7 +449,7 @@ _wallet_help() {
_wallet_account_ids() {
local -a accounts
local line
# Try to get accounts from wallet account list command
# Filter to lines starting with /N (numbered accounts) and extract the account ID
if command -v wallet &>/dev/null; then
@ -433,14 +458,35 @@ _wallet_account_ids() {
[[ -n "$line" ]] && accounts+=("${line%,}")
done < <(wallet account list 2>/dev/null | grep '^/[0-9]' | awk '{print $2}')
fi
# Provide type prefixes as fallback if command fails or returns nothing
if (( ${#accounts} == 0 )); then
compadd -S '' -- 'Public/' 'Private/'
return
fi
_multi_parts / accounts
}
# Helper function to complete account labels
# Uses `wallet account list` to get available labels
_wallet_account_labels() {
local -a labels
local line
if command -v wallet &>/dev/null; then
while IFS= read -r line; do
local label
# Extract label from [...] at end of line
label="${line##*\[}"
label="${label%\]}"
[[ -n "$label" && "$label" != "$line" ]] && labels+=("$label")
done < <(wallet account list 2>/dev/null)
fi
if (( ${#labels} > 0 )); then
compadd -a labels
fi
}
_wallet "$@"

View File

@ -10,7 +10,10 @@ use crate::{
WalletCore,
cli::{SubcommandReturnValue, WalletSubcommand},
config::Label,
helperfunctions::{AccountPrivacyKind, HumanReadableAccount, parse_addr_with_privacy_prefix},
helperfunctions::{
AccountPrivacyKind, HumanReadableAccount, parse_addr_with_privacy_prefix,
resolve_id_or_label,
},
};
/// Represents generic chain CLI subcommand.
@ -25,8 +28,16 @@ pub enum AccountSubcommand {
#[arg(short, long)]
keys: bool,
/// Valid 32 byte base58 string with privacy prefix.
#[arg(short, long)]
account_id: String,
#[arg(
short,
long,
conflicts_with = "account_label",
required_unless_present = "account_label"
)]
account_id: Option<String>,
/// Account label (alternative to --account-id).
#[arg(long, conflicts_with = "account_id")]
account_label: Option<String>,
},
/// Produce new public or private account.
#[command(subcommand)]
@ -43,8 +54,16 @@ pub enum AccountSubcommand {
/// Set a label for an account.
Label {
/// Valid 32 byte base58 string with privacy prefix.
#[arg(short, long)]
account_id: String,
#[arg(
short,
long,
conflicts_with = "account_label",
required_unless_present = "account_label"
)]
account_id: Option<String>,
/// Account label (alternative to --account-id).
#[arg(long = "account-label", conflicts_with = "account_id")]
account_label: Option<String>,
/// The label to assign to the account.
#[arg(short, long)]
label: String,
@ -171,8 +190,15 @@ impl WalletSubcommand for AccountSubcommand {
raw,
keys,
account_id,
account_label,
} => {
let (account_id_str, addr_kind) = parse_addr_with_privacy_prefix(&account_id)?;
let resolved = resolve_id_or_label(
account_id,
account_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let (account_id_str, addr_kind) = parse_addr_with_privacy_prefix(&resolved)?;
let account_id: nssa::AccountId = account_id_str.parse()?;
@ -371,8 +397,18 @@ impl WalletSubcommand for AccountSubcommand {
Ok(SubcommandReturnValue::Empty)
}
Self::Label { account_id, label } => {
let (account_id_str, _) = parse_addr_with_privacy_prefix(&account_id)?;
Self::Label {
account_id,
account_label,
label,
} => {
let resolved = resolve_id_or_label(
account_id,
account_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let (account_id_str, _) = parse_addr_with_privacy_prefix(&resolved)?;
// Check if label is already used by a different account
if let Some(existing_account) = wallet_core

View File

@ -5,7 +5,7 @@ use nssa::AccountId;
use crate::{
WalletCore,
cli::{SubcommandReturnValue, WalletSubcommand},
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix},
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix, resolve_id_or_label},
program_facades::amm::Amm,
};
@ -19,14 +19,35 @@ pub enum AmmProgramAgnosticSubcommand {
/// Only public execution allowed.
New {
/// `user_holding_a` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
user_holding_a: String,
#[arg(
long,
conflicts_with = "user_holding_a_label",
required_unless_present = "user_holding_a_label"
)]
user_holding_a: Option<String>,
/// User holding A account label (alternative to --user-holding-a).
#[arg(long, conflicts_with = "user_holding_a")]
user_holding_a_label: Option<String>,
/// `user_holding_b` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
user_holding_b: String,
#[arg(
long,
conflicts_with = "user_holding_b_label",
required_unless_present = "user_holding_b_label"
)]
user_holding_b: Option<String>,
/// User holding B account label (alternative to --user-holding-b).
#[arg(long, conflicts_with = "user_holding_b")]
user_holding_b_label: Option<String>,
/// `user_holding_lp` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
user_holding_lp: String,
#[arg(
long,
conflicts_with = "user_holding_lp_label",
required_unless_present = "user_holding_lp_label"
)]
user_holding_lp: Option<String>,
/// User holding LP account label (alternative to --user-holding-lp).
#[arg(long, conflicts_with = "user_holding_lp")]
user_holding_lp_label: Option<String>,
#[arg(long)]
balance_a: u128,
#[arg(long)]
@ -39,11 +60,25 @@ pub enum AmmProgramAgnosticSubcommand {
/// Only public execution allowed.
Swap {
/// `user_holding_a` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
user_holding_a: String,
#[arg(
long,
conflicts_with = "user_holding_a_label",
required_unless_present = "user_holding_a_label"
)]
user_holding_a: Option<String>,
/// User holding A account label (alternative to --user-holding-a).
#[arg(long, conflicts_with = "user_holding_a")]
user_holding_a_label: Option<String>,
/// `user_holding_b` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
user_holding_b: String,
#[arg(
long,
conflicts_with = "user_holding_b_label",
required_unless_present = "user_holding_b_label"
)]
user_holding_b: Option<String>,
/// User holding B account label (alternative to --user-holding-b).
#[arg(long, conflicts_with = "user_holding_b")]
user_holding_b_label: Option<String>,
#[arg(long)]
amount_in: u128,
#[arg(long)]
@ -59,14 +94,35 @@ pub enum AmmProgramAgnosticSubcommand {
/// Only public execution allowed.
AddLiquidity {
/// `user_holding_a` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
user_holding_a: String,
#[arg(
long,
conflicts_with = "user_holding_a_label",
required_unless_present = "user_holding_a_label"
)]
user_holding_a: Option<String>,
/// User holding A account label (alternative to --user-holding-a).
#[arg(long, conflicts_with = "user_holding_a")]
user_holding_a_label: Option<String>,
/// `user_holding_b` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
user_holding_b: String,
#[arg(
long,
conflicts_with = "user_holding_b_label",
required_unless_present = "user_holding_b_label"
)]
user_holding_b: Option<String>,
/// User holding B account label (alternative to --user-holding-b).
#[arg(long, conflicts_with = "user_holding_b")]
user_holding_b_label: Option<String>,
/// `user_holding_lp` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
user_holding_lp: String,
#[arg(
long,
conflicts_with = "user_holding_lp_label",
required_unless_present = "user_holding_lp_label"
)]
user_holding_lp: Option<String>,
/// User holding LP account label (alternative to --user-holding-lp).
#[arg(long, conflicts_with = "user_holding_lp")]
user_holding_lp_label: Option<String>,
#[arg(long)]
min_amount_lp: u128,
#[arg(long)]
@ -81,14 +137,35 @@ pub enum AmmProgramAgnosticSubcommand {
/// Only public execution allowed.
RemoveLiquidity {
/// `user_holding_a` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
user_holding_a: String,
#[arg(
long,
conflicts_with = "user_holding_a_label",
required_unless_present = "user_holding_a_label"
)]
user_holding_a: Option<String>,
/// User holding A account label (alternative to --user-holding-a).
#[arg(long, conflicts_with = "user_holding_a")]
user_holding_a_label: Option<String>,
/// `user_holding_b` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
user_holding_b: String,
#[arg(
long,
conflicts_with = "user_holding_b_label",
required_unless_present = "user_holding_b_label"
)]
user_holding_b: Option<String>,
/// User holding B account label (alternative to --user-holding-b).
#[arg(long, conflicts_with = "user_holding_b")]
user_holding_b_label: Option<String>,
/// `user_holding_lp` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
user_holding_lp: String,
#[arg(
long,
conflicts_with = "user_holding_lp_label",
required_unless_present = "user_holding_lp_label"
)]
user_holding_lp: Option<String>,
/// User holding LP account label (alternative to --user-holding-lp).
#[arg(long, conflicts_with = "user_holding_lp")]
user_holding_lp_label: Option<String>,
#[arg(long)]
balance_lp: u128,
#[arg(long)]
@ -106,11 +183,32 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
match self {
Self::New {
user_holding_a,
user_holding_a_label,
user_holding_b,
user_holding_b_label,
user_holding_lp,
user_holding_lp_label,
balance_a,
balance_b,
} => {
let user_holding_a = resolve_id_or_label(
user_holding_a,
user_holding_a_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let user_holding_b = resolve_id_or_label(
user_holding_b,
user_holding_b_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let user_holding_lp = resolve_id_or_label(
user_holding_lp,
user_holding_lp_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let (user_holding_a, user_holding_a_privacy) =
parse_addr_with_privacy_prefix(&user_holding_a)?;
let (user_holding_b, user_holding_b_privacy) =
@ -152,11 +250,25 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
}
Self::Swap {
user_holding_a,
user_holding_a_label,
user_holding_b,
user_holding_b_label,
amount_in,
min_amount_out,
token_definition,
} => {
let user_holding_a = resolve_id_or_label(
user_holding_a,
user_holding_a_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let user_holding_b = resolve_id_or_label(
user_holding_b,
user_holding_b_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let (user_holding_a, user_holding_a_privacy) =
parse_addr_with_privacy_prefix(&user_holding_a)?;
let (user_holding_b, user_holding_b_privacy) =
@ -187,12 +299,33 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
}
Self::AddLiquidity {
user_holding_a,
user_holding_a_label,
user_holding_b,
user_holding_b_label,
user_holding_lp,
user_holding_lp_label,
min_amount_lp,
max_amount_a,
max_amount_b,
} => {
let user_holding_a = resolve_id_or_label(
user_holding_a,
user_holding_a_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let user_holding_b = resolve_id_or_label(
user_holding_b,
user_holding_b_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let user_holding_lp = resolve_id_or_label(
user_holding_lp,
user_holding_lp_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let (user_holding_a, user_holding_a_privacy) =
parse_addr_with_privacy_prefix(&user_holding_a)?;
let (user_holding_b, user_holding_b_privacy) =
@ -235,12 +368,33 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand {
}
Self::RemoveLiquidity {
user_holding_a,
user_holding_a_label,
user_holding_b,
user_holding_b_label,
user_holding_lp,
user_holding_lp_label,
balance_lp,
min_amount_a,
min_amount_b,
} => {
let user_holding_a = resolve_id_or_label(
user_holding_a,
user_holding_a_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let user_holding_b = resolve_id_or_label(
user_holding_b,
user_holding_b_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let user_holding_lp = resolve_id_or_label(
user_holding_lp,
user_holding_lp_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let (user_holding_a, user_holding_a_privacy) =
parse_addr_with_privacy_prefix(&user_holding_a)?;
let (user_holding_b, user_holding_b_privacy) =

View File

@ -7,7 +7,10 @@ use crate::{
AccDecodeData::Decode,
WalletCore,
cli::{SubcommandReturnValue, WalletSubcommand},
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix},
helperfunctions::{
AccountPrivacyKind, parse_addr_with_privacy_prefix, resolve_account_label,
resolve_id_or_label,
},
program_facades::native_token_transfer::NativeTokenTransfer,
};
@ -17,8 +20,15 @@ pub enum AuthTransferSubcommand {
/// Initialize account under authenticated transfer program.
Init {
/// `account_id` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
account_id: String,
#[arg(
long,
conflicts_with = "account_label",
required_unless_present = "account_label"
)]
account_id: Option<String>,
/// Account label (alternative to --account-id).
#[arg(long, conflicts_with = "account_id")]
account_label: Option<String>,
},
/// Send native tokens from one account to another with variable privacy.
///
@ -28,11 +38,21 @@ pub enum AuthTransferSubcommand {
/// First is used for owned accounts, second otherwise.
Send {
/// from - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
from: String,
#[arg(
long,
conflicts_with = "from_label",
required_unless_present = "from_label"
)]
from: Option<String>,
/// From account label (alternative to --from).
#[arg(long, conflicts_with = "from")]
from_label: Option<String>,
/// to - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
#[arg(long, conflicts_with = "to_label")]
to: Option<String>,
/// To account label (alternative to --to).
#[arg(long, conflicts_with = "to")]
to_label: Option<String>,
/// `to_npk` - valid 32 byte hex string.
#[arg(long)]
to_npk: Option<String>,
@ -51,8 +71,17 @@ impl WalletSubcommand for AuthTransferSubcommand {
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
match self {
Self::Init { account_id } => {
let (account_id, addr_privacy) = parse_addr_with_privacy_prefix(&account_id)?;
Self::Init {
account_id,
account_label,
} => {
let resolved = resolve_id_or_label(
account_id,
account_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let (account_id, addr_privacy) = parse_addr_with_privacy_prefix(&resolved)?;
match addr_privacy {
AccountPrivacyKind::Public => {
@ -98,11 +127,30 @@ impl WalletSubcommand for AuthTransferSubcommand {
}
Self::Send {
from,
from_label,
to,
to_label,
to_npk,
to_vpk,
amount,
} => {
let from = resolve_id_or_label(
from,
from_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let to = match (to, to_label) {
(v, None) => v,
(None, Some(label)) => Some(resolve_account_label(
&label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?),
(Some(_), Some(_)) => {
anyhow::bail!("Provide only one of --to or --to-label")
}
};
let underlying_subcommand = match (to, to_npk, to_vpk) {
(None, None, None) => {
anyhow::bail!(

View File

@ -7,7 +7,7 @@ use crate::{
AccDecodeData::Decode,
WalletCore,
cli::{SubcommandReturnValue, WalletSubcommand},
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix},
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix, resolve_id_or_label},
program_facades::pinata::Pinata,
};
@ -17,8 +17,15 @@ pub enum PinataProgramAgnosticSubcommand {
/// Claim pinata.
Claim {
/// to - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
to: String,
#[arg(
long,
conflicts_with = "to_label",
required_unless_present = "to_label"
)]
to: Option<String>,
/// To account label (alternative to --to).
#[arg(long, conflicts_with = "to")]
to_label: Option<String>,
},
}
@ -28,7 +35,13 @@ impl WalletSubcommand for PinataProgramAgnosticSubcommand {
wallet_core: &mut WalletCore,
) -> Result<SubcommandReturnValue> {
let underlying_subcommand = match self {
Self::Claim { to } => {
Self::Claim { to, to_label } => {
let to = resolve_id_or_label(
to,
to_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let (to, to_addr_privacy) = parse_addr_with_privacy_prefix(&to)?;
match to_addr_privacy {

View File

@ -7,7 +7,10 @@ use crate::{
AccDecodeData::Decode,
WalletCore,
cli::{SubcommandReturnValue, WalletSubcommand},
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix},
helperfunctions::{
AccountPrivacyKind, parse_addr_with_privacy_prefix, resolve_account_label,
resolve_id_or_label,
},
program_facades::token::Token,
};
@ -17,11 +20,25 @@ pub enum TokenProgramAgnosticSubcommand {
/// Produce a new token.
New {
/// `definition_account_id` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
definition_account_id: String,
#[arg(
long,
conflicts_with = "definition_account_label",
required_unless_present = "definition_account_label"
)]
definition_account_id: Option<String>,
/// Definition account label (alternative to --definition-account-id).
#[arg(long, conflicts_with = "definition_account_id")]
definition_account_label: Option<String>,
/// `supply_account_id` - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
supply_account_id: String,
#[arg(
long,
conflicts_with = "supply_account_label",
required_unless_present = "supply_account_label"
)]
supply_account_id: Option<String>,
/// Supply account label (alternative to --supply-account-id).
#[arg(long, conflicts_with = "supply_account_id")]
supply_account_label: Option<String>,
#[arg(short, long)]
name: String,
#[arg(short, long)]
@ -35,11 +52,21 @@ pub enum TokenProgramAgnosticSubcommand {
/// First is used for owned accounts, second otherwise.
Send {
/// from - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
from: String,
#[arg(
long,
conflicts_with = "from_label",
required_unless_present = "from_label"
)]
from: Option<String>,
/// From account label (alternative to --from).
#[arg(long, conflicts_with = "from")]
from_label: Option<String>,
/// to - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
#[arg(long, conflicts_with = "to_label")]
to: Option<String>,
/// To account label (alternative to --to).
#[arg(long, conflicts_with = "to")]
to_label: Option<String>,
/// `to_npk` - valid 32 byte hex string.
#[arg(long)]
to_npk: Option<String>,
@ -58,11 +85,25 @@ pub enum TokenProgramAgnosticSubcommand {
/// we can not modify foreign accounts.
Burn {
/// definition - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
definition: String,
#[arg(
long,
conflicts_with = "definition_label",
required_unless_present = "definition_label"
)]
definition: Option<String>,
/// Definition account label (alternative to --definition).
#[arg(long, conflicts_with = "definition")]
definition_label: Option<String>,
/// holder - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
holder: String,
#[arg(
long,
conflicts_with = "holder_label",
required_unless_present = "holder_label"
)]
holder: Option<String>,
/// Holder account label (alternative to --holder).
#[arg(long, conflicts_with = "holder")]
holder_label: Option<String>,
/// amount - amount of balance to burn.
#[arg(long)]
amount: u128,
@ -77,11 +118,21 @@ pub enum TokenProgramAgnosticSubcommand {
/// First is used for owned accounts, second otherwise.
Mint {
/// definition - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
definition: String,
#[arg(
long,
conflicts_with = "definition_label",
required_unless_present = "definition_label"
)]
definition: Option<String>,
/// Definition account label (alternative to --definition).
#[arg(long, conflicts_with = "definition")]
definition_label: Option<String>,
/// holder - valid 32 byte base58 string with privacy prefix.
#[arg(long)]
#[arg(long, conflicts_with = "holder_label")]
holder: Option<String>,
/// Holder account label (alternative to --holder).
#[arg(long, conflicts_with = "holder")]
holder_label: Option<String>,
/// `holder_npk` - valid 32 byte hex string.
#[arg(long)]
holder_npk: Option<String>,
@ -102,10 +153,24 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
match self {
Self::New {
definition_account_id,
definition_account_label,
supply_account_id,
supply_account_label,
name,
total_supply,
} => {
let definition_account_id = resolve_id_or_label(
definition_account_id,
definition_account_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let supply_account_id = resolve_id_or_label(
supply_account_id,
supply_account_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let (definition_account_id, definition_addr_privacy) =
parse_addr_with_privacy_prefix(&definition_account_id)?;
let (supply_account_id, supply_addr_privacy) =
@ -158,11 +223,30 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
}
Self::Send {
from,
from_label,
to,
to_label,
to_npk,
to_vpk,
amount,
} => {
let from = resolve_id_or_label(
from,
from_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let to = match (to, to_label) {
(v, None) => v,
(None, Some(label)) => Some(resolve_account_label(
&label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?),
(Some(_), Some(_)) => {
anyhow::bail!("Provide only one of --to or --to-label")
}
};
let underlying_subcommand = match (to, to_npk, to_vpk) {
(None, None, None) => {
anyhow::bail!(
@ -248,9 +332,23 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
}
Self::Burn {
definition,
definition_label,
holder,
holder_label,
amount,
} => {
let definition = resolve_id_or_label(
definition,
definition_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let holder = resolve_id_or_label(
holder,
holder_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let underlying_subcommand = {
let (definition, definition_privacy) =
parse_addr_with_privacy_prefix(&definition)?;
@ -300,11 +398,30 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
}
Self::Mint {
definition,
definition_label,
holder,
holder_label,
holder_npk,
holder_vpk,
amount,
} => {
let definition = resolve_id_or_label(
definition,
definition_label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?;
let holder = match (holder, holder_label) {
(v, None) => v,
(None, Some(label)) => Some(resolve_account_label(
&label,
&wallet_core.storage.labels,
&wallet_core.storage.user_data,
)?),
(Some(_), Some(_)) => {
anyhow::bail!("Provide only one of --holder or --holder-label")
}
};
let underlying_subcommand = match (holder, holder_npk, holder_vpk) {
(None, None, None) => {
anyhow::bail!(

View File

@ -49,6 +49,66 @@ impl From<Account> for HumanReadableAccount {
}
}
/// Resolve an account id-or-label pair to a `Privacy/id` string.
///
/// Exactly one of `id` or `label` must be `Some`. If `id` is provided it is
/// returned as-is; if `label` is provided it is resolved via
/// [`resolve_account_label`]. Any other combination returns an error.
pub fn resolve_id_or_label(
id: Option<String>,
label: Option<String>,
labels: &HashMap<String, Label>,
user_data: &NSSAUserData,
) -> Result<String> {
match (id, label) {
(Some(id), None) => Ok(id),
(None, Some(label)) => resolve_account_label(&label, labels, user_data),
_ => anyhow::bail!("provide exactly one of account id or account label"),
}
}
/// Resolve an account label to its full `Privacy/id` string representation.
///
/// Looks up the label in the labels map and determines whether the account is
/// public or private by checking the user data key trees.
pub fn resolve_account_label(
label: &str,
labels: &HashMap<String, Label>,
user_data: &NSSAUserData,
) -> Result<String> {
let account_id_str = labels
.iter()
.find(|(_, l)| l.to_string() == label)
.map(|(k, _)| k.clone())
.ok_or_else(|| anyhow::anyhow!("No account found with label '{label}'"))?;
let account_id: nssa::AccountId = account_id_str.parse()?;
let privacy = if user_data
.public_key_tree
.account_id_map
.contains_key(&account_id)
|| user_data
.default_pub_account_signing_keys
.contains_key(&account_id)
{
"Public"
} else if user_data
.private_key_tree
.account_id_map
.contains_key(&account_id)
|| user_data
.default_user_private_accounts
.contains_key(&account_id)
{
"Private"
} else {
anyhow::bail!("Account with label '{label}' not found in wallet");
};
Ok(format!("{privacy}/{account_id_str}"))
}
/// Get home dir for wallet. Env var `NSSA_WALLET_HOME_DIR` must be set before execution to succeed.
fn get_home_nssa_var() -> Result<PathBuf> {
Ok(PathBuf::from_str(&std::env::var(HOME_DIR_ENV_VAR)?)?)