瀏覽代碼

issue #10831: added --with-memo option to all cli commands that submit (#16291)

* issue #10831: added --with-memo option to all cli commands that submit
transactions.  Also, improve the block command to show UTF-8 string instead
of integer values for memo program data.

* Fixed tests and changed some syntax according to feedback.

* Use spl_memo id (all versions where applicable) instead of hardcoding id.

* Update Cargo.toml in programs/bpf.

* Update formatting via cargo fmt.

* Update to use spl_memo version 3.0.1, which simplifies package imports
bji 4 年之前
父節點
當前提交
364af3a3e0

+ 27 - 23
Cargo.lock

@@ -4188,6 +4188,7 @@ dependencies = [
  "solana-version",
  "solana-vote-program",
  "solana_rbpf",
+ "spl-memo 3.0.1",
  "tempfile",
  "thiserror",
  "tiny-bip39 0.7.3",
@@ -4226,6 +4227,7 @@ dependencies = [
  "solana-stake-program",
  "solana-transaction-status",
  "solana-vote-program",
+ "spl-memo 3.0.1",
 ]
 
 [[package]]
@@ -4460,9 +4462,9 @@ dependencies = [
 
 [[package]]
 name = "solana-frozen-abi"
-version = "1.5.8"
+version = "1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc44c8096d5847d8cf7f3af3cce565de554cb56371decf93b060633ca8588913"
+checksum = "f81ac3925c8995d935d3af27390cf7b748a81837a29feb75a00e115f40dae6dc"
 dependencies = [
  "bs58",
  "bv",
@@ -4473,8 +4475,8 @@ dependencies = [
  "serde",
  "serde_derive",
  "sha2 0.9.2",
- "solana-frozen-abi-macro 1.5.8",
- "solana-logger 1.5.8",
+ "solana-frozen-abi-macro 1.6.4",
+ "solana-logger 1.6.4",
  "thiserror",
 ]
 
@@ -4498,9 +4500,9 @@ dependencies = [
 
 [[package]]
 name = "solana-frozen-abi-macro"
-version = "1.5.8"
+version = "1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f905159beff1b53e4ba8b018a9d13d96ba164c3973bf3b9d587e730bcc14fc18"
+checksum = "ae448159038e470f3b2dd1ab0d219246211f940a10bf1e656a02cb5c2d897437"
 dependencies = [
  "lazy_static",
  "proc-macro2 1.0.24",
@@ -4744,9 +4746,9 @@ dependencies = [
 
 [[package]]
 name = "solana-logger"
-version = "1.5.8"
+version = "1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d83a006d97da5514a4475141573383d3fcd71c729ff78494f96bb531cf734d21"
+checksum = "7fa7bddd7b89c26c6e3ef4af9b47d6bc8d60888559affb5160f5ade18c0cd058"
 dependencies = [
  "env_logger 0.8.3",
  "lazy_static",
@@ -4912,11 +4914,13 @@ dependencies = [
 
 [[package]]
 name = "solana-program"
-version = "1.5.8"
+version = "1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2007bf285617f8a783e96b20ccd2f8d813549090a5b520f22e5f65b3cd9b157"
+checksum = "231bf4cd42997b6f34e7c74a1449e8a1e8816fd83662fc3a5a0922da48fc667b"
 dependencies = [
  "bincode",
+ "borsh",
+ "borsh-derive",
  "bs58",
  "bv",
  "curve25519-dalek 2.1.0",
@@ -4933,10 +4937,10 @@ dependencies = [
  "serde_bytes",
  "serde_derive",
  "sha2 0.9.2",
- "solana-frozen-abi 1.5.8",
- "solana-frozen-abi-macro 1.5.8",
- "solana-logger 1.5.8",
- "solana-sdk-macro 1.5.8",
+ "solana-frozen-abi 1.6.4",
+ "solana-frozen-abi-macro 1.6.4",
+ "solana-logger 1.6.4",
+ "solana-sdk-macro 1.6.4",
  "thiserror",
 ]
 
@@ -5147,9 +5151,9 @@ dependencies = [
 
 [[package]]
 name = "solana-sdk-macro"
-version = "1.5.8"
+version = "1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8a6635f1798c8ff1b88bb0fad1d4693c011f36a73c9ae03f198203144edcabb"
+checksum = "3c805f1e32677f8ec0cc0b2f470833a0d5ab0922f068e52be3a4a93b468c9c70"
 dependencies = [
  "bs58",
  "proc-macro2 1.0.24",
@@ -5379,7 +5383,7 @@ dependencies = [
  "solana-vote-program",
  "spl-associated-token-account",
  "spl-memo 2.0.1",
- "spl-memo 3.0.0",
+ "spl-memo 3.0.1",
  "spl-token",
  "thiserror",
 ]
@@ -5530,7 +5534,7 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4adc47eebe5d2b662cbaaba1843719c28a67e5ec5d0460bc3ca60900a51f74e2"
 dependencies = [
- "solana-program 1.5.8",
+ "solana-program 1.6.4",
  "spl-token",
 ]
 
@@ -5540,16 +5544,16 @@ version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fb2b771f6146dec14ef5fbf498f9374652c54badc3befc8c40c1d426dd45d720"
 dependencies = [
- "solana-program 1.5.8",
+ "solana-program 1.6.4",
 ]
 
 [[package]]
 name = "spl-memo"
-version = "3.0.0"
+version = "3.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e76b60c6f58279b5469beb1705744e9778ee94d643c8e3e2ff91874c59bb3c63"
+checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325"
 dependencies = [
- "solana-program 1.5.8",
+ "solana-program 1.6.4",
 ]
 
 [[package]]
@@ -5562,7 +5566,7 @@ dependencies = [
  "num-derive",
  "num-traits",
  "num_enum",
- "solana-program 1.5.8",
+ "solana-program 1.6.4",
  "thiserror",
 ]
 

+ 1 - 0
clap-utils/src/lib.rs

@@ -27,5 +27,6 @@ pub mod fee_payer;
 pub mod input_parsers;
 pub mod input_validators;
 pub mod keypair;
+pub mod memo;
 pub mod nonce;
 pub mod offline;

+ 16 - 0
clap-utils/src/memo.rs

@@ -0,0 +1,16 @@
+use crate::ArgConstant;
+use clap::Arg;
+
+pub const MEMO_ARG: ArgConstant<'static> = ArgConstant {
+    name: "memo",
+    long: "--with-memo",
+    help: "Specify a memo string to include in the transaction.",
+};
+
+pub fn memo_arg<'a, 'b>() -> Arg<'a, 'b> {
+    Arg::with_name(MEMO_ARG.name)
+        .long(MEMO_ARG.long)
+        .takes_value(true)
+        .value_name("MEMO")
+        .help(MEMO_ARG.help)
+}

+ 1 - 0
cli-output/Cargo.toml

@@ -26,6 +26,7 @@ solana-sdk = { path = "../sdk", version = "=1.7.0" }
 solana-stake-program = { path = "../programs/stake", version = "=1.7.0" }
 solana-transaction-status = { path = "../transaction-status", version = "=1.7.0" }
 solana-vote-program = { path = "../programs/vote", version = "=1.7.0" }
+spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
 
 [package.metadata.docs.rs]
 targets = ["x86_64-unknown-linux-gnu"]

+ 13 - 1
cli-output/src/display.rs

@@ -5,9 +5,11 @@ use {
     indicatif::{ProgressBar, ProgressStyle},
     solana_sdk::{
         clock::UnixTimestamp, hash::Hash, message::Message, native_token::lamports_to_sol,
-        program_utils::limited_deserialize, transaction::Transaction,
+        program_utils::limited_deserialize, pubkey::Pubkey, transaction::Transaction,
     },
     solana_transaction_status::UiTransactionStatusMeta,
+    spl_memo::id as spl_memo_id,
+    spl_memo::v1::id as spl_memo_v1_id,
     std::{collections::HashMap, fmt, io},
 };
 
@@ -28,6 +30,11 @@ impl Default for BuildBalanceMessageConfig {
     }
 }
 
+fn is_memo_program(k: &Pubkey) -> bool {
+    let k_str = k.to_string();
+    (k_str == spl_memo_v1_id().to_string()) || (k_str == spl_memo_id().to_string())
+}
+
 pub fn build_balance_message_with_config(
     lamports: u64,
     config: &BuildBalanceMessageConfig,
@@ -253,6 +260,11 @@ pub fn write_transaction<W: io::Write>(
                 writeln!(w, "{}  {:?}", prefix, system_instruction)?;
                 raw = false;
             }
+        } else if is_memo_program(&program_pubkey) {
+            if let Ok(s) = std::str::from_utf8(&instruction.data) {
+                writeln!(w, "{}  Data: \"{}\"", prefix, s)?;
+                raw = false;
+            }
         }
 
         if raw {

+ 1 - 0
cli/Cargo.toml

@@ -45,6 +45,7 @@ solana-stake-program = { path = "../programs/stake", version = "=1.7.0" }
 solana-transaction-status = { path = "../transaction-status", version = "=1.7.0" }
 solana-version = { path = "../version", version = "=1.7.0" }
 solana-vote-program = { path = "../programs/vote", version = "=1.7.0" }
+spl-memo = { version = "=3.0.1", features = ["no-entrypoint"] }
 thiserror = "1.0.21"
 tiny-bip39 = "0.7.0"
 url = "2.1.1"

+ 87 - 4
cli/src/cli.rs

@@ -1,6 +1,6 @@
 use crate::{
-    cluster_query::*, feature::*, inflation::*, nonce::*, program::*, spend_utils::*, stake::*,
-    validator_info::*, vote::*,
+    cluster_query::*, feature::*, inflation::*, memo::*, nonce::*, program::*, spend_utils::*,
+    stake::*, validator_info::*, vote::*,
 };
 use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
 use log::*;
@@ -13,6 +13,7 @@ use solana_clap_utils::{
     input_parsers::*,
     input_validators::*,
     keypair::*,
+    memo::{memo_arg, MEMO_ARG},
     nonce::*,
     offline::*,
 };
@@ -161,18 +162,21 @@ pub enum CliCommand {
     AuthorizeNonceAccount {
         nonce_account: Pubkey,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
         new_authority: Pubkey,
     },
     CreateNonceAccount {
         nonce_account: SignerIndex,
         seed: Option<String>,
         nonce_authority: Option<Pubkey>,
+        memo: Option<String>,
         amount: SpendAmount,
     },
     GetNonce(Pubkey),
     NewNonce {
         nonce_account: Pubkey,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
     },
     ShowNonceAccount {
         nonce_account_pubkey: Pubkey,
@@ -181,6 +185,7 @@ pub enum CliCommand {
     WithdrawFromNonceAccount {
         nonce_account: Pubkey,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
         destination_account_pubkey: Pubkey,
         lamports: u64,
     },
@@ -205,6 +210,7 @@ pub enum CliCommand {
         blockhash_query: BlockhashQuery,
         nonce_account: Option<Pubkey>,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
         fee_payer: SignerIndex,
         from: SignerIndex,
     },
@@ -216,6 +222,7 @@ pub enum CliCommand {
         blockhash_query: BlockhashQuery,
         nonce_account: Option<Pubkey>,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
         fee_payer: SignerIndex,
     },
     DelegateStake {
@@ -228,6 +235,7 @@ pub enum CliCommand {
         blockhash_query: BlockhashQuery,
         nonce_account: Option<Pubkey>,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
         fee_payer: SignerIndex,
     },
     SplitStake {
@@ -238,6 +246,7 @@ pub enum CliCommand {
         blockhash_query: BlockhashQuery,
         nonce_account: Option<Pubkey>,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
         split_stake_account: SignerIndex,
         seed: Option<String>,
         lamports: u64,
@@ -252,6 +261,7 @@ pub enum CliCommand {
         blockhash_query: BlockhashQuery,
         nonce_account: Option<Pubkey>,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
         fee_payer: SignerIndex,
     },
     ShowStakeHistory {
@@ -269,6 +279,7 @@ pub enum CliCommand {
         blockhash_query: BlockhashQuery,
         nonce_account: Option<Pubkey>,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
         fee_payer: SignerIndex,
         custodian: Option<SignerIndex>,
     },
@@ -281,6 +292,7 @@ pub enum CliCommand {
         blockhash_query: BlockhashQuery,
         nonce_account: Option<Pubkey>,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
         fee_payer: SignerIndex,
     },
     WithdrawStake {
@@ -294,6 +306,7 @@ pub enum CliCommand {
         blockhash_query: BlockhashQuery,
         nonce_account: Option<Pubkey>,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
         fee_payer: SignerIndex,
     },
     // Validator Info Commands
@@ -311,6 +324,7 @@ pub enum CliCommand {
         authorized_voter: Option<Pubkey>,
         authorized_withdrawer: Option<Pubkey>,
         commission: u8,
+        memo: Option<String>,
     },
     ShowVoteAccount {
         pubkey: Pubkey,
@@ -321,21 +335,25 @@ pub enum CliCommand {
         destination_account_pubkey: Pubkey,
         withdraw_authority: SignerIndex,
         withdraw_amount: SpendAmount,
+        memo: Option<String>,
     },
     VoteAuthorize {
         vote_account_pubkey: Pubkey,
         new_authorized_pubkey: Pubkey,
         vote_authorize: VoteAuthorize,
+        memo: Option<String>,
     },
     VoteUpdateValidator {
         vote_account_pubkey: Pubkey,
         new_identity_account: SignerIndex,
         withdraw_authority: SignerIndex,
+        memo: Option<String>,
     },
     VoteUpdateCommission {
         vote_account_pubkey: Pubkey,
         commission: u8,
         withdraw_authority: SignerIndex,
+        memo: Option<String>,
     },
     // Wallet Commands
     Address,
@@ -368,6 +386,7 @@ pub enum CliCommand {
         blockhash_query: BlockhashQuery,
         nonce_account: Option<Pubkey>,
         nonce_authority: SignerIndex,
+        memo: Option<String>,
         fee_payer: SignerIndex,
         derived_address_seed: Option<String>,
         derived_address_program_id: Option<Pubkey>,
@@ -866,6 +885,7 @@ pub fn parse_command(
             let nonce_account = pubkey_of_signer(matches, NONCE_ARG.name, wallet_manager)?;
             let (nonce_authority, nonce_authority_pubkey) =
                 signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
+            let memo = matches.value_of(MEMO_ARG.name).map(String::from);
             let (fee_payer, fee_payer_pubkey) =
                 signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
             let (from, from_pubkey) = signer_of(matches, "from", wallet_manager)?;
@@ -896,6 +916,7 @@ pub fn parse_command(
                     blockhash_query,
                     nonce_account,
                     nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+                    memo,
                     fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
                     from: signer_info.index_of(from_pubkey).unwrap(),
                     derived_address_seed,
@@ -1154,6 +1175,7 @@ fn process_transfer(
     blockhash_query: &BlockhashQuery,
     nonce_account: Option<&Pubkey>,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
     fee_payer: SignerIndex,
     derived_address_seed: Option<String>,
     derived_address_program_id: Option<&Pubkey>,
@@ -1201,8 +1223,9 @@ fn process_transfer(
                 to,
                 lamports,
             )]
+            .with_memo(memo)
         } else {
-            vec![system_instruction::transfer(&from_pubkey, to, lamports)]
+            vec![system_instruction::transfer(&from_pubkey, to, lamports)].with_memo(memo)
         };
 
         if let Some(nonce_account) = &nonce_account {
@@ -1402,12 +1425,14 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
         CliCommand::AuthorizeNonceAccount {
             nonce_account,
             nonce_authority,
+            memo,
             new_authority,
         } => process_authorize_nonce_account(
             &rpc_client,
             config,
             nonce_account,
             *nonce_authority,
+            memo.as_ref(),
             new_authority,
         ),
         // Create nonce account
@@ -1415,6 +1440,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             nonce_account,
             seed,
             nonce_authority,
+            memo,
             amount,
         } => process_create_nonce_account(
             &rpc_client,
@@ -1422,6 +1448,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             *nonce_account,
             seed.clone(),
             *nonce_authority,
+            memo.as_ref(),
             *amount,
         ),
         // Get the current nonce
@@ -1432,7 +1459,14 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
         CliCommand::NewNonce {
             nonce_account,
             nonce_authority,
-        } => process_new_nonce(&rpc_client, config, nonce_account, *nonce_authority),
+            memo,
+        } => process_new_nonce(
+            &rpc_client,
+            config,
+            nonce_account,
+            *nonce_authority,
+            memo.as_ref(),
+        ),
         // Show the contents of a nonce account
         CliCommand::ShowNonceAccount {
             nonce_account_pubkey,
@@ -1447,6 +1481,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
         CliCommand::WithdrawFromNonceAccount {
             nonce_account,
             nonce_authority,
+            memo,
             destination_account_pubkey,
             lamports,
         } => process_withdraw_from_nonce_account(
@@ -1454,6 +1489,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             config,
             &nonce_account,
             *nonce_authority,
+            memo.as_ref(),
             &destination_account_pubkey,
             *lamports,
         ),
@@ -1493,6 +1529,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             ref nonce_account,
             nonce_authority,
+            memo,
             fee_payer,
             from,
         } => process_create_stake_account(
@@ -1509,6 +1546,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             nonce_account.as_ref(),
             *nonce_authority,
+            memo.as_ref(),
             *fee_payer,
             *from,
         ),
@@ -1520,6 +1558,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             nonce_account,
             nonce_authority,
+            memo,
             fee_payer,
         } => process_deactivate_stake_account(
             &rpc_client,
@@ -1531,6 +1570,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             *nonce_account,
             *nonce_authority,
+            memo.as_ref(),
             *fee_payer,
         ),
         CliCommand::DelegateStake {
@@ -1543,6 +1583,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             nonce_account,
             nonce_authority,
+            memo,
             fee_payer,
         } => process_delegate_stake(
             &rpc_client,
@@ -1556,6 +1597,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             *nonce_account,
             *nonce_authority,
+            memo.as_ref(),
             *fee_payer,
         ),
         CliCommand::SplitStake {
@@ -1566,6 +1608,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             nonce_account,
             nonce_authority,
+            memo,
             split_stake_account,
             seed,
             lamports,
@@ -1580,6 +1623,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             *nonce_account,
             *nonce_authority,
+            memo.as_ref(),
             *split_stake_account,
             seed,
             *lamports,
@@ -1594,6 +1638,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             nonce_account,
             nonce_authority,
+            memo,
             fee_payer,
         } => process_merge_stake(
             &rpc_client,
@@ -1606,6 +1651,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             *nonce_account,
             *nonce_authority,
+            memo.as_ref(),
             *fee_payer,
         ),
         CliCommand::ShowStakeAccount {
@@ -1628,6 +1674,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             nonce_account,
             nonce_authority,
+            memo,
             fee_payer,
             custodian,
         } => process_stake_authorize(
@@ -1641,6 +1688,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             *nonce_account,
             *nonce_authority,
+            memo.as_ref(),
             *fee_payer,
         ),
         CliCommand::StakeSetLockup {
@@ -1652,6 +1700,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             nonce_account,
             nonce_authority,
+            memo,
             fee_payer,
         } => process_stake_set_lockup(
             &rpc_client,
@@ -1664,6 +1713,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             *nonce_account,
             *nonce_authority,
+            memo.as_ref(),
             *fee_payer,
         ),
         CliCommand::WithdrawStake {
@@ -1677,6 +1727,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             ref nonce_account,
             nonce_authority,
+            memo,
             fee_payer,
         } => process_withdraw_stake(
             &rpc_client,
@@ -1691,6 +1742,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             nonce_account.as_ref(),
             *nonce_authority,
+            memo.as_ref(),
             *fee_payer,
         ),
 
@@ -1723,6 +1775,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             authorized_voter,
             authorized_withdrawer,
             commission,
+            memo,
         } => process_create_vote_account(
             &rpc_client,
             config,
@@ -1732,6 +1785,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             authorized_voter,
             authorized_withdrawer,
             *commission,
+            memo.as_ref(),
         ),
         CliCommand::ShowVoteAccount {
             pubkey: vote_account_pubkey,
@@ -1747,6 +1801,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             withdraw_authority,
             withdraw_amount,
             destination_account_pubkey,
+            memo,
         } => process_withdraw_from_vote_account(
             &rpc_client,
             config,
@@ -1754,39 +1809,46 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             *withdraw_authority,
             *withdraw_amount,
             destination_account_pubkey,
+            memo.as_ref(),
         ),
         CliCommand::VoteAuthorize {
             vote_account_pubkey,
             new_authorized_pubkey,
             vote_authorize,
+            memo,
         } => process_vote_authorize(
             &rpc_client,
             config,
             &vote_account_pubkey,
             &new_authorized_pubkey,
             *vote_authorize,
+            memo.as_ref(),
         ),
         CliCommand::VoteUpdateValidator {
             vote_account_pubkey,
             new_identity_account,
             withdraw_authority,
+            memo,
         } => process_vote_update_validator(
             &rpc_client,
             config,
             &vote_account_pubkey,
             *new_identity_account,
             *withdraw_authority,
+            memo.as_ref(),
         ),
         CliCommand::VoteUpdateCommission {
             vote_account_pubkey,
             commission,
             withdraw_authority,
+            memo,
         } => process_vote_update_commission(
             &rpc_client,
             config,
             &vote_account_pubkey,
             *commission,
             *withdraw_authority,
+            memo.as_ref(),
         ),
 
         // Wallet Commands
@@ -1853,6 +1915,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             ref blockhash_query,
             ref nonce_account,
             nonce_authority,
+            memo,
             fee_payer,
             derived_address_seed,
             ref derived_address_program_id,
@@ -1869,6 +1932,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
             blockhash_query,
             nonce_account.as_ref(),
             *nonce_authority,
+            memo.as_ref(),
             *fee_payer,
             derived_address_seed.clone(),
             derived_address_program_id.as_ref(),
@@ -2241,6 +2305,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
                 )
                 .offline_args()
                 .nonce_args(false)
+		.arg(memo_arg())
                 .arg(fee_payer_arg()),
         )
         .subcommand(
@@ -2642,6 +2707,7 @@ mod tests {
             authorized_voter: Some(bob_pubkey),
             authorized_withdrawer: Some(bob_pubkey),
             commission: 0,
+            memo: None,
         };
         config.signers = vec![&keypair, &bob_keypair, &identity_keypair];
         let result = process_command(&config);
@@ -2653,6 +2719,7 @@ mod tests {
             vote_account_pubkey: bob_pubkey,
             new_authorized_pubkey,
             vote_authorize: VoteAuthorize::Voter,
+            memo: None,
         };
         let result = process_command(&config);
         assert!(result.is_ok());
@@ -2663,6 +2730,7 @@ mod tests {
             vote_account_pubkey: bob_pubkey,
             new_identity_account: 2,
             withdraw_authority: 1,
+            memo: None,
         };
         let result = process_command(&config);
         assert!(result.is_ok());
@@ -2686,6 +2754,7 @@ mod tests {
             blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
             nonce_account: None,
             nonce_authority: 0,
+            memo: None,
             fee_payer: 0,
             from: 0,
         };
@@ -2706,6 +2775,7 @@ mod tests {
             blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
             nonce_account: None,
             nonce_authority: 0,
+            memo: None,
             fee_payer: 0,
         };
         config.signers = vec![&keypair];
@@ -2721,6 +2791,7 @@ mod tests {
             blockhash_query: BlockhashQuery::default(),
             nonce_account: None,
             nonce_authority: 0,
+            memo: None,
             fee_payer: 0,
         };
         let result = process_command(&config);
@@ -2736,6 +2807,7 @@ mod tests {
             blockhash_query: BlockhashQuery::default(),
             nonce_account: None,
             nonce_authority: 0,
+            memo: None,
             split_stake_account: 1,
             seed: None,
             lamports: 30,
@@ -2757,6 +2829,7 @@ mod tests {
             blockhash_query: BlockhashQuery::default(),
             nonce_account: None,
             nonce_authority: 0,
+            memo: None,
             fee_payer: 0,
         };
         config.signers = vec![&keypair, &merge_stake_account];
@@ -2834,6 +2907,7 @@ mod tests {
             authorized_voter: Some(bob_pubkey),
             authorized_withdrawer: Some(bob_pubkey),
             commission: 0,
+            memo: None,
         };
         config.signers = vec![&keypair, &bob_keypair, &identity_keypair];
         assert!(process_command(&config).is_err());
@@ -2842,6 +2916,7 @@ mod tests {
             vote_account_pubkey: bob_pubkey,
             new_authorized_pubkey: bob_pubkey,
             vote_authorize: VoteAuthorize::Voter,
+            memo: None,
         };
         assert!(process_command(&config).is_err());
 
@@ -2849,6 +2924,7 @@ mod tests {
             vote_account_pubkey: bob_pubkey,
             new_identity_account: 1,
             withdraw_authority: 1,
+            memo: None,
         };
         assert!(process_command(&config).is_err());
 
@@ -2947,6 +3023,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     derived_address_seed: None,
                     derived_address_program_id: None,
@@ -2973,6 +3050,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     derived_address_seed: None,
                     derived_address_program_id: None,
@@ -3004,6 +3082,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     derived_address_seed: None,
                     derived_address_program_id: None,
@@ -3038,6 +3117,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::None(blockhash),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     derived_address_seed: None,
                     derived_address_program_id: None,
@@ -3080,6 +3160,7 @@ mod tests {
                     ),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     derived_address_seed: None,
                     derived_address_program_id: None,
@@ -3123,6 +3204,7 @@ mod tests {
                     ),
                     nonce_account: Some(nonce_address),
                     nonce_authority: 1,
+                    memo: None,
                     fee_payer: 0,
                     derived_address_seed: None,
                     derived_address_program_id: None,
@@ -3161,6 +3243,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     derived_address_seed: Some(derived_address_seed),
                     derived_address_program_id: Some(solana_stake_program::id()),

+ 1 - 0
cli/src/lib.rs

@@ -26,6 +26,7 @@ pub mod cli;
 pub mod cluster_query;
 pub mod feature;
 pub mod inflation;
+pub mod memo;
 pub mod nonce;
 pub mod program;
 pub mod send_tpu;

+ 22 - 0
cli/src/memo.rs

@@ -0,0 +1,22 @@
+use solana_sdk::instruction::Instruction;
+use solana_sdk::pubkey::Pubkey;
+use spl_memo::id;
+
+pub trait WithMemo {
+    fn with_memo<T: AsRef<str>>(self, memo: Option<T>) -> Self;
+}
+
+impl WithMemo for Vec<Instruction> {
+    fn with_memo<T: AsRef<str>>(mut self, memo: Option<T>) -> Self {
+        if let Some(memo) = &memo {
+            let memo = memo.as_ref();
+            let memo_ix = Instruction {
+                program_id: Pubkey::new(&id().to_bytes()),
+                accounts: vec![],
+                data: memo.as_bytes().to_vec(),
+            };
+            self.push(memo_ix);
+        }
+        self
+    }
+}

+ 41 - 7
cli/src/nonce.rs

@@ -4,6 +4,7 @@ use crate::{
         log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
         ProcessResult,
     },
+    memo::WithMemo,
     spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
 };
 use clap::{App, Arg, ArgMatches, SubCommand};
@@ -11,6 +12,7 @@ use solana_clap_utils::{
     input_parsers::*,
     input_validators::*,
     keypair::{DefaultSigner, SignerIndex},
+    memo::MEMO_ARG,
     nonce::*,
 };
 use solana_cli_output::CliNonceAccount;
@@ -171,6 +173,7 @@ pub fn parse_authorize_nonce_account(
 ) -> Result<CliCommandInfo, CliError> {
     let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
     let new_authority = pubkey_of_signer(matches, "new_authority", wallet_manager)?.unwrap();
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
     let (nonce_authority, nonce_authority_pubkey) =
         signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
 
@@ -185,6 +188,7 @@ pub fn parse_authorize_nonce_account(
         command: CliCommand::AuthorizeNonceAccount {
             nonce_account,
             nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+            memo,
             new_authority,
         },
         signers: signer_info.signers,
@@ -201,6 +205,7 @@ pub fn parse_nonce_create_account(
     let seed = matches.value_of("seed").map(|s| s.to_string());
     let amount = SpendAmount::new_from_matches(matches, "amount");
     let nonce_authority = pubkey_of_signer(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
 
     let payer_provided = None;
     let signer_info = default_signer.generate_unique_signers(
@@ -214,6 +219,7 @@ pub fn parse_nonce_create_account(
             nonce_account: signer_info.index_of(nonce_account_pubkey).unwrap(),
             seed,
             nonce_authority,
+            memo,
             amount,
         },
         signers: signer_info.signers,
@@ -239,6 +245,7 @@ pub fn parse_new_nonce(
     wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
 ) -> Result<CliCommandInfo, CliError> {
     let nonce_account = pubkey_of_signer(matches, "nonce_account_pubkey", wallet_manager)?.unwrap();
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
     let (nonce_authority, nonce_authority_pubkey) =
         signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
 
@@ -253,6 +260,7 @@ pub fn parse_new_nonce(
         command: CliCommand::NewNonce {
             nonce_account,
             nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+            memo,
         },
         signers: signer_info.signers,
     })
@@ -284,6 +292,7 @@ pub fn parse_withdraw_from_nonce_account(
     let destination_account_pubkey =
         pubkey_of_signer(matches, "destination_account_pubkey", wallet_manager)?.unwrap();
     let lamports = lamports_of_sol(matches, "amount").unwrap();
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
     let (nonce_authority, nonce_authority_pubkey) =
         signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
 
@@ -298,6 +307,7 @@ pub fn parse_withdraw_from_nonce_account(
         command: CliCommand::WithdrawFromNonceAccount {
             nonce_account,
             nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+            memo,
             destination_account_pubkey,
             lamports,
         },
@@ -330,13 +340,19 @@ pub fn process_authorize_nonce_account(
     config: &CliConfig,
     nonce_account: &Pubkey,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
     new_authority: &Pubkey,
 ) -> ProcessResult {
     let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
 
     let nonce_authority = config.signers[nonce_authority];
-    let ix = authorize_nonce_account(nonce_account, &nonce_authority.pubkey(), new_authority);
-    let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
+    let ixs = vec![authorize_nonce_account(
+        nonce_account,
+        &nonce_authority.pubkey(),
+        new_authority,
+    )]
+    .with_memo(memo);
+    let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
     let mut tx = Transaction::new_unsigned(message);
     tx.try_sign(&config.signers, recent_blockhash)?;
 
@@ -357,6 +373,7 @@ pub fn process_create_nonce_account(
     nonce_account: SignerIndex,
     seed: Option<String>,
     nonce_authority: Option<Pubkey>,
+    memo: Option<&String>,
     amount: SpendAmount,
 ) -> ProcessResult {
     let nonce_account_pubkey = config.signers[nonce_account].pubkey();
@@ -383,6 +400,7 @@ pub fn process_create_nonce_account(
                 &nonce_authority,
                 lamports,
             )
+            .with_memo(memo)
         } else {
             create_nonce_account(
                 &config.signers[0].pubkey(),
@@ -390,6 +408,7 @@ pub fn process_create_nonce_account(
                 &nonce_authority,
                 lamports,
             )
+            .with_memo(memo)
         };
         Message::new(&ixs, Some(&config.signers[0].pubkey()))
     };
@@ -451,6 +470,7 @@ pub fn process_new_nonce(
     config: &CliConfig,
     nonce_account: &Pubkey,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
 ) -> ProcessResult {
     check_unique_pubkeys(
         (&config.signers[0].pubkey(), "cli keypair".to_string()),
@@ -466,9 +486,13 @@ pub fn process_new_nonce(
     }
 
     let nonce_authority = config.signers[nonce_authority];
-    let ix = advance_nonce_account(&nonce_account, &nonce_authority.pubkey());
+    let ixs = vec![advance_nonce_account(
+        &nonce_account,
+        &nonce_authority.pubkey(),
+    )]
+    .with_memo(memo);
     let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
-    let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
+    let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
     let mut tx = Transaction::new_unsigned(message);
     tx.try_sign(&config.signers, recent_blockhash)?;
     check_account_for_fee_with_commitment(
@@ -517,19 +541,21 @@ pub fn process_withdraw_from_nonce_account(
     config: &CliConfig,
     nonce_account: &Pubkey,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
     destination_account_pubkey: &Pubkey,
     lamports: u64,
 ) -> ProcessResult {
     let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
 
     let nonce_authority = config.signers[nonce_authority];
-    let ix = withdraw_nonce_account(
+    let ixs = vec![withdraw_nonce_account(
         nonce_account,
         &nonce_authority.pubkey(),
         destination_account_pubkey,
         lamports,
-    );
-    let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
+    )]
+    .with_memo(memo);
+    let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
     let mut tx = Transaction::new_unsigned(message);
     tx.try_sign(&config.signers, recent_blockhash)?;
     check_account_for_fee_with_commitment(
@@ -597,6 +623,7 @@ mod tests {
                 command: CliCommand::AuthorizeNonceAccount {
                     nonce_account: nonce_account_pubkey,
                     nonce_authority: 0,
+                    memo: None,
                     new_authority: Pubkey::default(),
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
@@ -618,6 +645,7 @@ mod tests {
                 command: CliCommand::AuthorizeNonceAccount {
                     nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
                     nonce_authority: 1,
+                    memo: None,
                     new_authority: Pubkey::default(),
                 },
                 signers: vec![
@@ -641,6 +669,7 @@ mod tests {
                     nonce_account: 1,
                     seed: None,
                     nonce_authority: None,
+                    memo: None,
                     amount: SpendAmount::Some(50_000_000_000),
                 },
                 signers: vec![
@@ -666,6 +695,7 @@ mod tests {
                     nonce_account: 1,
                     seed: None,
                     nonce_authority: Some(nonce_authority_keypair.pubkey()),
+                    memo: None,
                     amount: SpendAmount::Some(50_000_000_000),
                 },
                 signers: vec![
@@ -701,6 +731,7 @@ mod tests {
                 command: CliCommand::NewNonce {
                     nonce_account: nonce_account.pubkey(),
                     nonce_authority: 0,
+                    memo: None,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
             }
@@ -721,6 +752,7 @@ mod tests {
                 command: CliCommand::NewNonce {
                     nonce_account: nonce_account.pubkey(),
                     nonce_authority: 1,
+                    memo: None,
                 },
                 signers: vec![
                     read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -765,6 +797,7 @@ mod tests {
                 command: CliCommand::WithdrawFromNonceAccount {
                     nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
                     nonce_authority: 0,
+                    memo: None,
                     destination_account_pubkey: nonce_account_pubkey,
                     lamports: 42_000_000_000
                 },
@@ -793,6 +826,7 @@ mod tests {
                 command: CliCommand::WithdrawFromNonceAccount {
                     nonce_account: read_keypair_file(&keypair_file).unwrap().pubkey(),
                     nonce_authority: 1,
+                    memo: None,
                     destination_account_pubkey: nonce_account_pubkey,
                     lamports: 42_000_000_000
                 },

+ 81 - 5
cli/src/stake.rs

@@ -4,6 +4,7 @@ use crate::{
         log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
         ProcessResult,
     },
+    memo::WithMemo,
     nonce::check_nonce_account,
     spend_utils::{resolve_spend_tx_and_check_account_balances, SpendAmount},
 };
@@ -14,6 +15,7 @@ use solana_clap_utils::{
     input_parsers::*,
     input_validators::*,
     keypair::{DefaultSigner, SignerIndex},
+    memo::MEMO_ARG,
     nonce::*,
     offline::*,
     ArgConstant,
@@ -445,6 +447,7 @@ pub fn parse_stake_create_account(
     let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
     let blockhash_query = BlockhashQuery::new_from_matches(matches);
     let nonce_account = pubkey_of_signer(matches, NONCE_ARG.name, wallet_manager)?;
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
     let (nonce_authority, nonce_authority_pubkey) =
         signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
     let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
@@ -476,6 +479,7 @@ pub fn parse_stake_create_account(
             blockhash_query,
             nonce_account,
             nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+            memo,
             fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
             from: signer_info.index_of(from_pubkey).unwrap(),
         },
@@ -497,6 +501,7 @@ pub fn parse_stake_delegate_stake(
     let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
     let blockhash_query = BlockhashQuery::new_from_matches(matches);
     let nonce_account = pubkey_of(matches, NONCE_ARG.name);
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
     let (stake_authority, stake_authority_pubkey) =
         signer_of(matches, STAKE_AUTHORITY_ARG.name, wallet_manager)?;
     let (nonce_authority, nonce_authority_pubkey) =
@@ -521,6 +526,7 @@ pub fn parse_stake_delegate_stake(
             blockhash_query,
             nonce_account,
             nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+            memo,
             fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
         },
         signers: signer_info.signers,
@@ -573,6 +579,7 @@ pub fn parse_stake_authorize(
     let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
     let blockhash_query = BlockhashQuery::new_from_matches(matches);
     let nonce_account = pubkey_of(matches, NONCE_ARG.name);
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
     let (nonce_authority, nonce_authority_pubkey) =
         signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
     let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
@@ -610,6 +617,7 @@ pub fn parse_stake_authorize(
             blockhash_query,
             nonce_account,
             nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+            memo,
             fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
             custodian: custodian_pubkey.and_then(|_| signer_info.index_of(custodian_pubkey)),
         },
@@ -633,6 +641,7 @@ pub fn parse_split_stake(
     let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
     let blockhash_query = BlockhashQuery::new_from_matches(matches);
     let nonce_account = pubkey_of(matches, NONCE_ARG.name);
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
     let (stake_authority, stake_authority_pubkey) =
         signer_of(matches, STAKE_AUTHORITY_ARG.name, wallet_manager)?;
     let (nonce_authority, nonce_authority_pubkey) =
@@ -655,6 +664,7 @@ pub fn parse_split_stake(
             blockhash_query,
             nonce_account,
             nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+            memo,
             split_stake_account: signer_info.index_of(split_stake_account_pubkey).unwrap(),
             seed,
             lamports,
@@ -678,6 +688,7 @@ pub fn parse_merge_stake(
     let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
     let blockhash_query = BlockhashQuery::new_from_matches(matches);
     let nonce_account = pubkey_of(matches, NONCE_ARG.name);
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
     let (stake_authority, stake_authority_pubkey) =
         signer_of(matches, STAKE_AUTHORITY_ARG.name, wallet_manager)?;
     let (nonce_authority, nonce_authority_pubkey) =
@@ -701,6 +712,7 @@ pub fn parse_merge_stake(
             blockhash_query,
             nonce_account,
             nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+            memo,
             fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
         },
         signers: signer_info.signers,
@@ -718,6 +730,7 @@ pub fn parse_stake_deactivate_stake(
     let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
     let blockhash_query = BlockhashQuery::new_from_matches(matches);
     let nonce_account = pubkey_of(matches, NONCE_ARG.name);
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
     let (stake_authority, stake_authority_pubkey) =
         signer_of(matches, STAKE_AUTHORITY_ARG.name, wallet_manager)?;
     let (nonce_authority, nonce_authority_pubkey) =
@@ -740,6 +753,7 @@ pub fn parse_stake_deactivate_stake(
             blockhash_query,
             nonce_account,
             nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+            memo,
             fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
         },
         signers: signer_info.signers,
@@ -760,6 +774,7 @@ pub fn parse_stake_withdraw_stake(
     let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
     let blockhash_query = BlockhashQuery::new_from_matches(matches);
     let nonce_account = pubkey_of(matches, NONCE_ARG.name);
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
     let (withdraw_authority, withdraw_authority_pubkey) =
         signer_of(matches, WITHDRAW_AUTHORITY_ARG.name, wallet_manager)?;
     let (nonce_authority, nonce_authority_pubkey) =
@@ -788,6 +803,7 @@ pub fn parse_stake_withdraw_stake(
             blockhash_query,
             nonce_account,
             nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+            memo,
             fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
             custodian: custodian_pubkey.and_then(|_| signer_info.index_of(custodian_pubkey)),
         },
@@ -810,6 +826,7 @@ pub fn parse_stake_set_lockup(
     let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name);
     let blockhash_query = BlockhashQuery::new_from_matches(matches);
     let nonce_account = pubkey_of(matches, NONCE_ARG.name);
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
 
     let (custodian, custodian_pubkey) = signer_of(matches, "custodian", wallet_manager)?;
     let (nonce_authority, nonce_authority_pubkey) =
@@ -837,6 +854,7 @@ pub fn parse_stake_set_lockup(
             blockhash_query,
             nonce_account,
             nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
+            memo,
             fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
         },
         signers: signer_info.signers,
@@ -882,6 +900,7 @@ pub fn process_create_stake_account(
     blockhash_query: &BlockhashQuery,
     nonce_account: Option<&Pubkey>,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
     fee_payer: SignerIndex,
     from: SignerIndex,
 ) -> ProcessResult {
@@ -916,6 +935,7 @@ pub fn process_create_stake_account(
                 lockup,
                 lamports,
             )
+            .with_memo(memo)
         } else {
             stake_instruction::create_account(
                 &from.pubkey(),
@@ -924,6 +944,7 @@ pub fn process_create_stake_account(
                 lockup,
                 lamports,
             )
+            .with_memo(memo)
         };
         if let Some(nonce_account) = &nonce_account {
             Message::new_with_nonce(
@@ -1014,6 +1035,7 @@ pub fn process_stake_authorize(
     blockhash_query: &BlockhashQuery,
     nonce_account: Option<Pubkey>,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
     fee_payer: SignerIndex,
 ) -> ProcessResult {
     let mut ixs = Vec::new();
@@ -1032,6 +1054,7 @@ pub fn process_stake_authorize(
             custodian.map(|signer| signer.pubkey()).as_ref(),
         ));
     }
+    ixs = ixs.with_memo(memo);
 
     let (recent_blockhash, fee_calculator) =
         blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
@@ -1093,6 +1116,7 @@ pub fn process_deactivate_stake_account(
     blockhash_query: &BlockhashQuery,
     nonce_account: Option<Pubkey>,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
     fee_payer: SignerIndex,
 ) -> ProcessResult {
     let (recent_blockhash, fee_calculator) =
@@ -1101,7 +1125,8 @@ pub fn process_deactivate_stake_account(
     let ixs = vec![stake_instruction::deactivate_stake(
         stake_account_pubkey,
         &stake_authority.pubkey(),
-    )];
+    )]
+    .with_memo(memo);
     let nonce_authority = config.signers[nonce_authority];
     let fee_payer = config.signers[fee_payer];
 
@@ -1162,6 +1187,7 @@ pub fn process_withdraw_stake(
     blockhash_query: &BlockhashQuery,
     nonce_account: Option<&Pubkey>,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
     fee_payer: SignerIndex,
 ) -> ProcessResult {
     let (recent_blockhash, fee_calculator) =
@@ -1175,7 +1201,8 @@ pub fn process_withdraw_stake(
         destination_account_pubkey,
         lamports,
         custodian.map(|signer| signer.pubkey()).as_ref(),
-    )];
+    )]
+    .with_memo(memo);
 
     let fee_payer = config.signers[fee_payer];
     let nonce_authority = config.signers[nonce_authority];
@@ -1234,6 +1261,7 @@ pub fn process_split_stake(
     blockhash_query: &BlockhashQuery,
     nonce_account: Option<Pubkey>,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
     split_stake_account: SignerIndex,
     split_stake_account_seed: &Option<String>,
     lamports: u64,
@@ -1315,6 +1343,7 @@ pub fn process_split_stake(
             &split_stake_account.pubkey(),
             seed,
         )
+        .with_memo(memo)
     } else {
         stake_instruction::split(
             &stake_account_pubkey,
@@ -1322,6 +1351,7 @@ pub fn process_split_stake(
             lamports,
             &split_stake_account_address,
         )
+        .with_memo(memo)
     };
 
     let nonce_authority = config.signers[nonce_authority];
@@ -1381,6 +1411,7 @@ pub fn process_merge_stake(
     blockhash_query: &BlockhashQuery,
     nonce_account: Option<Pubkey>,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
     fee_payer: SignerIndex,
 ) -> ProcessResult {
     let fee_payer = config.signers[fee_payer];
@@ -1427,7 +1458,8 @@ pub fn process_merge_stake(
         &stake_account_pubkey,
         &source_stake_account_pubkey,
         &stake_authority.pubkey(),
-    );
+    )
+    .with_memo(memo);
 
     let nonce_authority = config.signers[nonce_authority];
 
@@ -1490,6 +1522,7 @@ pub fn process_stake_set_lockup(
     blockhash_query: &BlockhashQuery,
     nonce_account: Option<Pubkey>,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
     fee_payer: SignerIndex,
 ) -> ProcessResult {
     let (recent_blockhash, fee_calculator) =
@@ -1500,7 +1533,8 @@ pub fn process_stake_set_lockup(
         stake_account_pubkey,
         lockup,
         &custodian.pubkey(),
-    )];
+    )]
+    .with_memo(memo);
     let nonce_authority = config.signers[nonce_authority];
     let fee_payer = config.signers[fee_payer];
 
@@ -1842,6 +1876,7 @@ pub fn process_delegate_stake(
     blockhash_query: &BlockhashQuery,
     nonce_account: Option<Pubkey>,
     nonce_authority: SignerIndex,
+    memo: Option<&String>,
     fee_payer: SignerIndex,
 ) -> ProcessResult {
     check_unique_pubkeys(
@@ -1905,7 +1940,8 @@ pub fn process_delegate_stake(
         stake_account_pubkey,
         &stake_authority.pubkey(),
         vote_account_pubkey,
-    )];
+    )]
+    .with_memo(memo);
     let nonce_authority = config.signers[nonce_authority];
     let fee_payer = config.signers[fee_payer];
 
@@ -2030,6 +2066,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     custodian: None,
                 },
@@ -2066,6 +2103,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     custodian: None,
                 },
@@ -2106,6 +2144,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     custodian: None,
                 },
@@ -2135,6 +2174,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     custodian: None,
                 },
@@ -2161,6 +2201,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     custodian: None,
                 },
@@ -2193,6 +2234,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     custodian: None,
                 },
@@ -2226,6 +2268,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     custodian: None,
                 },
@@ -2256,6 +2299,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     custodian: None,
                 },
@@ -2292,6 +2336,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::None(blockhash),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     custodian: None,
                 },
@@ -2330,6 +2375,7 @@ mod tests {
                     ),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 1,
                     custodian: None,
                 },
@@ -2378,6 +2424,7 @@ mod tests {
                     ),
                     nonce_account: Some(nonce_account),
                     nonce_authority: 2,
+                    memo: None,
                     fee_payer: 1,
                     custodian: None,
                 },
@@ -2412,6 +2459,7 @@ mod tests {
                     ),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     custodian: None,
                 },
@@ -2451,6 +2499,7 @@ mod tests {
                     ),
                     nonce_account: Some(nonce_account_pubkey),
                     nonce_authority: 1,
+                    memo: None,
                     fee_payer: 0,
                     custodian: None,
                 },
@@ -2486,6 +2535,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 1,
                     custodian: None,
                 },
@@ -2525,6 +2575,7 @@ mod tests {
                     ),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 1,
                     custodian: None,
                 },
@@ -2573,6 +2624,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     from: 0,
                 },
@@ -2611,6 +2663,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     from: 0,
                 },
@@ -2668,6 +2721,7 @@ mod tests {
                     ),
                     nonce_account: Some(nonce_account),
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                     from: 0,
                 },
@@ -2700,6 +2754,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::default(),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
@@ -2730,6 +2785,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::default(),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![
@@ -2762,6 +2818,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::default(),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
@@ -2795,6 +2852,7 @@ mod tests {
                     ),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
@@ -2823,6 +2881,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::None(blockhash),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
@@ -2861,6 +2920,7 @@ mod tests {
                     ),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 1,
                 },
                 signers: vec![
@@ -2908,6 +2968,7 @@ mod tests {
                     ),
                     nonce_account: Some(nonce_account),
                     nonce_authority: 2,
+                    memo: None,
                     fee_payer: 1,
                 },
                 signers: vec![
@@ -2943,6 +3004,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 1,
                 },
                 signers: vec![
@@ -2975,6 +3037,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
@@ -3006,6 +3069,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![
@@ -3042,6 +3106,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![
@@ -3089,6 +3154,7 @@ mod tests {
                     ),
                     nonce_account: Some(nonce_account),
                     nonce_authority: 1,
+                    memo: None,
                     fee_payer: 1,
                 },
                 signers: vec![
@@ -3117,6 +3183,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::default(),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
@@ -3142,6 +3209,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::default(),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![
@@ -3177,6 +3245,7 @@ mod tests {
                     ),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
@@ -3202,6 +3271,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::None(blockhash),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
@@ -3237,6 +3307,7 @@ mod tests {
                     ),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 1,
                 },
                 signers: vec![
@@ -3281,6 +3352,7 @@ mod tests {
                     ),
                     nonce_account: Some(nonce_account),
                     nonce_authority: 2,
+                    memo: None,
                     fee_payer: 1,
                 },
                 signers: vec![
@@ -3310,6 +3382,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 1,
                 },
                 signers: vec![
@@ -3345,6 +3418,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::default(),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     split_stake_account: 1,
                     seed: None,
                     lamports: 50_000_000_000,
@@ -3410,6 +3484,7 @@ mod tests {
                     ),
                     nonce_account: Some(nonce_account),
                     nonce_authority: 1,
+                    memo: None,
                     split_stake_account: 2,
                     seed: None,
                     lamports: 50_000_000_000,
@@ -3449,6 +3524,7 @@ mod tests {
                     blockhash_query: BlockhashQuery::default(),
                     nonce_account: None,
                     nonce_authority: 0,
+                    memo: None,
                     fee_payer: 0,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],

+ 53 - 13
cli/src/vote.rs

@@ -4,6 +4,7 @@ use crate::{
         log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
         ProcessResult,
     },
+    memo::WithMemo,
     spend_utils::{resolve_spend_tx_and_check_account_balance, SpendAmount},
 };
 use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
@@ -11,6 +12,7 @@ use solana_clap_utils::{
     input_parsers::*,
     input_validators::*,
     keypair::{DefaultSigner, SignerIndex},
+    memo::{memo_arg, MEMO_ARG},
 };
 use solana_cli_output::{CliEpochVotingHistory, CliLockout, CliVoteAccount};
 use solana_client::rpc_client::RpcClient;
@@ -79,7 +81,8 @@ impl VoteSubCommands for App<'_, '_> {
                         .value_name("STRING")
                         .takes_value(true)
                         .help("Seed for address generation; if specified, the resulting account will be at a derived address of the VOTE ACCOUNT pubkey")
-                ),
+                )
+		.arg(memo_arg())
         )
         .subcommand(
             SubCommand::with_name("vote-authorize-voter")
@@ -105,7 +108,8 @@ impl VoteSubCommands for App<'_, '_> {
                         .value_name("NEW_AUTHORIZED_PUBKEY")
                         .required(true),
                         "New authorized vote signer. "),
-                ),
+                )
+		.arg(memo_arg())
         )
         .subcommand(
             SubCommand::with_name("vote-authorize-withdrawer")
@@ -131,7 +135,8 @@ impl VoteSubCommands for App<'_, '_> {
                         .value_name("AUTHORIZED_PUBKEY")
                         .required(true),
                         "New authorized withdrawer. "),
-                ),
+                )
+		.arg(memo_arg())
         )
         .subcommand(
             SubCommand::with_name("vote-update-validator")
@@ -161,6 +166,7 @@ impl VoteSubCommands for App<'_, '_> {
                         .validator(is_valid_signer)
                         .help("Authorized withdrawer keypair"),
                 )
+		.arg(memo_arg())
         )
         .subcommand(
             SubCommand::with_name("vote-update-commission")
@@ -190,6 +196,7 @@ impl VoteSubCommands for App<'_, '_> {
                         .validator(is_valid_signer)
                         .help("Authorized withdrawer keypair"),
                 )
+		.arg(memo_arg())
         )
         .subcommand(
             SubCommand::with_name("vote-account")
@@ -243,6 +250,7 @@ impl VoteSubCommands for App<'_, '_> {
                         .validator(is_valid_signer)
                         .help("Authorized withdrawer [default: cli config keypair]"),
                 )
+		.arg(memo_arg())
         )
     }
 }
@@ -259,6 +267,7 @@ pub fn parse_create_vote_account(
     let commission = value_t_or_exit!(matches, "commission", u8);
     let authorized_voter = pubkey_of_signer(matches, "authorized_voter", wallet_manager)?;
     let authorized_withdrawer = pubkey_of_signer(matches, "authorized_withdrawer", wallet_manager)?;
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
 
     let payer_provided = None;
     let signer_info = default_signer.generate_unique_signers(
@@ -275,6 +284,7 @@ pub fn parse_create_vote_account(
             authorized_voter,
             authorized_withdrawer,
             commission,
+            memo,
         },
         signers: signer_info.signers,
     })
@@ -298,12 +308,14 @@ pub fn parse_vote_authorize(
         matches,
         wallet_manager,
     )?;
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
 
     Ok(CliCommandInfo {
         command: CliCommand::VoteAuthorize {
             vote_account_pubkey,
             new_authorized_pubkey,
             vote_authorize,
+            memo,
         },
         signers: signer_info.signers,
     })
@@ -327,12 +339,14 @@ pub fn parse_vote_update_validator(
         matches,
         wallet_manager,
     )?;
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
 
     Ok(CliCommandInfo {
         command: CliCommand::VoteUpdateValidator {
             vote_account_pubkey,
             new_identity_account: signer_info.index_of(new_identity_pubkey).unwrap(),
             withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
+            memo,
         },
         signers: signer_info.signers,
     })
@@ -355,12 +369,14 @@ pub fn parse_vote_update_commission(
         matches,
         wallet_manager,
     )?;
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
 
     Ok(CliCommandInfo {
         command: CliCommand::VoteUpdateCommission {
             vote_account_pubkey,
             commission,
             withdraw_authority: signer_info.index_of(authorized_withdrawer_pubkey).unwrap(),
+            memo,
         },
         signers: signer_info.signers,
     })
@@ -402,6 +418,7 @@ pub fn parse_withdraw_from_vote_account(
         matches,
         wallet_manager,
     )?;
+    let memo = matches.value_of(MEMO_ARG.name).map(String::from);
 
     Ok(CliCommandInfo {
         command: CliCommand::WithdrawFromVoteAccount {
@@ -409,6 +426,7 @@ pub fn parse_withdraw_from_vote_account(
             destination_account_pubkey,
             withdraw_authority: signer_info.index_of(withdraw_authority_pubkey).unwrap(),
             withdraw_amount,
+            memo,
         },
         signers: signer_info.signers,
     })
@@ -423,6 +441,7 @@ pub fn process_create_vote_account(
     authorized_voter: &Option<Pubkey>,
     authorized_withdrawer: &Option<Pubkey>,
     commission: u8,
+    memo: Option<&String>,
 ) -> ProcessResult {
     let vote_account = config.signers[vote_account];
     let vote_account_pubkey = vote_account.pubkey();
@@ -465,6 +484,7 @@ pub fn process_create_vote_account(
                 &vote_init,
                 lamports,
             )
+            .with_memo(memo)
         } else {
             vote_instruction::create_account(
                 &config.signers[0].pubkey(),
@@ -472,6 +492,7 @@ pub fn process_create_vote_account(
                 &vote_init,
                 lamports,
             )
+            .with_memo(memo)
         };
         Message::new(&ixs, Some(&config.signers[0].pubkey()))
     };
@@ -515,6 +536,7 @@ pub fn process_vote_authorize(
     vote_account_pubkey: &Pubkey,
     new_authorized_pubkey: &Pubkey,
     vote_authorize: VoteAuthorize,
+    memo: Option<&String>,
 ) -> ProcessResult {
     // If the `authorized_account` is also the fee payer, `config.signers` will only have one
     // keypair in it
@@ -534,7 +556,8 @@ pub fn process_vote_authorize(
         &authorized.pubkey(),  // current authorized
         new_authorized_pubkey, // new vote signer/withdrawer
         vote_authorize,        // vote or withdraw
-    )];
+    )]
+    .with_memo(memo);
 
     let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
     let mut tx = Transaction::new_unsigned(message);
@@ -556,6 +579,7 @@ pub fn process_vote_update_validator(
     vote_account_pubkey: &Pubkey,
     new_identity_account: SignerIndex,
     withdraw_authority: SignerIndex,
+    memo: Option<&String>,
 ) -> ProcessResult {
     let authorized_withdrawer = config.signers[withdraw_authority];
     let new_identity_account = config.signers[new_identity_account];
@@ -569,7 +593,8 @@ pub fn process_vote_update_validator(
         vote_account_pubkey,
         &authorized_withdrawer.pubkey(),
         &new_identity_pubkey,
-    )];
+    )]
+    .with_memo(memo);
 
     let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
     let mut tx = Transaction::new_unsigned(message);
@@ -591,6 +616,7 @@ pub fn process_vote_update_commission(
     vote_account_pubkey: &Pubkey,
     commission: u8,
     withdraw_authority: SignerIndex,
+    memo: Option<&String>,
 ) -> ProcessResult {
     let authorized_withdrawer = config.signers[withdraw_authority];
     let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
@@ -598,7 +624,8 @@ pub fn process_vote_update_commission(
         vote_account_pubkey,
         &authorized_withdrawer.pubkey(),
         commission,
-    )];
+    )]
+    .with_memo(memo);
 
     let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
     let mut tx = Transaction::new_unsigned(message);
@@ -706,6 +733,7 @@ pub fn process_withdraw_from_vote_account(
     withdraw_authority: SignerIndex,
     withdraw_amount: SpendAmount,
     destination_account_pubkey: &Pubkey,
+    memo: Option<&String>,
 ) -> ProcessResult {
     let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
     let withdraw_authority = config.signers[withdraw_authority];
@@ -726,14 +754,15 @@ pub fn process_withdraw_from_vote_account(
         }
     };
 
-    let ix = withdraw(
+    let ixs = vec![withdraw(
         vote_account_pubkey,
         &withdraw_authority.pubkey(),
         lamports,
         destination_account_pubkey,
-    );
+    )]
+    .with_memo(memo);
 
-    let message = Message::new(&[ix], Some(&config.signers[0].pubkey()));
+    let message = Message::new(&ixs, Some(&config.signers[0].pubkey()));
     let mut transaction = Transaction::new_unsigned(message);
     transaction.try_sign(&config.signers, recent_blockhash)?;
     check_account_for_fee_with_commitment(
@@ -790,7 +819,8 @@ mod tests {
                 command: CliCommand::VoteAuthorize {
                     vote_account_pubkey: pubkey,
                     new_authorized_pubkey: pubkey2,
-                    vote_authorize: VoteAuthorize::Voter
+                    vote_authorize: VoteAuthorize::Voter,
+                    memo: None,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
             }
@@ -813,7 +843,8 @@ mod tests {
                 command: CliCommand::VoteAuthorize {
                     vote_account_pubkey: pubkey,
                     new_authorized_pubkey: pubkey2,
-                    vote_authorize: VoteAuthorize::Voter
+                    vote_authorize: VoteAuthorize::Voter,
+                    memo: None,
                 },
                 signers: vec![
                     read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -847,6 +878,7 @@ mod tests {
                     authorized_voter: None,
                     authorized_withdrawer: None,
                     commission: 10,
+                    memo: None,
                 },
                 signers: vec![
                     read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -876,6 +908,7 @@ mod tests {
                     authorized_voter: None,
                     authorized_withdrawer: None,
                     commission: 100,
+                    memo: None,
                 },
                 signers: vec![
                     read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -908,7 +941,8 @@ mod tests {
                     identity_account: 2,
                     authorized_voter: Some(authed),
                     authorized_withdrawer: None,
-                    commission: 100
+                    commission: 100,
+                    memo: None,
                 },
                 signers: vec![
                     read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -939,7 +973,8 @@ mod tests {
                     identity_account: 2,
                     authorized_voter: None,
                     authorized_withdrawer: Some(authed),
-                    commission: 100
+                    commission: 100,
+                    memo: None,
                 },
                 signers: vec![
                     read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -963,6 +998,7 @@ mod tests {
                     vote_account_pubkey: pubkey,
                     new_identity_account: 2,
                     withdraw_authority: 1,
+                    memo: None,
                 },
                 signers: vec![
                     read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -986,6 +1022,7 @@ mod tests {
                     vote_account_pubkey: pubkey,
                     commission: 42,
                     withdraw_authority: 1,
+                    memo: None,
                 },
                 signers: vec![
                     read_keypair_file(&default_keypair_file).unwrap().into(),
@@ -1010,6 +1047,7 @@ mod tests {
                     destination_account_pubkey: pubkey,
                     withdraw_authority: 0,
                     withdraw_amount: SpendAmount::Some(42_000_000_000),
+                    memo: None,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
             }
@@ -1031,6 +1069,7 @@ mod tests {
                     destination_account_pubkey: pubkey,
                     withdraw_authority: 0,
                     withdraw_amount: SpendAmount::All,
+                    memo: None,
                 },
                 signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
             }
@@ -1057,6 +1096,7 @@ mod tests {
                     destination_account_pubkey: pubkey,
                     withdraw_authority: 1,
                     withdraw_amount: SpendAmount::Some(42_000_000_000),
+                    memo: None,
                 },
                 signers: vec![
                     read_keypair_file(&default_keypair_file).unwrap().into(),

+ 10 - 0
cli/tests/nonce.rs

@@ -108,6 +108,7 @@ fn full_battery_tests(
         nonce_account: 1,
         seed,
         nonce_authority: optional_authority,
+        memo: None,
         amount: SpendAmount::Some(1000),
     };
 
@@ -141,6 +142,7 @@ fn full_battery_tests(
     config_payer.command = CliCommand::NewNonce {
         nonce_account,
         nonce_authority: index,
+        memo: None,
     };
     process_command(&config_payer).unwrap();
 
@@ -158,6 +160,7 @@ fn full_battery_tests(
     config_payer.command = CliCommand::WithdrawFromNonceAccount {
         nonce_account,
         nonce_authority: index,
+        memo: None,
         destination_account_pubkey: payee_pubkey,
         lamports: 100,
     };
@@ -178,6 +181,7 @@ fn full_battery_tests(
     config_payer.command = CliCommand::AuthorizeNonceAccount {
         nonce_account,
         nonce_authority: index,
+        memo: None,
         new_authority: new_authority.pubkey(),
     };
     process_command(&config_payer).unwrap();
@@ -186,6 +190,7 @@ fn full_battery_tests(
     config_payer.command = CliCommand::NewNonce {
         nonce_account,
         nonce_authority: index,
+        memo: None,
     };
     process_command(&config_payer).unwrap_err();
 
@@ -194,6 +199,7 @@ fn full_battery_tests(
     config_payer.command = CliCommand::NewNonce {
         nonce_account,
         nonce_authority: 1,
+        memo: None,
     };
     process_command(&config_payer).unwrap();
 
@@ -201,6 +207,7 @@ fn full_battery_tests(
     config_payer.command = CliCommand::WithdrawFromNonceAccount {
         nonce_account,
         nonce_authority: 1,
+        memo: None,
         destination_account_pubkey: payee_pubkey,
         lamports: 100,
     };
@@ -263,6 +270,7 @@ fn test_create_account_with_seed() {
         nonce_account: 0,
         seed: Some(seed),
         nonce_authority: Some(authority_pubkey),
+        memo: None,
         amount: SpendAmount::Some(241),
     };
     process_command(&creator_config).unwrap();
@@ -299,6 +307,7 @@ fn test_create_account_with_seed() {
         blockhash_query: BlockhashQuery::None(nonce_hash),
         nonce_account: Some(nonce_address),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -327,6 +336,7 @@ fn test_create_account_with_seed() {
         ),
         nonce_account: Some(nonce_address),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,

+ 51 - 0
cli/tests/stake.rs

@@ -56,6 +56,7 @@ fn test_stake_delegation_force() {
         authorized_voter: None,
         authorized_withdrawer: None,
         commission: 0,
+        memo: None,
     };
     process_command(&config).unwrap();
 
@@ -74,6 +75,7 @@ fn test_stake_delegation_force() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -91,6 +93,7 @@ fn test_stake_delegation_force() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config).unwrap_err();
@@ -106,6 +109,7 @@ fn test_stake_delegation_force() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config).unwrap();
@@ -158,6 +162,7 @@ fn test_seed_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -174,6 +179,7 @@ fn test_seed_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config_validator).unwrap();
@@ -187,6 +193,7 @@ fn test_seed_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config_validator).unwrap();
@@ -234,6 +241,7 @@ fn test_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -251,6 +259,7 @@ fn test_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config_validator).unwrap();
@@ -264,6 +273,7 @@ fn test_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config_validator).unwrap();
@@ -332,6 +342,7 @@ fn test_offline_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -349,6 +360,7 @@ fn test_offline_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::None(blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     config_offline.output_format = OutputFormat::JsonCompact;
@@ -369,6 +381,7 @@ fn test_offline_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config_payer).unwrap();
@@ -383,6 +396,7 @@ fn test_offline_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::None(blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     let sig_response = process_command(&config_offline).unwrap();
@@ -400,6 +414,7 @@ fn test_offline_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config_payer).unwrap();
@@ -449,6 +464,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -461,6 +477,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
         nonce_account: 1,
         seed: None,
         nonce_authority: Some(config.signers[0].pubkey()),
+        memo: None,
         amount: SpendAmount::Some(minimum_nonce_balance),
     };
     process_command(&config).unwrap();
@@ -490,6 +507,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
         ),
         nonce_account: Some(nonce_account.pubkey()),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config).unwrap();
@@ -516,6 +534,7 @@ fn test_nonced_stake_delegation_and_deactivation() {
         ),
         nonce_account: Some(nonce_account.pubkey()),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config).unwrap();
@@ -580,6 +599,7 @@ fn test_stake_authorize() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -597,6 +617,7 @@ fn test_stake_authorize() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         custodian: None,
     };
@@ -626,6 +647,7 @@ fn test_stake_authorize() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         custodian: None,
     };
@@ -650,6 +672,7 @@ fn test_stake_authorize() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         custodian: None,
     };
@@ -674,6 +697,7 @@ fn test_stake_authorize() {
         blockhash_query: BlockhashQuery::None(blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         custodian: None,
     };
@@ -691,6 +715,7 @@ fn test_stake_authorize() {
         blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         custodian: None,
     };
@@ -713,6 +738,7 @@ fn test_stake_authorize() {
         nonce_account: 1,
         seed: None,
         nonce_authority: Some(offline_authority_pubkey),
+        memo: None,
         amount: SpendAmount::Some(minimum_nonce_balance),
     };
     process_command(&config).unwrap();
@@ -739,6 +765,7 @@ fn test_stake_authorize() {
         blockhash_query: BlockhashQuery::None(nonce_hash),
         nonce_account: Some(nonce_account.pubkey()),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         custodian: None,
     };
@@ -760,6 +787,7 @@ fn test_stake_authorize() {
         ),
         nonce_account: Some(nonce_account.pubkey()),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         custodian: None,
     };
@@ -846,6 +874,7 @@ fn test_stake_authorize_with_fee_payer() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -863,6 +892,7 @@ fn test_stake_authorize_with_fee_payer() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 1,
         custodian: None,
     };
@@ -883,6 +913,7 @@ fn test_stake_authorize_with_fee_payer() {
         blockhash_query: BlockhashQuery::None(blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         custodian: None,
     };
@@ -900,6 +931,7 @@ fn test_stake_authorize_with_fee_payer() {
         blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         custodian: None,
     };
@@ -969,6 +1001,7 @@ fn test_stake_split() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -989,6 +1022,7 @@ fn test_stake_split() {
         nonce_account: 1,
         seed: None,
         nonce_authority: Some(offline_pubkey),
+        memo: None,
         amount: SpendAmount::Some(minimum_nonce_balance),
     };
     process_command(&config).unwrap();
@@ -1016,6 +1050,7 @@ fn test_stake_split() {
         blockhash_query: BlockhashQuery::None(nonce_hash),
         nonce_account: Some(nonce_account.pubkey()),
         nonce_authority: 0,
+        memo: None,
         split_stake_account: 1,
         seed: None,
         lamports: 2 * minimum_stake_balance,
@@ -1038,6 +1073,7 @@ fn test_stake_split() {
         ),
         nonce_account: Some(nonce_account.pubkey()),
         nonce_authority: 0,
+        memo: None,
         split_stake_account: 1,
         seed: None,
         lamports: 2 * minimum_stake_balance,
@@ -1121,6 +1157,7 @@ fn test_stake_set_lockup() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -1147,6 +1184,7 @@ fn test_stake_set_lockup() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config).unwrap();
@@ -1181,6 +1219,7 @@ fn test_stake_set_lockup() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config).unwrap();
@@ -1200,6 +1239,7 @@ fn test_stake_set_lockup() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config).unwrap();
@@ -1231,6 +1271,7 @@ fn test_stake_set_lockup() {
         blockhash_query: BlockhashQuery::default(),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config).unwrap();
@@ -1246,6 +1287,7 @@ fn test_stake_set_lockup() {
         nonce_account: 1,
         seed: None,
         nonce_authority: Some(offline_pubkey),
+        memo: None,
         amount: SpendAmount::Some(minimum_nonce_balance),
     };
     process_command(&config).unwrap();
@@ -1276,6 +1318,7 @@ fn test_stake_set_lockup() {
         blockhash_query: BlockhashQuery::None(nonce_hash),
         nonce_account: Some(nonce_account_pubkey),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     config_offline.output_format = OutputFormat::JsonCompact;
@@ -1296,6 +1339,7 @@ fn test_stake_set_lockup() {
         ),
         nonce_account: Some(nonce_account_pubkey),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config).unwrap();
@@ -1362,6 +1406,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
         nonce_account: 1,
         seed: None,
         nonce_authority: Some(offline_pubkey),
+        memo: None,
         amount: SpendAmount::Some(minimum_nonce_balance),
     };
     process_command(&config).unwrap();
@@ -1392,6 +1437,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
         blockhash_query: BlockhashQuery::None(nonce_hash),
         nonce_account: Some(nonce_pubkey),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -1417,6 +1463,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
         ),
         nonce_account: Some(nonce_pubkey),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -1448,6 +1495,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
         blockhash_query: BlockhashQuery::None(nonce_hash),
         nonce_account: Some(nonce_pubkey),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     let sig_response = process_command(&config_offline).unwrap();
@@ -1468,6 +1516,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
         ),
         nonce_account: Some(nonce_pubkey),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
     };
     process_command(&config).unwrap();
@@ -1498,6 +1547,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
         blockhash_query: BlockhashQuery::None(nonce_hash),
         nonce_account: Some(nonce_pubkey),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };
@@ -1521,6 +1571,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
         ),
         nonce_account: Some(nonce_pubkey),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         from: 0,
     };

+ 15 - 0
cli/tests/transfer.rs

@@ -57,6 +57,7 @@ fn test_transfer() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -77,6 +78,7 @@ fn test_transfer() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -109,6 +111,7 @@ fn test_transfer() {
         blockhash_query: BlockhashQuery::None(blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -130,6 +133,7 @@ fn test_transfer() {
         blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -148,6 +152,7 @@ fn test_transfer() {
         nonce_account: 1,
         seed: None,
         nonce_authority: None,
+        memo: None,
         amount: SpendAmount::Some(minimum_nonce_balance),
     };
     process_command(&config).unwrap();
@@ -179,6 +184,7 @@ fn test_transfer() {
         ),
         nonce_account: Some(nonce_account.pubkey()),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -201,6 +207,7 @@ fn test_transfer() {
     config.command = CliCommand::AuthorizeNonceAccount {
         nonce_account: nonce_account.pubkey(),
         nonce_authority: 0,
+        memo: None,
         new_authority: offline_pubkey,
     };
     process_command(&config).unwrap();
@@ -229,6 +236,7 @@ fn test_transfer() {
         blockhash_query: BlockhashQuery::None(nonce_hash),
         nonce_account: Some(nonce_account.pubkey()),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -252,6 +260,7 @@ fn test_transfer() {
         ),
         nonce_account: Some(nonce_account.pubkey()),
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -319,6 +328,7 @@ fn test_transfer_multisession_signing() {
         blockhash_query: BlockhashQuery::None(blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -349,6 +359,7 @@ fn test_transfer_multisession_signing() {
         blockhash_query: BlockhashQuery::None(blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -376,6 +387,7 @@ fn test_transfer_multisession_signing() {
         blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -425,6 +437,7 @@ fn test_transfer_all() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -472,6 +485,7 @@ fn test_transfer_unfunded_recipient() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -529,6 +543,7 @@ fn test_transfer_with_seed() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: Some(derived_address_seed),
         derived_address_program_id: Some(derived_address_program_id),

+ 5 - 0
cli/tests/vote.rs

@@ -50,6 +50,7 @@ fn test_vote_authorize_and_withdraw() {
         authorized_voter: None,
         authorized_withdrawer: Some(config.signers[0].pubkey()),
         commission: 0,
+        memo: None,
     };
     process_command(&config).unwrap();
     let vote_account = rpc_client
@@ -77,6 +78,7 @@ fn test_vote_authorize_and_withdraw() {
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         nonce_account: None,
         nonce_authority: 0,
+        memo: None,
         fee_payer: 0,
         derived_address_seed: None,
         derived_address_program_id: None,
@@ -92,6 +94,7 @@ fn test_vote_authorize_and_withdraw() {
         vote_account_pubkey,
         new_authorized_pubkey: withdraw_authority.pubkey(),
         vote_authorize: VoteAuthorize::Withdrawer,
+        memo: None,
     };
     process_command(&config).unwrap();
     let vote_account = rpc_client
@@ -109,6 +112,7 @@ fn test_vote_authorize_and_withdraw() {
         withdraw_authority: 1,
         withdraw_amount: SpendAmount::Some(100),
         destination_account_pubkey: destination_account,
+        memo: None,
     };
     process_command(&config).unwrap();
     check_recent_balance(expected_balance - 100, &rpc_client, &vote_account_pubkey);
@@ -121,6 +125,7 @@ fn test_vote_authorize_and_withdraw() {
         vote_account_pubkey,
         new_identity_account: 2,
         withdraw_authority: 1,
+        memo: None,
     };
     process_command(&config).unwrap();
 }

+ 1 - 1
docs/src/developing/programming-model/transactions.md

@@ -92,7 +92,7 @@ array and operates on the accounts specified by the instructions. The program
 can return successfully, or with an error code. An error return causes the
 entire transaction to fail immediately.
 
-Program's typically provide helper functions to construct instruction they
+Programs typically provide helper functions to construct instructions they
 support. For example, the system program provides the following Rust helper to
 construct a
 [`SystemInstruction::CreateAccount`](https://github.com/solana-labs/solana/blob/6606590b8132e56dab9e60b3f7d20ba7412a736c/sdk/program/src/system_instruction.rs#L63)

+ 25 - 22
programs/bpf/Cargo.lock

@@ -2917,6 +2917,7 @@ dependencies = [
  "solana-stake-program",
  "solana-transaction-status",
  "solana-vote-program",
+ "spl-memo 3.0.1",
 ]
 
 [[package]]
@@ -2986,9 +2987,9 @@ dependencies = [
 
 [[package]]
 name = "solana-frozen-abi"
-version = "1.5.8"
+version = "1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc44c8096d5847d8cf7f3af3cce565de554cb56371decf93b060633ca8588913"
+checksum = "f81ac3925c8995d935d3af27390cf7b748a81837a29feb75a00e115f40dae6dc"
 dependencies = [
  "bs58",
  "bv",
@@ -2999,8 +3000,8 @@ dependencies = [
  "serde",
  "serde_derive",
  "sha2 0.9.2",
- "solana-frozen-abi-macro 1.5.8",
- "solana-logger 1.5.8",
+ "solana-frozen-abi-macro 1.6.4",
+ "solana-logger 1.6.4",
  "thiserror",
 ]
 
@@ -3024,9 +3025,9 @@ dependencies = [
 
 [[package]]
 name = "solana-frozen-abi-macro"
-version = "1.5.8"
+version = "1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f905159beff1b53e4ba8b018a9d13d96ba164c3973bf3b9d587e730bcc14fc18"
+checksum = "ae448159038e470f3b2dd1ab0d219246211f940a10bf1e656a02cb5c2d897437"
 dependencies = [
  "lazy_static",
  "proc-macro2 1.0.24",
@@ -3048,9 +3049,9 @@ dependencies = [
 
 [[package]]
 name = "solana-logger"
-version = "1.5.8"
+version = "1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d83a006d97da5514a4475141573383d3fcd71c729ff78494f96bb531cf734d21"
+checksum = "7fa7bddd7b89c26c6e3ef4af9b47d6bc8d60888559affb5160f5ade18c0cd058"
 dependencies = [
  "env_logger",
  "lazy_static",
@@ -3110,11 +3111,13 @@ dependencies = [
 
 [[package]]
 name = "solana-program"
-version = "1.5.8"
+version = "1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2007bf285617f8a783e96b20ccd2f8d813549090a5b520f22e5f65b3cd9b157"
+checksum = "231bf4cd42997b6f34e7c74a1449e8a1e8816fd83662fc3a5a0922da48fc667b"
 dependencies = [
  "bincode",
+ "borsh",
+ "borsh-derive",
  "bs58",
  "bv",
  "curve25519-dalek 2.1.0",
@@ -3131,10 +3134,10 @@ dependencies = [
  "serde_bytes",
  "serde_derive",
  "sha2 0.9.2",
- "solana-frozen-abi 1.5.8",
- "solana-frozen-abi-macro 1.5.8",
- "solana-logger 1.5.8",
- "solana-sdk-macro 1.5.8",
+ "solana-frozen-abi 1.6.4",
+ "solana-frozen-abi-macro 1.6.4",
+ "solana-logger 1.6.4",
+ "solana-sdk-macro 1.6.4",
  "thiserror",
 ]
 
@@ -3287,9 +3290,9 @@ dependencies = [
 
 [[package]]
 name = "solana-sdk-macro"
-version = "1.5.8"
+version = "1.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8a6635f1798c8ff1b88bb0fad1d4693c011f36a73c9ae03f198203144edcabb"
+checksum = "3c805f1e32677f8ec0cc0b2f470833a0d5ab0922f068e52be3a4a93b468c9c70"
 dependencies = [
  "bs58",
  "proc-macro2 1.0.24",
@@ -3361,7 +3364,7 @@ dependencies = [
  "solana-vote-program",
  "spl-associated-token-account",
  "spl-memo 2.0.1",
- "spl-memo 3.0.0",
+ "spl-memo 3.0.1",
  "spl-token",
  "thiserror",
 ]
@@ -3439,16 +3442,16 @@ version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fb2b771f6146dec14ef5fbf498f9374652c54badc3befc8c40c1d426dd45d720"
 dependencies = [
- "solana-program 1.5.8",
+ "solana-program 1.6.4",
 ]
 
 [[package]]
 name = "spl-memo"
-version = "3.0.0"
+version = "3.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e76b60c6f58279b5469beb1705744e9778ee94d643c8e3e2ff91874c59bb3c63"
+checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325"
 dependencies = [
- "solana-program 1.5.8",
+ "solana-program 1.6.4",
 ]
 
 [[package]]
@@ -3461,7 +3464,7 @@ dependencies = [
  "num-derive 0.3.0",
  "num-traits",
  "num_enum",
- "solana-program 1.5.8",
+ "solana-program 1.6.4",
  "thiserror",
 ]
 

+ 1 - 1
transaction-status/Cargo.toml

@@ -25,7 +25,7 @@ solana-stake-program = { path = "../programs/stake", version = "=1.7.0" }
 solana-vote-program = { path = "../programs/vote", version = "=1.7.0" }
 spl-associated-token-account-v1-0 = { package = "spl-associated-token-account", version = "=1.0.2", features = ["no-entrypoint"] }
 spl-memo-v1-0 = { package = "spl-memo", version = "=2.0.1", features = ["no-entrypoint"] }
-spl-memo-v3-0 = { package = "spl-memo", version = "=3.0.0", features = ["no-entrypoint"] }
+spl-memo-v3-0 = { package = "spl-memo", version = "=3.0.1", features = ["no-entrypoint"] }
 spl-token-v2-0 = { package = "spl-token", version = "=3.1.0", features = ["no-entrypoint"] }
 thiserror = "1.0"