forked from Concordium/concordium-rust-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathv2_contract_deploy_init_update.rs
202 lines (185 loc) · 7.18 KB
/
v2_contract_deploy_init_update.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//! Basic example that shows how to deploy, initialize, and update a smart
//! contract.
//!
//! In particular, it uses the "weather" contract which is part of the
//! [icecream example contract](https://github.com/Concordium/concordium-rust-smart-contracts/blob/main/examples/icecream/src/lib.rs).
use anyhow::Context;
use clap::AppSettings;
use concordium_rust_sdk::{
common::{self, types::TransactionTime, SerdeDeserialize, SerdeSerialize},
smart_contracts::{
common as concordium_std,
common::Amount,
types::{OwnedContractName, OwnedReceiveName},
},
types::{
smart_contracts::{ModuleReference, OwnedParameter, WasmModule},
transactions::{send, BlockItem, InitContractPayload, UpdateContractPayload},
AccountInfo, ContractAddress, WalletAccount,
},
v2,
};
use std::path::PathBuf;
use structopt::*;
use thiserror::Error;
#[derive(StructOpt)]
struct App {
#[structopt(
long = "node",
help = "GRPC interface of the node.",
default_value = "http://localhost:20000"
)]
endpoint: v2::Endpoint,
#[structopt(long = "account", help = "Path to the account key file.")]
keys_path: PathBuf,
#[structopt(subcommand, help = "The action you want to perform.")]
action: Action,
}
#[derive(StructOpt)]
enum Action {
#[structopt(about = "Deploy the module")]
Deploy {
#[structopt(long = "module", help = "Path to the contract module.")]
module_path: PathBuf,
},
#[structopt(about = "Initialize the contract with the provided weather")]
Init {
#[structopt(long, help = "The initial weather.")]
weather: Weather,
#[structopt(
long,
help = "The module reference used for initializing the contract instance."
)]
module_ref: ModuleReference,
},
#[structopt(about = "Update the contract and set the provided weather")]
Update {
#[structopt(long, help = "The new weather.")]
weather: Weather,
#[structopt(long, help = "The contract to update the weather on.")]
address: ContractAddress,
},
#[structopt(
about = "Update the contract and set the provided weather using JSON parameters and a \
schema."
)]
UpdateWithSchema {
#[structopt(long, help = "Path of the JSON parameter.")]
parameter: PathBuf,
#[structopt(long, help = "Path to the schema.")]
schema: PathBuf,
#[structopt(long, help = "The contract to update the weather on.")]
address: ContractAddress,
},
}
// The order must match the enum defined in the contract code. Otherwise, the
// serialization will be incorrect.
#[derive(SerdeSerialize, SerdeDeserialize, concordium_std::Serial, StructOpt)]
enum Weather {
Rainy,
Sunny,
}
#[derive(Debug, Error)]
#[error("invalid weather variant; expected \"rainy\" or \"sunny\", but got \"{0}\"")]
struct WeatherError(String);
impl std::str::FromStr for Weather {
type Err = WeatherError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"rainy" => Ok(Weather::Rainy),
"sunny" => Ok(Weather::Sunny),
_ => Err(WeatherError(s.to_owned())),
}
}
}
#[tokio::main(flavor = "multi_thread")]
async fn main() -> anyhow::Result<()> {
let app = {
let app = App::clap().global_setting(AppSettings::ColoredHelp);
let matches = app.get_matches();
App::from_clap(&matches)
};
let mut client = v2::Client::new(app.endpoint)
.await
.context("Cannot connect.")?;
// load account keys and sender address from a file
let keys: WalletAccount =
WalletAccount::from_json_file(app.keys_path).context("Could not read the keys file.")?;
// Get the initial nonce at the last finalized block.
let acc_info: AccountInfo = client
.get_account_info(&keys.address.into(), &v2::BlockIdentifier::Best)
.await?
.response;
let nonce = acc_info.account_nonce;
// set expiry to now + 5min
let expiry: TransactionTime =
TransactionTime::from_seconds((chrono::Utc::now().timestamp() + 300) as u64);
let tx = match app.action {
Action::Init {
weather,
module_ref: mod_ref,
} => {
let param = OwnedParameter::from_serial(&weather)
.expect("Known to not exceed parameter size limit.");
let payload = InitContractPayload {
amount: Amount::zero(),
mod_ref,
init_name: OwnedContractName::new_unchecked("init_weather".to_string()),
param,
};
send::init_contract(&keys, keys.address, nonce, expiry, payload, 10000u64.into())
}
Action::Update { weather, address } => {
let message = OwnedParameter::from_serial(&weather)
.expect("Known to not exceed parameter size limit.");
let payload = UpdateContractPayload {
amount: Amount::zero(),
address,
receive_name: OwnedReceiveName::new_unchecked("weather.set".to_string()),
message,
};
send::update_contract(&keys, keys.address, nonce, expiry, payload, 10000u64.into())
}
Action::UpdateWithSchema {
parameter,
schema,
address,
} => {
let parameter: serde_json::Value = serde_json::from_slice(
&std::fs::read(parameter).context("Unable to read parameter file.")?,
)
.context("Unable to parse parameter JSON.")?;
let schema_source = std::fs::read(schema).context("Unable to read the schema file.")?;
let schema = concordium_std::from_bytes::<concordium_std::schema::VersionedModuleSchema>(
&schema_source,
)?;
let param_schema = schema.get_receive_param_schema("weather", "set")?;
let serialized_parameter = param_schema.serial_value(¶meter)?;
let message = OwnedParameter::try_from(serialized_parameter).unwrap();
let payload = UpdateContractPayload {
amount: Amount::zero(),
address,
receive_name: OwnedReceiveName::new_unchecked("weather.set".to_string()),
message,
};
send::update_contract(&keys, keys.address, nonce, expiry, payload, 10000u64.into())
}
Action::Deploy { module_path } => {
let contents = std::fs::read(module_path).context("Could not read contract module.")?;
let payload: WasmModule =
common::Deserial::deserial(&mut std::io::Cursor::new(contents))?;
send::deploy_module(&keys, keys.address, nonce, expiry, payload)
}
};
let item = BlockItem::AccountTransaction(tx);
// submit the transaction to the chain
let transaction_hash = client.send_block_item(&item).await?;
println!(
"Transaction {} submitted (nonce = {}).",
transaction_hash, nonce,
);
let (bh, bs) = client.wait_until_finalized(&transaction_hash).await?;
println!("Transaction finalized in block {}.", bh);
println!("The outcome is {:#?}", bs);
Ok(())
}