1use anyhow::Result;
2use async_std::println;
3use ethcontract::{
4 dyns::{DynMethodBuilder, DynViewMethodBuilder, DynWeb3},
5 transaction::TransactionResult,
6 Account, Address, Password, PrivateKey, U256,
7};
8use structopt::StructOpt;
9use tide::http::Url;
10
11mod dump;
12mod erc1155_preset_minter_pauser;
13mod erc20_preset_fixed_supply;
14mod erc20_preset_minter_pauser;
15mod erc721_preset_minter_pauser_auto_id;
16mod erc777_preset_fixed_supply;
17mod experimental;
18mod finance;
19use experimental::token::{RoyaltyInfo, RoyaltyInfoRaw};
20
21#[derive(Debug, StructOpt)]
22#[structopt(about = "Utilities for dealing with Smart Contracts")]
23pub struct Command {
24 #[structopt(
25 env,
26 short,
27 long,
28 value_name = "url",
29 default_value = "ws://127.0.0.1:7545"
30 )]
31 web3_rpc_url: Url,
32
33 #[structopt(long, value_name = "H160", required_unless = "private-key")]
34 from: Option<Address>,
35
36 #[structopt(long, value_name = "String", conflicts_with = "private-key")]
37 password: Option<String>,
38
39 #[structopt(env, long, value_name = "HexData", conflicts_with = "password")]
40 private_key: Option<PrivateKey>,
41
42 #[structopt(long, value_name = "U64", conflicts_with = "from")]
43 chain_id: Option<u64>,
44
45 #[structopt(subcommand)]
46 variant: CommandVariant,
47}
48#[derive(Debug, StructOpt)]
49#[structopt(about = "Utilities for dealing with Smart Contracts")]
50pub enum CommandVariant {
51 Deploy(DeployCommand),
52 Call(CallCommand),
53 Send(SendCommand),
54 Events(EventsCommand),
55}
56
57impl Command {
58 pub async fn execute(self) -> Result<()> {
59 let account = match self.private_key {
60 Some(private_key) => Account::Offline(private_key, self.chain_id),
61 None => match self.from {
62 Some(address) => match self.password {
63 Some(password) => Account::Locked(address, Password::new(password), None),
64 None => Account::Local(address, None),
65 },
66 None => panic!("Missing either address or private key. CLI argument validation should have prevented this. (╯°□°)╯︵ ┻━┻"),
67 },
68 };
69
70 let web3 = crate::util::web3_from_url(self.web3_rpc_url).await?;
71
72 match self.variant {
73 CommandVariant::Deploy(variant) => variant.execute(&web3, account).await,
74 CommandVariant::Call(variant) => variant.execute(&web3, account).await,
75 CommandVariant::Send(variant) => variant.execute(&web3, account).await,
76 CommandVariant::Events(variant) => variant.execute(&web3, account).await,
77 }
78 }
79}
80
81#[derive(Debug, StructOpt)]
82#[structopt(about = "Deploys a contract, returns Contract Address.")]
83pub struct DeployCommand {
84 #[structopt(subcommand)]
85 variant: DeployVariant,
86}
87
88impl DeployCommand {
89 pub async fn execute(self, web3: &DynWeb3, account: Account) -> Result<()> {
90 let address = match self.variant {
91 DeployVariant::ERC1155PresetMinterPauser(variant) => {
92 variant.build(web3).from(account).deploy().await?.address()
93 }
94 DeployVariant::ERC20PresetFixedSupply(variant) => {
95 variant.build(web3).from(account).deploy().await?.address()
96 }
97 DeployVariant::ERC20PresetMinterPauser(variant) => {
98 variant.build(web3).from(account).deploy().await?.address()
99 }
100 DeployVariant::ERC721PresetMinterPauserAutoId(variant) => {
101 variant.build(web3).from(account).deploy().await?.address()
102 }
103 DeployVariant::ERC777PresetFixedSupply(variant) => {
104 variant.build(web3).from(account).deploy().await?.address()
105 }
106 DeployVariant::Experimental(variant) => variant.execute(web3, account).await?,
108 };
109
110 println!("Deployed at {:?}", address).await;
111
112 Ok(())
113 }
114}
115
116#[derive(Debug, StructOpt)]
117#[structopt(rename_all = "verbatim")]
118pub enum DeployVariant {
119 ERC1155PresetMinterPauser(erc1155_preset_minter_pauser::DeployCommand),
120 ERC20PresetFixedSupply(erc20_preset_fixed_supply::DeployCommand),
121 ERC20PresetMinterPauser(erc20_preset_minter_pauser::DeployCommand),
122 ERC721PresetMinterPauserAutoId(erc721_preset_minter_pauser_auto_id::DeployCommand),
123 ERC777PresetFixedSupply(erc777_preset_fixed_supply::DeployCommand),
124 #[structopt(name = "experimental", alias = "X")]
126 Experimental(experimental::DeployVariant),
127}
128
129#[derive(Debug, StructOpt)]
130#[structopt(about = "Calls a read-only method of a deployed contract, returns Value.")]
131pub struct CallCommand {
132 #[structopt(env, long, value_name = "H160")]
133 pub(crate) contract_address: Address,
134
135 #[structopt(subcommand)]
136 variant: CallVariant,
137}
138
139impl CallCommand {
140 pub async fn execute(self, web3: &DynWeb3, account: Account) -> Result<()> {
141 let account = account.address();
142 let address = self.contract_address;
143
144 let callable = match self.variant {
145 CallVariant::ERC1155PresetMinterPauser(variant) => variant.build(web3, address),
146 CallVariant::ERC20PresetFixedSupply(variant) => variant.build(web3, address),
147 CallVariant::ERC20PresetMinterPauser(variant) => variant.build(web3, address),
148 CallVariant::ERC721PresetMinterPauserAutoId(variant) => variant.build(web3, address),
149 CallVariant::ERC777PresetFixedSupply(variant) => variant.build(web3, address),
150 CallVariant::Experimental(variant) => variant.build(web3, address),
151 };
152
153 let result = match callable {
154 CallReturn::Address(method) => method
155 .from(account)
156 .call()
157 .await
158 .map(|address| format!("{:?}", address))?,
159 CallReturn::Bool(method) => method.from(account).call().await?.to_string(),
160 CallReturn::String(method) => method.from(account).call().await?.to_string(),
161 CallReturn::U256(method) => method.from(account).call().await?.to_string(),
162 CallReturn::U8(method) => method.from(account).call().await?.to_string(),
163 CallReturn::VecOfAddress(method) => method
164 .from(account)
165 .call()
166 .await?
167 .into_iter()
168 .map(|address| address.to_string())
169 .collect::<Vec<String>>()
170 .join("\n"),
171 CallReturn::VecOfU256(method) => method
172 .from(account)
173 .call()
174 .await?
175 .into_iter()
176 .map(|address| address.to_string())
177 .collect::<Vec<String>>()
178 .join("\n"),
179 CallReturn::Void(method) => method
180 .from(account)
181 .call()
182 .await
183 .map(|void| format!("{:?}", void))?,
184 CallReturn::RoyaltyInfo(method) => format!(
185 "{:?}",
186 RoyaltyInfo::from(method.from(account).call().await?)
187 ),
188 };
189
190 println!("{}", result).await;
191
192 Ok(())
193 }
194}
195
196#[derive(Debug, StructOpt)]
197#[structopt(rename_all = "verbatim")]
198pub enum CallVariant {
199 ERC1155PresetMinterPauser(erc1155_preset_minter_pauser::CallCommand),
200 ERC20PresetFixedSupply(erc20_preset_fixed_supply::CallCommand),
201 ERC20PresetMinterPauser(erc20_preset_minter_pauser::CallCommand),
202 ERC721PresetMinterPauserAutoId(erc721_preset_minter_pauser_auto_id::CallCommand),
203 ERC777PresetFixedSupply(erc777_preset_fixed_supply::CallCommand),
204 #[structopt(name = "experimental", alias = "X")]
205 Experimental(experimental::CallVariant),
206}
207
208#[derive(Debug, StructOpt)]
209#[structopt(about = "Sends a transaction to a deployed contract, returns Transaction Hash.")]
210pub struct SendCommand {
211 #[structopt(env, long, value_name = "H160")]
212 pub(crate) contract_address: Address,
213
214 #[structopt(subcommand)]
215 variant: SendVariant,
216}
217
218impl SendCommand {
219 pub async fn execute(self, web3: &DynWeb3, account: Account) -> Result<()> {
220 let address = self.contract_address;
221
222 let sendable = match self.variant {
223 SendVariant::ERC1155PresetMinterPauser(variant) => variant.build(web3, address),
224 SendVariant::ERC20PresetFixedSupply(variant) => variant.build(web3, address),
225 SendVariant::ERC20PresetMinterPauser(variant) => variant.build(web3, address),
226 SendVariant::ERC721PresetMinterPauserAutoId(variant) => variant.build(web3, address),
227 SendVariant::ERC777PresetFixedSupply(variant) => variant.build(web3, address),
228 SendVariant::Experimental(variant) => variant.build(web3, address),
229 };
230
231 let result = match sendable {
232 SendReturn::Bool(method) => method.from(account).send().await?,
233 SendReturn::Void(method) => method.from(account).send().await?,
234 SendReturn::U256(method) => method.from(account).send().await?,
235 SendReturn::Address(method) => method.from(account).send().await?,
236 };
237
238 match result {
239 TransactionResult::Hash(hash) => println!("Pending (Transaction {:?})", hash).await,
240 TransactionResult::Receipt(receipt) => {
241 let hash = receipt.transaction_hash;
242 if let Some(status) = receipt.status {
243 if status.is_zero() {
244 println!("Failure (Transaction {:?})", hash).await
245 } else {
246 println!("Success (Transaction {:?})", hash).await
247 }
248 }
249 }
250 }
251
252 Ok(())
253 }
254}
255
256#[derive(Debug, StructOpt)]
257#[structopt(rename_all = "verbatim")]
258pub enum SendVariant {
259 ERC1155PresetMinterPauser(erc1155_preset_minter_pauser::SendCommand),
260 ERC20PresetFixedSupply(erc20_preset_fixed_supply::SendCommand),
261 ERC20PresetMinterPauser(erc20_preset_minter_pauser::SendCommand),
262 ERC721PresetMinterPauserAutoId(erc721_preset_minter_pauser_auto_id::SendCommand),
263 ERC777PresetFixedSupply(erc777_preset_fixed_supply::SendCommand),
264 #[structopt(name = "experimental", alias = "X")]
265 Experimental(experimental::SendVariant),
266}
267
268#[derive(Debug, StructOpt)]
269#[structopt(about = "Reads the events for a deployed contract, returns JSON.")]
270pub struct EventsCommand {
271 #[structopt(env, long, value_name = "H160")]
272 pub(crate) contract_address: Address,
273
274 #[structopt(long, help = "Stream future events instead of querying past events.")]
275 stream: bool,
276
277 #[structopt(subcommand)]
278 variant: EventsVariant,
279}
280
281impl EventsCommand {
282 pub async fn execute(self, web3: &DynWeb3, _account: Account) -> Result<()> {
283 let address = self.contract_address;
284
285 match self.variant {
286 EventsVariant::ERC20PresetFixedSupply(variant) => {
287 variant.execute(web3, address, self.stream).await?
288 }
289 EventsVariant::ERC20PresetMinterPauser(variant) => {
290 variant.execute(web3, address, self.stream).await?
291 }
292 EventsVariant::ERC721PresetMinterPauserAutoId(variant) => {
293 variant.execute(web3, address, self.stream).await?
294 }
295 EventsVariant::ERC777PresetFixedSupply(variant) => {
296 variant.execute(web3, address, self.stream).await?
297 }
298 EventsVariant::ERC1155PresetMinterPauser(variant) => {
299 variant.execute(web3, address, self.stream).await?
300 }
301 EventsVariant::Experimental(variant) => variant.execute(web3, address, self.stream).await?,
302 };
303
304 Ok(())
305 }
306}
307
308#[derive(Debug, StructOpt)]
309#[structopt(rename_all = "verbatim")]
310pub enum EventsVariant {
311 ERC20PresetFixedSupply(erc20_preset_fixed_supply::EventsCommand),
312 ERC20PresetMinterPauser(erc20_preset_minter_pauser::EventsCommand),
313 ERC721PresetMinterPauserAutoId(erc721_preset_minter_pauser_auto_id::EventsCommand),
314 ERC777PresetFixedSupply(erc777_preset_fixed_supply::EventsCommand),
315 ERC1155PresetMinterPauser(erc1155_preset_minter_pauser::EventsCommand),
316 #[structopt(name = "experimental", alias = "X")]
317 Experimental(experimental::EventsVariant),
318}
319
320pub enum CallReturn {
321 Address(DynViewMethodBuilder<Address>),
322 Bool(DynViewMethodBuilder<bool>),
323 String(DynViewMethodBuilder<String>),
324 U256(DynViewMethodBuilder<U256>),
325 U8(DynViewMethodBuilder<u8>),
326 VecOfAddress(DynViewMethodBuilder<Vec<Address>>),
327 VecOfU256(DynViewMethodBuilder<Vec<U256>>),
328 Void(DynViewMethodBuilder<()>),
329 RoyaltyInfo(DynViewMethodBuilder<RoyaltyInfoRaw>),
330}
331
332impl From<DynViewMethodBuilder<Address>> for CallReturn {
333 fn from(builder: DynViewMethodBuilder<Address>) -> Self {
334 Self::Address(builder)
335 }
336}
337
338impl From<DynViewMethodBuilder<bool>> for CallReturn {
339 fn from(builder: DynViewMethodBuilder<bool>) -> Self {
340 Self::Bool(builder)
341 }
342}
343
344impl From<DynViewMethodBuilder<String>> for CallReturn {
345 fn from(builder: DynViewMethodBuilder<String>) -> Self {
346 Self::String(builder)
347 }
348}
349
350impl From<DynViewMethodBuilder<U256>> for CallReturn {
351 fn from(builder: DynViewMethodBuilder<U256>) -> Self {
352 Self::U256(builder)
353 }
354}
355
356impl From<DynViewMethodBuilder<u8>> for CallReturn {
357 fn from(builder: DynViewMethodBuilder<u8>) -> Self {
358 Self::U8(builder)
359 }
360}
361
362impl From<DynViewMethodBuilder<Vec<Address>>> for CallReturn {
363 fn from(builder: DynViewMethodBuilder<Vec<Address>>) -> Self {
364 Self::VecOfAddress(builder)
365 }
366}
367
368impl From<DynViewMethodBuilder<Vec<U256>>> for CallReturn {
369 fn from(builder: DynViewMethodBuilder<Vec<U256>>) -> Self {
370 Self::VecOfU256(builder)
371 }
372}
373
374impl From<DynViewMethodBuilder<()>> for CallReturn {
375 fn from(builder: DynViewMethodBuilder<()>) -> Self {
376 Self::Void(builder)
377 }
378}
379
380impl From<DynViewMethodBuilder<RoyaltyInfoRaw>> for CallReturn {
381 fn from(builder: DynViewMethodBuilder<RoyaltyInfoRaw>) -> Self {
382 Self::RoyaltyInfo(builder)
383 }
384}
385
386impl<T> From<T> for CallReturn
387where
388 T: Into<SendReturn>,
389{
390 fn from(builder: T) -> Self {
391 match builder.into() {
392 SendReturn::Bool(builder) => CallReturn::Bool(builder.view()),
393 SendReturn::Void(builder) => CallReturn::Void(builder.view()),
394 SendReturn::U256(builder) => CallReturn::U256(builder.view()),
395 SendReturn::Address(builder) => CallReturn::Address(builder.view()),
396 }
397 }
398}
399
400pub enum SendReturn {
401 Void(DynMethodBuilder<()>),
402 Bool(DynMethodBuilder<bool>),
403 U256(DynMethodBuilder<U256>),
404 Address(DynMethodBuilder<Address>),
405}
406
407impl From<DynMethodBuilder<()>> for SendReturn {
408 fn from(builder: DynMethodBuilder<()>) -> Self {
409 Self::Void(builder)
410 }
411}
412
413impl From<DynMethodBuilder<bool>> for SendReturn {
414 fn from(builder: DynMethodBuilder<bool>) -> Self {
415 Self::Bool(builder)
416 }
417}
418
419impl From<DynMethodBuilder<U256>> for SendReturn {
420 fn from(builder: DynMethodBuilder<U256>) -> Self {
421 Self::U256(builder)
422 }
423}
424
425impl From<DynMethodBuilder<Address>> for SendReturn {
426 fn from(builder: DynMethodBuilder<Address>) -> Self {
427 Self::Address(builder)
428 }
429}