Skip to content

Commit

Permalink
Improve solana-tokens UX (solana-labs#12253)
Browse files Browse the repository at this point in the history
* Fix computed banks port

* Readme incorrect

* Return error if csv cannot be read

* Move column headers over columns

* Add dry-run check for sender/fee-payer balances

* Use clap requires method for paired args

* Write transaction-log anytime outfile is specified

* Replace campaign-name with required db-path

* Remove bids

* Exclude new_stake_account_address from logs for non-stake distributions

* Fix readme
  • Loading branch information
CriesofCarrots authored Sep 16, 2020
1 parent 3930cb8 commit 90a591d
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 195 deletions.
10 changes: 5 additions & 5 deletions cli-config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl Config {
}
let mut url = json_rpc_url.unwrap();
let port = url.port_or_known_default().unwrap_or(80);
url.set_port(Some(port + 2)).expect("unable to set port");
url.set_port(Some(port + 3)).expect("unable to set port");
url.to_string()
}

Expand Down Expand Up @@ -138,21 +138,21 @@ mod test {
fn compute_rpc_banks_url() {
assert_eq!(
Config::compute_rpc_banks_url(&"http://devnet.solana.com"),
"http://devnet.solana.com:82/".to_string()
"http://devnet.solana.com:83/".to_string()
);

assert_eq!(
Config::compute_rpc_banks_url(&"https://devnet.solana.com"),
"https://devnet.solana.com:445/".to_string()
"https://devnet.solana.com:446/".to_string()
);

assert_eq!(
Config::compute_rpc_banks_url(&"http://example.com:8899"),
"http://example.com:8901/".to_string()
"http://example.com:8902/".to_string()
);
assert_eq!(
Config::compute_rpc_banks_url(&"https://example.com:1234"),
"https://example.com:1236/".to_string()
"https://example.com:1237/".to_string()
);

assert_eq!(Config::compute_rpc_banks_url(&"garbage"), String::new());
Expand Down
50 changes: 23 additions & 27 deletions tokens/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,38 @@ expected amount are sent. The command-line tool here automates that process.

## Distribute tokens

Send tokens to the recipients in `<BIDS_CSV>`.
Send tokens to the recipients in `<RECIPIENTS_CSV>`.

Example bids.csv:
Example recipients.csv:

```text
primary_address,bid_amount_dollars
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv,6.6
recipient,amount,lockup_date
3ihfUy1n9gaqihM5bJCiTAGLgWc5zo3DqVUS6T736NLM,42.0,
CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT,43.0,
```

```bash
solana-tokens distribute-tokens --from <KEYPAIR> --dollars-per-sol <NUMBER> --from-bids --input-csv <BIDS_CSV> --fee-payer <KEYPAIR>
solana-tokens distribute-tokens --from <KEYPAIR> --input-csv <RECIPIENTS_CSV> --fee-payer <KEYPAIR>
```

Example transaction log before:

```text
recipient,amount,signature
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv,30,1111111111111111111111111111111111111111111111111111111111111111
recipient,amount,finalized_date,signature
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv,70.0,2020-09-15T23:29:26.879747Z,UB168XhBhecxzeD1w2ZRUhwTHpPSqv2WNh8NrZHqz1F2EqxxbSW6iFfVtsg3HkU9NX2cD7R92D8VRLSyArZ9xKQ
```

Send tokens to the recipients in `<BIDS_CSV>` if the distribution is
not already recordered in the transaction log.
Send tokens to the recipients in `<RECIPIENTS_CSV>` if the distribution is
not already recorded in the transaction log.

```bash
solana-tokens distribute-tokens --from <KEYPAIR> --dollars-per-sol <NUMBER> --from-bids --input-csv <BIDS_CSV> --fee-payer <KEYPAIR>
solana-tokens distribute-tokens --from <KEYPAIR> --input-csv <RECIPIENTS_CSV> --fee-payer <KEYPAIR>
```

Example output:

```text
Recipient Amount
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv 70
Recipient Expected Balance (◎)
3ihfUy1n9gaqihM5bJCiTAGLgWc5zo3DqVUS6T736NLM 42
UKUcTXgbeTYh65RaVV5gSf6xBHevqHvAXMo3e8Q6np8k 43
```
Expand All @@ -52,10 +52,9 @@ solana-tokens transaction-log --output-path transactions.csv

```text
recipient,amount,signature
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv,30,1111111111111111111111111111111111111111111111111111111111111111
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv,70,1111111111111111111111111111111111111111111111111111111111111111
3ihfUy1n9gaqihM5bJCiTAGLgWc5zo3DqVUS6T736NLM,42,1111111111111111111111111111111111111111111111111111111111111111
UKUcTXgbeTYh65RaVV5gSf6xBHevqHvAXMo3e8Q6np8k,43,1111111111111111111111111111111111111111111111111111111111111111
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv,70.0,2020-09-15T23:29:26.879747Z,UB168XhBhecxzeD1w2ZRUhwTHpPSqv2WNh8NrZHqz1F2EqxxbSW6iFfVtsg3HkU9NX2cD7R92D8VRLSyArZ9xKQ
3ihfUy1n9gaqihM5bJCiTAGLgWc5zo3DqVUS6T736NLM,42.0,2020-09-15T23:31:50.264241Z,53AVNEVpQBteJBRAKp6naxXsgESDjqe1ge9Dg2HeCSpYWTuGTLqHrBpkHTnpvPJURNgKWxkJfihuRa5STVRjL2hy
CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT,43.0,2020-09-15T23:33:53.680821Z,4XsMfLx9D2ZxVpdJ5xdkV2w4X4SKEQ5zbQhcH4NcRwgZDkdRNiZjvnMFaWaWHUh5eF1LwFPpQdjn6mzSsiCVj3L7
```

### Calculate what tokens should be sent
Expand All @@ -64,26 +63,23 @@ List the differences between a list of expected distributions and the record of
transactions have already been sent.

```bash
solana-tokens distribute-tokens --dollars-per-sol <NUMBER> --dry-run --from-bids --input-csv <BIDS_CSV>
solana-tokens distribute-tokens --dry-run --input-csv <RECIPIENTS_CSV>
```

Example bids.csv:
Example recipients.csv:

```text
primary_address,bid_amount_dollars
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv,6.6
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv,15.4
3ihfUy1n9gaqihM5bJCiTAGLgWc5zo3DqVUS6T736NLM,9.24
UKUcTXgbeTYh65RaVV5gSf6xBHevqHvAXMo3e8Q6np8k,9.46
recipient,amount,lockup_date
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv,80,
7aHDubg5FBYj1SgmyBgU3ZJdtfuqYCQsJQK2pTR5JUqr,42,
```

Example output:

```text
Recipient Amount
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv 70
3ihfUy1n9gaqihM5bJCiTAGLgWc5zo3DqVUS6T736NLM 42
UKUcTXgbeTYh65RaVV5gSf6xBHevqHvAXMo3e8Q6np8k 43
Recipient Expected Balance (◎)
6Vo87BaDhp4v4GHwVDhw5huhxVF8CyxSXYtkUwVHbbPv 10
7aHDubg5FBYj1SgmyBgU3ZJdtfuqYCQsJQK2pTR5JUqr 42
```

## Distribute stake accounts
Expand Down
105 changes: 44 additions & 61 deletions tokens/src/arg_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ where
SubCommand::with_name("distribute-tokens")
.about("Distribute tokens")
.arg(
Arg::with_name("campaign_name")
.long("campaign-name")
Arg::with_name("db_path")
.long("db-path")
.required(true)
.takes_value(true)
.value_name("NAME")
.help("Campaign name for storing transaction data"),
)
.arg(
Arg::with_name("from_bids")
.long("from-bids")
.help("Input CSV contains bids in dollars, not allocations in SOL"),
.value_name("FILE")
.help(
"Location for storing distribution database. \
The database is used for tracking transactions as they are finalized \
and preventing double spends.",
),
)
.arg(
Arg::with_name("input_csv")
Expand All @@ -60,18 +60,19 @@ where
.value_name("FILE")
.help("Input CSV file"),
)
.arg(
Arg::with_name("dollars_per_sol")
.long("dollars-per-sol")
.takes_value(true)
.value_name("NUMBER")
.help("Dollars per SOL, if input CSV contains bids"),
)
.arg(
Arg::with_name("dry_run")
.long("dry-run")
.help("Do not execute any transfers"),
)
.arg(
Arg::with_name("output_path")
.long("output-path")
.short("o")
.value_name("FILE")
.takes_value(true)
.help("Write the transaction log to this file"),
)
.arg(
Arg::with_name("sender_keypair")
.long("from")
Expand All @@ -95,11 +96,16 @@ where
SubCommand::with_name("distribute-stake")
.about("Distribute stake accounts")
.arg(
Arg::with_name("campaign_name")
.long("campaign-name")
Arg::with_name("db_path")
.long("db-path")
.required(true)
.takes_value(true)
.value_name("NAME")
.help("Campaign name for storing transaction data"),
.value_name("FILE")
.help(
"Location for storing distribution database. \
The database is used for tracking transactions as they are finalized \
and preventing double spends.",
),
)
.arg(
Arg::with_name("input_csv")
Expand All @@ -114,6 +120,14 @@ where
.long("dry-run")
.help("Do not execute any transfers"),
)
.arg(
Arg::with_name("output_path")
.long("output-path")
.short("o")
.value_name("FILE")
.takes_value(true)
.help("Write the transaction log to this file"),
)
.arg(
Arg::with_name("sender_keypair")
.long("from")
Expand Down Expand Up @@ -186,29 +200,18 @@ where
.takes_value(true)
.value_name("FILE")
.help("Bids CSV file"),
)
.arg(
Arg::with_name("from_bids")
.long("from-bids")
.help("Input CSV contains bids in dollars, not allocations in SOL"),
)
.arg(
Arg::with_name("dollars_per_sol")
.long("dollars-per-sol")
.takes_value(true)
.value_name("NUMBER")
.help("Dollars per SOL"),
),
)
.subcommand(
SubCommand::with_name("transaction-log")
.about("Print the database to a CSV file")
.arg(
Arg::with_name("campaign_name")
.long("campaign-name")
Arg::with_name("db_path")
.long("db-path")
.required(true)
.takes_value(true)
.value_name("NAME")
.help("Campaign name for storing transaction data"),
.value_name("FILE")
.help("Location of database to query"),
)
.arg(
Arg::with_name("output_path")
Expand All @@ -222,22 +225,6 @@ where
.get_matches_from(args)
}

fn create_db_path(campaign_name: Option<String>) -> String {
let (prefix, hyphen) = if let Some(name) = campaign_name {
(name, "-")
} else {
("".to_string(), "")
};
let path = dirs::home_dir().unwrap();
let filename = format!("{}{}transactions.db", prefix, hyphen);
path.join(".config")
.join("solana-tokens")
.join(filename)
.to_str()
.unwrap()
.to_string()
}

fn parse_distribute_tokens_args(
matches: &ArgMatches<'_>,
) -> Result<DistributeTokensArgs, Box<dyn Error>> {
Expand All @@ -262,9 +249,8 @@ fn parse_distribute_tokens_args(

Ok(DistributeTokensArgs {
input_csv: value_t_or_exit!(matches, "input_csv", String),
from_bids: matches.is_present("from_bids"),
transaction_db: create_db_path(value_t!(matches, "campaign_name", String).ok()),
dollars_per_sol: value_t!(matches, "dollars_per_sol", f64).ok(),
transaction_db: value_t_or_exit!(matches, "db_path", String),
output_path: matches.value_of("output_path").map(|path| path.to_string()),
dry_run: matches.is_present("dry_run"),
sender_keypair,
fee_payer,
Expand Down Expand Up @@ -338,9 +324,8 @@ fn parse_distribute_stake_args(
};
Ok(DistributeTokensArgs {
input_csv: value_t_or_exit!(matches, "input_csv", String),
from_bids: false,
transaction_db: create_db_path(value_t!(matches, "campaign_name", String).ok()),
dollars_per_sol: None,
transaction_db: value_t_or_exit!(matches, "db_path", String),
output_path: matches.value_of("output_path").map(|path| path.to_string()),
dry_run: matches.is_present("dry_run"),
sender_keypair,
fee_payer,
Expand All @@ -351,14 +336,12 @@ fn parse_distribute_stake_args(
fn parse_balances_args(matches: &ArgMatches<'_>) -> BalancesArgs {
BalancesArgs {
input_csv: value_t_or_exit!(matches, "input_csv", String),
from_bids: matches.is_present("from_bids"),
dollars_per_sol: value_t!(matches, "dollars_per_sol", f64).ok(),
}
}

fn parse_transaction_log_args(matches: &ArgMatches<'_>) -> TransactionLogArgs {
TransactionLogArgs {
transaction_db: create_db_path(value_t!(matches, "campaign_name", String).ok()),
transaction_db: value_t_or_exit!(matches, "db_path", String),
output_path: value_t_or_exit!(matches, "output_path", String),
}
}
Expand Down
5 changes: 1 addition & 4 deletions tokens/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ use solana_sdk::{pubkey::Pubkey, signature::Signer};

pub struct DistributeTokensArgs {
pub input_csv: String,
pub from_bids: bool,
pub transaction_db: String,
pub dollars_per_sol: Option<f64>,
pub output_path: Option<String>,
pub dry_run: bool,
pub sender_keypair: Box<dyn Signer>,
pub fee_payer: Box<dyn Signer>,
Expand All @@ -21,8 +20,6 @@ pub struct StakeArgs {

pub struct BalancesArgs {
pub input_csv: String,
pub from_bids: bool,
pub dollars_per_sol: Option<f64>,
}

pub struct TransactionLogArgs {
Expand Down
Loading

0 comments on commit 90a591d

Please sign in to comment.