Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add runget subcmd #18

Merged
merged 1 commit into from
May 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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