Newer
Older
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
// This file is part of ink!.
// ink! is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ink! is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ink!. If not, see <http://www.gnu.org/licenses/>.
mod cmd;
#[cfg(feature = "extrinsics")]
use sp_core::{crypto::Pair, sr25519, H256};
use std::{path::PathBuf, result::Result as StdResult, str::FromStr};
use anyhow::Result;
use structopt::{clap, StructOpt};
#[derive(Debug, StructOpt)]
#[structopt(bin_name = "cargo")]
pub(crate) enum Opts {
/// Utilities to develop Wasm smart contracts.
#[structopt(name = "contract")]
#[structopt(setting = clap::AppSettings::UnifiedHelpMessage)]
#[structopt(setting = clap::AppSettings::DeriveDisplayOrder)]
#[structopt(setting = clap::AppSettings::DontCollapseArgsInUsage)]
Contract(ContractArgs),
}
#[derive(Debug, StructOpt)]
pub(crate) struct ContractArgs {
#[structopt(subcommand)]
cmd: Command,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum AbstractionLayer {
Core,
Model,
Lang,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) struct InvalidAbstractionLayer;
impl std::fmt::Display for InvalidAbstractionLayer {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "expected `core`, `model` or `lang`")
}
}
type Err = InvalidAbstractionLayer;
fn from_str(input: &str) -> StdResult<Self, Self::Err> {
match input {
"core" => Ok(AbstractionLayer::Core),
"model" => Ok(AbstractionLayer::Model),
"lang" => Ok(AbstractionLayer::Lang),
_ => Err(InvalidAbstractionLayer),
}
}
}
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub(crate) struct HexData(pub Vec<u8>);
#[cfg(feature = "extrinsics")]
impl FromStr for HexData {
type Err = hex::FromHexError;
fn from_str(input: &str) -> StdResult<Self, Self::Err> {
hex::decode(input).map(HexData)
}
}
/// Arguments required for creating and sending an extrinsic to a substrate node
#[cfg(feature = "extrinsics")]
#[derive(Debug, StructOpt)]
pub(crate) struct ExtrinsicOpts {
/// Websockets url of a substrate node
#[structopt(
name = "url",
long,
parse(try_from_str),
default_value = "ws://localhost:9944"
)]
url: url::Url,
/// Secret key URI for the account deploying the contract.
#[structopt(name = "suri", long, short)]
suri: String,
/// Password for the secret key
#[structopt(name = "password", long, short)]
password: Option<String>,
/// Maximum amount of gas to be used for this command
#[structopt(name = "gas", long, default_value = "500000")]
gas_limit: u64,
}
#[cfg(feature = "extrinsics")]
impl ExtrinsicOpts {
pub fn signer(&self) -> Result<sr25519::Pair> {
sr25519::Pair::from_string(&self.suri, self.password.as_ref().map(String::as_ref))
.map_err(|_| anyhow::anyhow!("Secret string error"))
}
}
#[derive(Debug, StructOpt)]
enum Command {
/// Setup and create a new smart contract project
#[structopt(name = "new")]
New {
/// The abstraction layer to use: `core`, `model` or `lang`
#[structopt(short = "l", long = "layer", default_value = "lang")]
layer: AbstractionLayer,
/// The name of the newly created smart contract
name: String,
/// The optional target directory for the contract project
#[structopt(short, long, parse(from_os_str))]
target_dir: Option<PathBuf>,
#[structopt(name = "build")]
Build {},
/// Generate contract metadata artifacts
#[structopt(name = "generate-metadata")]
GenerateMetadata {},
/// Test the smart contract off-chain
#[structopt(name = "test")]
Test {},
/// Upload the smart contract code to the chain
#[cfg(feature = "extrinsics")]
#[structopt(name = "deploy")]
Deploy {
#[structopt(flatten)]
extrinsic_opts: ExtrinsicOpts,
/// Path to wasm contract code, defaults to ./target/<name>-pruned.wasm
#[structopt(parse(from_os_str))]
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/// Instantiate a deployed smart contract
#[cfg(feature = "extrinsics")]
#[structopt(name = "instantiate")]
Instantiate {
#[structopt(flatten)]
extrinsic_opts: ExtrinsicOpts,
/// Transfers an initial balance to the instantiated contract
#[structopt(name = "endowment", long, default_value = "0")]
endowment: u128,
/// The hash of the smart contract code already uploaded to the chain
#[structopt(long, parse(try_from_str = parse_code_hash))]
code_hash: H256,
/// Hex encoded data to call a contract constructor
#[structopt(long)]
data: HexData,
},
}
#[cfg(feature = "extrinsics")]
fn parse_code_hash(input: &str) -> Result<H256> {
let bytes = hex::decode(input)?;
if bytes.len() != 32 {
anyhow::bail!("Code hash should be 32 bytes in length")
}
let mut arr = [0u8; 32];
arr.copy_from_slice(&bytes);
Ok(H256(arr))
Andrew Jones
committed
fn main() {
let Opts::Contract(args) = Opts::from_args();
Andrew Jones
committed
match exec(args.cmd) {
Ok(msg) => println!("\t{}", msg),
Err(err) => eprintln!("error: {}", err),
}
}
fn exec(cmd: Command) -> Result<String> {
Andrew Jones
committed
match &cmd {
Command::New {
layer,
name,
target_dir,
} => cmd::execute_new(*layer, name, target_dir.as_ref()),
Command::Build {} => cmd::execute_build(None),
Command::GenerateMetadata {} => cmd::execute_generate_metadata(None),
Command::Test {} => Err(anyhow::anyhow!("Command unimplemented")),
} => {
let code_hash = cmd::execute_deploy(extrinsic_opts, wasm_path.as_ref())?;
Ok(format!("Code hash: {:?}", code_hash))
},
#[cfg(feature = "extrinsics")]
Command::Instantiate {
extrinsic_opts,
endowment,
code_hash,
data,
} => {
let contract_account = cmd::execute_instantiate(extrinsic_opts, *endowment, *code_hash, data.clone())?;
Ok(format!("Contract account: {:?}", contract_account))
},