Skip to content

Commit

Permalink
Add runget subcmd (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
Keshoid authored May 20, 2020
1 parent a9c0f92 commit 61b538f
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 66 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ readme = "README.md"
license-file = "LICENSE.md"
keywords = ["TON", "SDK", "smart contract", "tonlabs"]
edition = "2018"
version = "0.1.4"
version = "0.1.5"

[dependencies]
base64 = "0.10.1"
Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ If `--abi` or `--sign` option is omitted in parameters it must present in config

### 3) Call Method

Call contract in blockchain:

tonos-cli call [--abi <abi_file>] [--sign <keyfile>] <address> <method> <params>

If `--abi` or `--sign` option is omitted in parameters, it must be specified in the config file. See below for more details.
Expand All @@ -88,11 +90,22 @@ Integer and address types can be supplied without quotes.
Arrays can be used without `[]` brackets.


Run get-method:
Run contract method locally:

tonos-cli run [--abi <abi_file>] <address> <method> <params>

If `--abi` or `--sign` option is omitted in parameters, it must be specified in the config file. See below for more details.
If `--abi` option is omitted in parameters, it must be specified in the config file. See below for more details.

Run funC get-method:

tonos-cli runget <address> <method> [<params>...]

`params` can have multiple values: one for each function parameter. Example:

tonos-cli runget -1:3333333333333333333333333333333333333333333333333333333333333333 compute_returned_stake 0x4107f968dc3caf85c2aa4e7d1b842d835d743855f62afe87e5862012be3eff4f

tonos-cli runget -1:3333333333333333333333333333333333333333333333333333333333333333 active_election_id


### 4) Generate signed message

Expand Down
129 changes: 75 additions & 54 deletions src/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,60 @@ fn decode_call_parameters(ton: &TonClient, msg: &EncodedMessage, abi: &str) -> R
))
}

fn parse_integer_param(value: &str) -> Result<String, String> {
let value = value.trim_matches('\"');

if value.ends_with('T') {
convert::convert_token(value.trim_end_matches('T'))
} else {
Ok(value.to_owned())
}
}

