Skip to content

Commit

Permalink
feat: add tx op add (#1663)
Browse files Browse the repository at this point in the history
Co-authored-by: Leigh McCulloch <[email protected]>
Co-authored-by: Jane Wang <[email protected]>
  • Loading branch information
3 people authored Dec 4, 2024
1 parent 5454d84 commit a03b1d7
Show file tree
Hide file tree
Showing 28 changed files with 808 additions and 145 deletions.
375 changes: 308 additions & 67 deletions FULL_HELP_DOCS.md

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions cmd/crates/soroban-test/tests/it/integration/hello_world.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use predicates::boolean::PredicateBooleanExt;
use soroban_cli::{
commands::{
contract::{self, fetch},
txn_result::TxnResult,
},
config::{address::Address, locator, secret},
config::{locator, secret},
};
use soroban_rpc::GetLatestLedgerResponse;
use soroban_test::{AssertExt, TestEnv, LOCAL_NETWORK_PASSPHRASE};
Expand All @@ -19,7 +18,7 @@ async fn invoke_view_with_non_existent_source_account() {
let sandbox = &TestEnv::new();
let id = deploy_hello(sandbox).await;
let world = "world";
let mut cmd = hello_world_cmd(&id, world);
let cmd = hello_world_cmd(&id, world);
let res = sandbox.run_cmd_with(cmd, "").await.unwrap();
assert_eq!(res, TxnResult::Res(format!(r#"["Hello",{world:?}]"#)));
}
Expand Down
9 changes: 4 additions & 5 deletions cmd/crates/soroban-test/tests/it/integration/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ use soroban_test::AssertExt;
use soroban_test::TestEnv;

fn pubkey_for_identity(sandbox: &TestEnv, name: &str) -> String {
let output = sandbox
sandbox
.new_assert_cmd("keys")
.arg("address")
.arg(name)
.assert()
.stdout_as_str();
return output;
.stdout_as_str()
}

#[tokio::test]
Expand Down Expand Up @@ -61,7 +60,7 @@ async fn overwrite_identity() {
"error: An identity with the name 'test2' already exists",
));

assert_eq!(initial_pubkey, pubkey_for_identity(&sandbox, "test2"));
assert_eq!(initial_pubkey, pubkey_for_identity(sandbox, "test2"));

sandbox
.new_assert_cmd("keys")
Expand All @@ -72,5 +71,5 @@ async fn overwrite_identity() {
.stderr(predicate::str::contains("Overwriting identity 'test2'"))
.success();

assert_ne!(initial_pubkey, pubkey_for_identity(&sandbox, "test2"));
assert_ne!(initial_pubkey, pubkey_for_identity(sandbox, "test2"));
}
98 changes: 98 additions & 0 deletions cmd/crates/soroban-test/tests/it/integration/tx/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use soroban_cli::{
utils::contract_id_hash_from_asset,
xdr::{self, ReadXdr, SequenceNumber},
};
use soroban_rpc::LedgerEntryResult;
use soroban_test::{AssertExt, TestEnv};

use crate::integration::{
Expand Down Expand Up @@ -30,6 +31,20 @@ fn new_account(sandbox: &TestEnv, name: &str) -> String {
.stdout_as_str()
}

fn gen_account_no_fund(sandbox: &TestEnv, name: &str) -> String {
sandbox
.new_assert_cmd("keys")
.args(["generate", "--no-fund", name])
.assert()
.success();
sandbox
.new_assert_cmd("keys")
.args(["address", name])
.assert()
.success()
.stdout_as_str()
}

// returns test and test1 addresses
fn setup_accounts(sandbox: &TestEnv) -> (String, String) {
(test_address(sandbox), new_account(sandbox, "test1"))
Expand Down Expand Up @@ -614,3 +629,86 @@ async fn issue_asset(sandbox: &TestEnv, test: &str, asset: &str, limit: u64, ini
.assert()
.success();
}

#[tokio::test]
async fn multi_create_accounts() {
let sandbox = &TestEnv::new();
let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap();
let nums: Vec<u8> = (1..=3).collect();
let mut accounts: Vec<(String, String)> = nums
.iter()
.map(|x| {
let name = format!("test_{x}");
let address = gen_account_no_fund(sandbox, &name);
(name, address)
})
.collect();
let (_, test_99_address) = accounts.pop().unwrap();

let input = sandbox
.new_assert_cmd("tx")
.args([
"new",
"create-account",
"--fee=1000000",
"--build-only",
"--destination",
&test_99_address,
])
.assert()
.success()
.stdout_as_str();

let final_tx = accounts.iter().fold(input, |tx_env, (_, address)| {
sandbox
.new_assert_cmd("tx")
.args(["op", "add", "create-account", "--destination", address])
.write_stdin(tx_env.as_bytes())
.assert()
.success()
.stdout_as_str()
});
let out = sandbox
.new_assert_cmd("tx")
.arg("send")
.write_stdin(
sandbox
.new_assert_cmd("tx")
.arg("sign")
.arg("--sign-with-key=test")
.write_stdin(final_tx.as_bytes())
.assert()
.success()
.stdout_as_str()
.as_bytes(),
)
.assert()
.success()
.stdout_as_str();
println!("{out}");
let keys = accounts
.iter()
.map(|(_, address)| {
xdr::LedgerKey::Account(xdr::LedgerKeyAccount {
account_id: address.parse().unwrap(),
})
})
.collect::<Vec<_>>();

let account = client.get_account(&test_99_address).await.unwrap();
println!("{account:#?}");
let entries = client.get_ledger_entries(&keys).await.unwrap();
println!("{entries:#?}");
entries
.entries
.unwrap()
.iter()
.for_each(|LedgerEntryResult { xdr, .. }| {
let xdr::LedgerEntryData::Account(value) =
xdr::LedgerEntryData::from_xdr_base64(xdr, xdr::Limits::none()).unwrap()
else {
panic!("Expected Account");
};
assert_eq!(value.balance, 10_000_000);
});
}
2 changes: 1 addition & 1 deletion cmd/crates/soroban-test/tests/it/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod build;
mod config;
mod help;
mod init;
#[cfg(feature = "it")]
// #[cfg(feature = "it")]
mod integration;
mod plugin;
mod util;
Expand Down
24 changes: 24 additions & 0 deletions cmd/soroban-cli/src/commands/tx/help.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
pub const ACCOUNT_MERGE:&str = "Transfers the XLM balance of an account to another account and removes the source account from the ledger";
pub const BUMP_SEQUENCE: &str = "Bumps forward the sequence number of the source account to the given sequence number, invalidating any transaction with a smaller sequence number";
pub const CHANGE_TRUST: &str = r"Creates, updates, or deletes a trustline
Learn more about trustlines
https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#trustlines";

pub const CREATE_ACCOUNT: &str =
"Creates and funds a new account with the specified starting balance";
pub const MANAGE_DATA: &str = r"Sets, modifies, or deletes a data entry (name/value pair) that is attached to an account
Learn more about entries and subentries:
https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#subentries";
pub const PAYMENT: &str = "Sends an amount in a specific asset to a destination account";
pub const SET_OPTIONS: &str = r"Set option for an account such as flags, inflation destination, signers, home domain, and master key weight
Learn more about flags:
https://developers.stellar.org/docs/learn/glossary#flags
Learn more about the home domain:
https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0001.md
Learn more about signers operations and key weight:
https://developers.stellar.org/docs/learn/encyclopedia/security/signatures-multisig#multisig";
pub const SET_TRUSTLINE_FLAGS: &str = r"Allows issuing account to configure authorization and trustline flags to an asset
The Asset parameter is of the `TrustLineAsset` type. If you are modifying a trustline to a regular asset (i.e. one in a Code:Issuer format), this is equivalent to the Asset type.
If you are modifying a trustline to a pool share, however, this is composed of the liquidity pool's unique ID.
Learn more about flags:
https://developers.stellar.org/docs/learn/glossary#flags";
28 changes: 18 additions & 10 deletions cmd/soroban-cli/src/commands/tx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use super::global;

pub mod args;
pub mod hash;
pub mod help;
pub mod new;
pub mod op;
pub mod send;
pub mod sign;
pub mod simulate;
Expand All @@ -12,17 +14,20 @@ pub use args::Args;

#[derive(Debug, clap::Subcommand)]
pub enum Cmd {
/// Simulate a transaction envelope from stdin
Simulate(simulate::Cmd),
/// Calculate the hash of a transaction envelope from stdin
Hash(hash::Cmd),
/// Sign a transaction envelope appending the signature to the envelope
Sign(sign::Cmd),
/// Send a transaction envelope to the network
Send(send::Cmd),
/// Create a new transaction
#[command(subcommand)]
New(new::Cmd),
/// Manipulate the operations in a transaction, including adding new operations
#[command(subcommand, visible_alias = "op")]
Operation(op::Cmd),
/// Send a transaction envelope to the network
Send(send::Cmd),
/// Sign a transaction envelope appending the signature to the envelope
Sign(sign::Cmd),
/// Simulate a transaction envelope from stdin
Simulate(simulate::Cmd),
}

#[derive(thiserror::Error, Debug)]
Expand All @@ -32,21 +37,24 @@ pub enum Error {
#[error(transparent)]
New(#[from] new::Error),
#[error(transparent)]
Simulate(#[from] simulate::Error),
Op(#[from] op::Error),
#[error(transparent)]
Send(#[from] send::Error),
#[error(transparent)]
Sign(#[from] sign::Error),
#[error(transparent)]
Send(#[from] send::Error),
Simulate(#[from] simulate::Error),
}

impl Cmd {
pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> {
match self {
Cmd::Simulate(cmd) => cmd.run(global_args).await?,
Cmd::Hash(cmd) => cmd.run(global_args)?,
Cmd::New(cmd) => cmd.run(global_args).await?,
Cmd::Sign(cmd) => cmd.run(global_args).await?,
Cmd::Operation(cmd) => cmd.run(global_args)?,
Cmd::Send(cmd) => cmd.run(global_args).await?,
Cmd::Sign(cmd) => cmd.run(global_args).await?,
Cmd::Simulate(cmd) => cmd.run(global_args).await?,
};
Ok(())
}
Expand Down
10 changes: 8 additions & 2 deletions cmd/soroban-cli/src/commands/tx/new/account_merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ use crate::{commands::tx, xdr};
pub struct Cmd {
#[command(flatten)]
pub tx: tx::Args,
#[clap(flatten)]
pub op: Args,
}

#[derive(Debug, clap::Args, Clone)]
pub struct Args {
/// Muxed Account to merge with, e.g. `GBX...`, 'MBX...'
#[arg(long)]
pub account: xdr::MuxedAccount,
}

impl From<&Cmd> for xdr::OperationBody {
fn from(cmd: &Cmd) -> Self {
impl From<&Args> for xdr::OperationBody {
fn from(cmd: &Args) -> Self {
xdr::OperationBody::AccountMerge(cmd.account.clone())
}
}
10 changes: 8 additions & 2 deletions cmd/soroban-cli/src/commands/tx/new/bump_sequence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ use crate::{commands::tx, xdr};
pub struct Cmd {
#[command(flatten)]
pub tx: tx::Args,
#[clap(flatten)]
pub op: Args,
}

#[derive(Debug, clap::Args, Clone)]
pub struct Args {
/// Sequence number to bump to
#[arg(long)]
pub bump_to: i64,
}

impl From<&Cmd> for xdr::OperationBody {
fn from(cmd: &Cmd) -> Self {
impl From<&Args> for xdr::OperationBody {
fn from(cmd: &Args) -> Self {
xdr::OperationBody::BumpSequence(xdr::BumpSequenceOp {
bump_to: cmd.bump_to.into(),
})
Expand Down
10 changes: 8 additions & 2 deletions cmd/soroban-cli/src/commands/tx/new/change_trust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ use crate::{commands::tx, tx::builder, xdr};
pub struct Cmd {
#[command(flatten)]
pub tx: tx::Args,
#[clap(flatten)]
pub op: Args,
}

#[derive(Debug, clap::Args, Clone)]
pub struct Args {
#[arg(long)]
pub line: builder::Asset,
/// Limit for the trust line, 0 to remove the trust line
#[arg(long, default_value = i64::MAX.to_string())]
pub limit: i64,
}

impl From<&Cmd> for xdr::OperationBody {
fn from(cmd: &Cmd) -> Self {
impl From<&Args> for xdr::OperationBody {
fn from(cmd: &Args) -> Self {
let line = match cmd.line.0.clone() {
xdr::Asset::CreditAlphanum4(asset) => xdr::ChangeTrustAsset::CreditAlphanum4(asset),
xdr::Asset::CreditAlphanum12(asset) => xdr::ChangeTrustAsset::CreditAlphanum12(asset),
Expand Down
12 changes: 9 additions & 3 deletions cmd/soroban-cli/src/commands/tx/new/create_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,22 @@ use crate::{commands::tx, tx::builder, xdr};
pub struct Cmd {
#[command(flatten)]
pub tx: tx::Args,
#[clap(flatten)]
pub op: Args,
}

#[derive(Debug, clap::Args, Clone)]
pub struct Args {
/// Account Id to create, e.g. `GBX...`
#[arg(long)]
#[arg(long, alias = "dest")]
pub destination: xdr::AccountId,
/// Initial balance in stroops of the account, default 1 XLM
#[arg(long, default_value = "10_000_000")]
pub starting_balance: builder::Amount,
}

impl From<&Cmd> for xdr::OperationBody {
fn from(cmd: &Cmd) -> Self {
impl From<&Args> for xdr::OperationBody {
fn from(cmd: &Args) -> Self {
xdr::OperationBody::CreateAccount(xdr::CreateAccountOp {
destination: cmd.destination.clone(),
starting_balance: cmd.starting_balance.into(),
Expand Down
10 changes: 8 additions & 2 deletions cmd/soroban-cli/src/commands/tx/new/manage_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ use crate::{commands::tx, xdr};
pub struct Cmd {
#[command(flatten)]
pub tx: tx::Args,
#[clap(flatten)]
pub op: Args,
}

#[derive(Debug, clap::Args, Clone)]
pub struct Args {
/// String up to 64 bytes long.
/// If this is a new Name it will add the given name/value pair to the account.
/// If this Name is already present then the associated value will be modified.
Expand All @@ -19,8 +25,8 @@ pub struct Cmd {
pub data_value: Option<xdr::BytesM<64>>,
}

impl From<&Cmd> for xdr::OperationBody {
fn from(cmd: &Cmd) -> Self {
impl From<&Args> for xdr::OperationBody {
fn from(cmd: &Args) -> Self {
let data_value = cmd.data_value.clone().map(Into::into);
let data_name = cmd.data_name.clone().into();
xdr::OperationBody::ManageData(xdr::ManageDataOp {
Expand Down
Loading

0 comments on commit a03b1d7

Please sign in to comment.