niftygate_contract/
command.rs

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::NFTT(variant) => variant.build(web3).from(account).deploy().await?.address(),
107      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  // NFTT(nftt::DeployCommand),
125  #[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}