fn build_json_from_params(params_vec: Vec<&str>, abi: &str, method: &str) -> Result<String, String> {
let abi_obj = Contract::load(abi.as_bytes()).map_err(|e| format!("failed to parse ABI: {}", e))?;
let functions = abi_obj.functions();

let func_obj = functions.get(method).unwrap();
let inputs = func_obj.input_params();

let mut params_json = json!({ });
for input in inputs {
let mut iter = params_vec.iter();
let _param = iter.find(|x| x.trim_start_matches('-') == input.name)
.ok_or(format!(r#"argument "{}" of type "{}" not found"#, input.name, input.kind))?;

let value = iter.next()
.ok_or(format!(r#"argument "{}" of type "{}" has no value"#, input.name, input.kind))?
.to_string();

let value = match input.kind {
ParamType::Uint(_) | ParamType::Int(_) => {
json!(parse_integer_param(&value)?)
},
ParamType::Array(ref x) => {
if let ParamType::Uint(_) = **x {
let mut result_vec: Vec<String> = vec![];
for i in value.split(|c| c == ',' || c == '[' || c == ']') {
if i != "" {
result_vec.push(parse_integer_param(i)?)
}
}
json!(result_vec)
} else {
json!(value)
}
},
_ => {
json!(value)
}
};
params_json[input.name.clone()] = value;
}

serde_json::to_string(&params_json).map_err(|e| format!("{}", e))
}

pub fn call_contract_with_result(
conf: Config,
addr: &str,
Expand Down Expand Up @@ -271,65 +325,32 @@ pub fn call_contract_with_msg(conf: Config, str_msg: String, abi: String) -> Res
Ok(())
}

fn parse_integer_param(value: &str) -> Result<String, String> {
let value = value.trim_matches('\"');

if value.ends_with('T') {
convert::convert_token(value.trim_end_matches('T'))
} else {
Ok(value.to_owned())
}
}

fn build_json_from_params(params_vec: Vec<&str>, abi: &str, method: &str) -> Result<String, String> {
let abi_obj = Contract::load(abi.as_bytes()).map_err(|e| format!("failed to parse ABI: {}", e))?;
let functions = abi_obj.functions();

let func_obj = functions.get(method).unwrap();
let inputs = func_obj.input_params();

let mut params_json = json!({ });
for input in inputs {
let mut iter = params_vec.iter();
let _param = iter.find(|x| x.trim_start_matches('-') == input.name)
.ok_or(format!(r#"argument "{}" of type "{}" not found"#, input.name, input.kind))?;

let value = iter.next()
.ok_or(format!(r#"argument "{}" of type "{}" has no value"#, input.name, input.kind))?
.to_string();

let value = match input.kind {
ParamType::Uint(_) | ParamType::Int(_) => {
json!(parse_integer_param(&value)?)
},
ParamType::Array(ref x) => {
if let ParamType::Uint(_) = **x {
let mut result_vec: Vec<String> = vec![];
for i in value.split(|c| c == ',' || c == '[' || c == ']') {
if i != "" {
result_vec.push(parse_integer_param(i)?)
}
}
json!(result_vec)
} else {
json!(value)
}
},
_ => {
json!(value)
}
};
params_json[input.name.clone()] = value;
}

serde_json::to_string(&params_json).map_err(|e| format!("{}", e))
}

pub fn parse_params(params_vec: Vec<&str>, abi: &str, method: &str) -> Result<String, String> {
if params_vec.len() == 1 {
// if there is only 1 parameter it must be a json string with arguments
Ok(params_vec[0].to_owned())
} else {
build_json_from_params(params_vec, abi, method)
}
}

pub fn run_get_method(conf: Config, addr: &str, method: &str, params: Option<String>) -> Result<(), String> {
let ton = create_client(&conf)?;

let ton_addr = TonAddress::from_str(addr)
.map_err(|e| format!("failed to parse address: {}", e.to_string()))?;

println!("Running get-method...");
let result = ton.contracts.run_get(
Some(&ton_addr),
None,
method,
params.map(|p| p.into()),
)
.map_err(|e| format!("run failed: {}", e.to_string()))?
.output;

println!("Succeded.");
println!("Result: {}", result);
Ok(())
}
1 change: 0 additions & 1 deletion src/getconfig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,6 @@ pub fn query_global_config(conf: Config, index: &str) -> Result<(), String> {

let config_name = format!("p{}", index);

println!("Quering...");
let last_key_block_query = ton.queries.blocks.query(
"{}",
"id prev_key_block_seqno",
Expand Down
44 changes: 36 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*
* Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at: https://ton.dev/licenses
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
Expand All @@ -30,7 +29,7 @@ mod voting;
mod getconfig;

use account::get_account;
use call::{call_contract, call_contract_with_msg, generate_message, parse_params};
use call::{call_contract, call_contract_with_msg, generate_message, parse_params, run_get_method};
use clap::{ArgMatches, SubCommand, Arg, AppSettings};
use config::{Config, set_config};
use crypto::{generate_mnemonic, extract_pubkey, generate_keypair};
Expand Down Expand Up @@ -94,6 +93,21 @@ fn main_internal() -> Result <(), String> {
.help("Method arguments. Must be a list of --name value ... pairs or a json string with all arguments.")
.multiple(true));

let runget_sub_command = SubCommand::with_name("runget")
.about("Runs contract get-method.")
.setting(AppSettings::AllowLeadingHyphen)
.setting(AppSettings::TrailingVarArg)
.setting(AppSettings::DontCollapseArgsInUsage)
.arg(Arg::with_name("ADDRESS")
.required(true)
.help("Contract address."))
.arg(Arg::with_name("METHOD")
.required(true)
.help("Name of the calling method."))
.arg(Arg::with_name("PARAMS")
.help("Arguments for the contract method.")
.multiple(true));

let matches = clap_app! (tonlabs_cli =>
(version: &*format!("0.1 ({})", build_info))
(author: "TONLabs")
Expand Down Expand Up @@ -172,6 +186,7 @@ fn main_internal() -> Result <(), String> {
(@arg VERBOSE: -v --verbose "Prints additional information about command execution.")
)
(@subcommand message =>
(@setting AllowLeadingHyphen)
(about: "Generates a signed message with encoded function call.")
(author: "TONLabs")
(@arg ADDRESS: +required +takes_value "Contract address.")
Expand All @@ -184,15 +199,14 @@ fn main_internal() -> Result <(), String> {
)
(@subcommand run =>
(@setting AllowLeadingHyphen)
(about: "Runs contract's get-method.")
(version: "0.1")
(author: "TONLabs")
(about: "Runs contract function locally.")
(@arg ADDRESS: +required +takes_value "Contract address.")
(@arg METHOD: +required +takes_value conflicts_with[BODY] "Name of calling contract method.")
(@arg PARAMS: +required +takes_value conflicts_with[BODY] "Arguments for the contract method.")
(@arg ABI: --abi +takes_value conflicts_with[BODY] "Json file with contract ABI.")
(@arg METHOD: +required +takes_value "Name of calling contract method.")
(@arg PARAMS: +required +takes_value "Arguments for the contract method.")
(@arg ABI: --abi +takes_value "Json file with contract ABI.")
(@arg VERBOSE: -v --verbose "Prints additional information about command execution.")
)
(subcommand: runget_sub_command)
(@subcommand config =>
(@setting AllowLeadingHyphen)
(about: "Saves certain default values for options into config file.")
Expand Down Expand Up @@ -258,6 +272,9 @@ fn main_internal() -> Result <(), String> {
if let Some(m) = matches.subcommand_matches("run") {
return call_command(m, conf, CallType::Run);
}
if let Some(m) = matches.subcommand_matches("runget") {
return runget_command(m, conf);
}
if let Some(m) = matches.subcommand_matches("message") {
return call_command(m, conf, CallType::Msg);
}
Expand Down Expand Up @@ -449,6 +466,17 @@ fn callex_command(matches: &ArgMatches, config: Config) -> Result<(), String> {
)
}

fn runget_command(matches: &ArgMatches, config: Config) -> Result<(), String> {
let address = matches.value_of("ADDRESS");
let method = matches.value_of("METHOD");
let params = matches.values_of("PARAMS");
let params = params.map(|values| {
json!(values.collect::<Vec<_>>()).to_string()
});
print_args!(matches, address, method, params);
run_get_method(config, address.unwrap(), method.unwrap(), params)
}

fn deploy_command(matches: &ArgMatches, config: Config) -> Result<(), String> {
let tvc = matches.value_of("TVC");
let params = matches.value_of("PARAMS");
Expand Down

0 comments on commit 61b538f

Please sign in to comment.