From 744a19541213783be7b77a7a93780972adaa7311 Mon Sep 17 00:00:00 2001 From: "Nikita.m" Date: Wed, 20 May 2020 03:32:35 +0300 Subject: [PATCH] Add runget subcmd --- Cargo.toml | 2 +- README.md | 17 ++++++- src/call.rs | 129 +++++++++++++++++++++++++++-------------------- src/getconfig.rs | 1 - src/main.rs | 44 +++++++++++++--- 5 files changed, 127 insertions(+), 66 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9010640e..580d9d0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/README.md b/README.md index b01a9234..8d8f3da4 100644 --- a/README.md +++ b/README.md @@ -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 ] [--sign ]
If `--abi` or `--sign` option is omitted in parameters, it must be specified in the config file. See below for more details. @@ -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 ]
-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
[...] + +`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 diff --git a/src/call.rs b/src/call.rs index 850c5652..aa7e8bd9 100644 --- a/src/call.rs +++ b/src/call.rs @@ -138,6 +138,60 @@ fn decode_call_parameters(ton: &TonClient, msg: &EncodedMessage, abi: &str) -> R )) } +fn parse_integer_param(value: &str) -> Result { + 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 { + 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 = 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(¶ms_json).map_err(|e| format!("{}", e)) +} + pub fn call_contract_with_result( conf: Config, addr: &str, @@ -271,60 +325,6 @@ pub fn call_contract_with_msg(conf: Config, str_msg: String, abi: String) -> Res Ok(()) } -fn parse_integer_param(value: &str) -> Result { - 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 { - 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 = 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(¶ms_json).map_err(|e| format!("{}", e)) -} - pub fn parse_params(params_vec: Vec<&str>, abi: &str, method: &str) -> Result { if params_vec.len() == 1 { // if there is only 1 parameter it must be a json string with arguments @@ -332,4 +332,25 @@ pub fn parse_params(params_vec: Vec<&str>, abi: &str, method: &str) -> Result) -> 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(()) } \ No newline at end of file diff --git a/src/getconfig.rs b/src/getconfig.rs index cf65c3f2..6a98ca3c 100644 --- a/src/getconfig.rs +++ b/src/getconfig.rs @@ -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", diff --git a/src/main.rs b/src/main.rs index 5656dc4c..7ce8f47f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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, @@ -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}; @@ -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") @@ -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.") @@ -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.") @@ -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); } @@ -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::>()).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");