sig-proxy 0.5.0

Signature-Based (Web3) Authenticating Proxy
use crate::WrappedResult;
use async_std::println;
use ethcontract::{
  dyns::{DynMethodBuilder, DynViewMethodBuilder, DynWeb3},
  transaction::TransactionResult,
  Account, Address, Password, PrivateKey, U256,
};
use structopt::StructOpt;
use tide::http::Url;

mod dump;
mod erc1155_preset_minter_pauser;
mod erc20_preset_fixed_supply;
mod erc20_preset_minter_pauser;
mod erc721_preset_minter_pauser_auto_id;
mod erc777_preset_fixed_supply;

#[derive(Debug, StructOpt)]
#[structopt(about = "Utilities for dealing with Smart Contracts")]
pub struct Command {
  #[structopt(
    env,
    short,
    long,
    value_name = "url",
    default_value = "ws://127.0.0.1:7545"
  )]
  web3_rpc_url: Url,

  #[structopt(long, value_name = "H160", required_unless = "private-key")]
  from: Option<Address>,

  #[structopt(long, value_name = "String", conflicts_with = "private-key")]
  password: Option<String>,

  #[structopt(env, long, value_name = "HexData", conflicts_with = "password")]
  private_key: Option<PrivateKey>,

  #[structopt(long, value_name = "U64", conflicts_with = "from")]
  chain_id: Option<u64>,

  #[structopt(subcommand)]
  variant: CommandVariant,
}
#[derive(Debug, StructOpt)]
#[structopt(about = "Utilities for dealing with Smart Contracts")]
pub enum CommandVariant {
  Deploy(DeployCommand),
  Call(CallCommand),
  Send(SendCommand),
  Events(EventsCommand),
}

impl Command {
  pub async fn execute(self) -> WrappedResult<()> {
    let account = match self.private_key {
      Some(private_key) => Account::Offline(private_key, self.chain_id),
      None => match self.from {
        Some(address) => match self.password {
          Some(password) => Account::Locked(address, Password::new(password), None),
          None => Account::Local(address, None),
        },
        None => panic!("Missing either address or private key. CLI argument validation should have prevented this. (╯°□°)╯︵ ┻━┻"),
      },
    };

    let web3 = crate::util::web3_from_url(self.web3_rpc_url).await?;

    match self.variant {
      CommandVariant::Deploy(variant) => variant.execute(&web3, account).await,
      CommandVariant::Call(variant) => variant.execute(&web3, account).await,
      CommandVariant::Send(variant) => variant.execute(&web3, account).await,
      CommandVariant::Events(variant) => variant.execute(&web3, account).await,
    }
  }
}

#[derive(Debug, StructOpt)]
#[structopt(about = "Deploys a contract, returns Contract Address.")]
pub struct DeployCommand {
  #[structopt(subcommand)]
  variant: DeployVariant,
}

impl DeployCommand {
  pub async fn execute(self, web3: &DynWeb3, account: Account) -> WrappedResult<()> {
    let address = match self.variant {
      DeployVariant::ERC1155PresetMinterPauser(variant) => {
        variant.build(web3).from(account).deploy().await?.address()
      }
      DeployVariant::ERC20PresetFixedSupply(variant) => {
        variant.build(web3).from(account).deploy().await?.address()
      }
      DeployVariant::ERC20PresetMinterPauser(variant) => {
        variant.build(web3).from(account).deploy().await?.address()
      }
      DeployVariant::ERC721PresetMinterPauserAutoId(variant) => {
        variant.build(web3).from(account).deploy().await?.address()
      }
      DeployVariant::ERC777PresetFixedSupply(variant) => {
        variant.build(web3).from(account).deploy().await?.address()
      }
    };

    println!("Deployed at {:?}", address).await;

    Ok(())
  }
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "verbatim")]
pub enum DeployVariant {
  ERC1155PresetMinterPauser(erc1155_preset_minter_pauser::DeployCommand),
  ERC20PresetFixedSupply(erc20_preset_fixed_supply::DeployCommand),
  ERC20PresetMinterPauser(erc20_preset_minter_pauser::DeployCommand),
  ERC721PresetMinterPauserAutoId(erc721_preset_minter_pauser_auto_id::DeployCommand),
  ERC777PresetFixedSupply(erc777_preset_fixed_supply::DeployCommand),
}

#[derive(Debug, StructOpt)]
#[structopt(about = "Calls a read-only method of a deployed contract, returns Value.")]
pub struct CallCommand {
  #[structopt(env, long, value_name = "H160")]
  pub(crate) contract_address: Address,

  #[structopt(subcommand)]
  variant: CallVariant,
}

impl CallCommand {
  pub async fn execute(self, web3: &DynWeb3, account: Account) -> WrappedResult<()> {
    let account = account.address();
    let address = self.contract_address;

    let callable = match self.variant {
      CallVariant::ERC1155PresetMinterPauser(variant) => variant.build(web3, address),
      CallVariant::ERC20PresetFixedSupply(variant) => variant.build(web3, address),
      CallVariant::ERC20PresetMinterPauser(variant) => variant.build(web3, address),
      CallVariant::ERC721PresetMinterPauserAutoId(variant) => variant.build(web3, address),
      CallVariant::ERC777PresetFixedSupply(variant) => variant.build(web3, address),
    };

    let result = match callable {
      CallReturn::Address(method) => method
        .from(account)
        .call()
        .await
        .map(|address| format!("{:?}", address))?,
      CallReturn::Bool(method) => method.from(account).call().await?.to_string(),
      CallReturn::String(method) => method.from(account).call().await?.to_string(),
      CallReturn::U256(method) => method.from(account).call().await?.to_string(),
      CallReturn::U8(method) => method.from(account).call().await?.to_string(),
      CallReturn::VecOfAddress(method) => method
        .from(account)
        .call()
        .await?
        .into_iter()
        .map(|address| address.to_string())
        .collect::<Vec<String>>()
        .join("\n"),
      CallReturn::VecOfU256(method) => method
        .from(account)
        .call()
        .await?
        .into_iter()
        .map(|address| address.to_string())
        .collect::<Vec<String>>()
        .join("\n"),
      CallReturn::Void(method) => method
        .from(account)
        .call()
        .await
        .map(|void| format!("{:?}", void))?,
    };

    println!("{}", result).await;

    Ok(())
  }
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "verbatim")]
pub enum CallVariant {
  ERC1155PresetMinterPauser(erc1155_preset_minter_pauser::CallCommand),
  ERC20PresetFixedSupply(erc20_preset_fixed_supply::CallCommand),
  ERC20PresetMinterPauser(erc20_preset_minter_pauser::CallCommand),
  ERC721PresetMinterPauserAutoId(erc721_preset_minter_pauser_auto_id::CallCommand),
  ERC777PresetFixedSupply(erc777_preset_fixed_supply::CallCommand),
}

#[derive(Debug, StructOpt)]
#[structopt(about = "Sends a transaction to a deployed contract, returns Transaction Hash.")]
pub struct SendCommand {
  #[structopt(env, long, value_name = "H160")]
  pub(crate) contract_address: Address,

  #[structopt(subcommand)]
  variant: SendVariant,
}

impl SendCommand {
  pub async fn execute(self, web3: &DynWeb3, account: Account) -> WrappedResult<()> {
    let address = self.contract_address;

    let sendable = match self.variant {
      SendVariant::ERC1155PresetMinterPauser(variant) => variant.build(web3, address),
      SendVariant::ERC20PresetFixedSupply(variant) => variant.build(web3, address),
      SendVariant::ERC20PresetMinterPauser(variant) => variant.build(web3, address),
      SendVariant::ERC721PresetMinterPauserAutoId(variant) => variant.build(web3, address),
      SendVariant::ERC777PresetFixedSupply(variant) => variant.build(web3, address),
    };

    let result = match sendable {
      SendReturn::Bool(method) => method.from(account).send().await?,
      SendReturn::Void(method) => method.from(account).send().await?,
    };

    match result {
      TransactionResult::Hash(hash) => println!("Pending (Transaction {:?})", hash).await,
      TransactionResult::Receipt(receipt) => {
        let hash = receipt.transaction_hash;
        if let Some(status) = receipt.status {
          if status.is_zero() {
            println!("Failure (Transaction {:?})", hash).await
          } else {
            println!("Success (Transaction {:?})", hash).await
          }
        }
      }
    }

    Ok(())
  }
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "verbatim")]
pub enum SendVariant {
  ERC1155PresetMinterPauser(erc1155_preset_minter_pauser::SendCommand),
  ERC20PresetFixedSupply(erc20_preset_fixed_supply::SendCommand),
  ERC20PresetMinterPauser(erc20_preset_minter_pauser::SendCommand),
  ERC721PresetMinterPauserAutoId(erc721_preset_minter_pauser_auto_id::SendCommand),
  ERC777PresetFixedSupply(erc777_preset_fixed_supply::SendCommand),
}

#[derive(Debug, StructOpt)]
#[structopt(about = "Reads the events for a deployed contract, returns JSON.")]
pub struct EventsCommand {
  #[structopt(env, long, value_name = "H160")]
  pub(crate) contract_address: Address,

  #[structopt(long, help = "Stream future events instead of querying past events.")]
  stream: bool,

  #[structopt(subcommand)]
  variant: EventsVariant,
}

impl EventsCommand {
  pub async fn execute(self, web3: &DynWeb3, _account: Account) -> WrappedResult<()> {
    let address = self.contract_address;

    match self.variant {
      EventsVariant::ERC20PresetFixedSupply(variant) => {
        variant.execute(web3, address, self.stream).await?
      }
      EventsVariant::ERC20PresetMinterPauser(variant) => {
        variant.execute(web3, address, self.stream).await?
      }
      EventsVariant::ERC721PresetMinterPauserAutoId(variant) => {
        variant.execute(web3, address, self.stream).await?
      }
      EventsVariant::ERC777PresetFixedSupply(variant) => {
        variant.execute(web3, address, self.stream).await?
      }
      EventsVariant::ERC1155PresetMinterPauser(variant) => {
        variant.execute(web3, address, self.stream).await?
      }
    };

    Ok(())
  }
}

#[derive(Debug, StructOpt)]
#[structopt(rename_all = "verbatim")]
pub enum EventsVariant {
  ERC20PresetFixedSupply(erc20_preset_fixed_supply::EventsCommand),
  ERC20PresetMinterPauser(erc20_preset_minter_pauser::EventsCommand),
  ERC721PresetMinterPauserAutoId(erc721_preset_minter_pauser_auto_id::EventsCommand),
  ERC777PresetFixedSupply(erc777_preset_fixed_supply::EventsCommand),
  ERC1155PresetMinterPauser(erc1155_preset_minter_pauser::EventsCommand),
}

pub enum CallReturn {
  Address(DynViewMethodBuilder<Address>),
  Bool(DynViewMethodBuilder<bool>),
  String(DynViewMethodBuilder<String>),
  U256(DynViewMethodBuilder<U256>),
  U8(DynViewMethodBuilder<u8>),
  VecOfAddress(DynViewMethodBuilder<Vec<Address>>),
  VecOfU256(DynViewMethodBuilder<Vec<U256>>),
  Void(DynViewMethodBuilder<()>),
}

impl From<DynViewMethodBuilder<Address>> for CallReturn {
  fn from(builder: DynViewMethodBuilder<Address>) -> Self {
    Self::Address(builder)
  }
}

impl From<DynViewMethodBuilder<bool>> for CallReturn {
  fn from(builder: DynViewMethodBuilder<bool>) -> Self {
    Self::Bool(builder)
  }
}

impl From<DynViewMethodBuilder<String>> for CallReturn {
  fn from(builder: DynViewMethodBuilder<String>) -> Self {
    Self::String(builder)
  }
}

impl From<DynViewMethodBuilder<U256>> for CallReturn {
  fn from(builder: DynViewMethodBuilder<U256>) -> Self {
    Self::U256(builder)
  }
}

impl From<DynViewMethodBuilder<u8>> for CallReturn {
  fn from(builder: DynViewMethodBuilder<u8>) -> Self {
    Self::U8(builder)
  }
}

impl From<DynViewMethodBuilder<Vec<Address>>> for CallReturn {
  fn from(builder: DynViewMethodBuilder<Vec<Address>>) -> Self {
    Self::VecOfAddress(builder)
  }
}

impl From<DynViewMethodBuilder<Vec<U256>>> for CallReturn {
  fn from(builder: DynViewMethodBuilder<Vec<U256>>) -> Self {
    Self::VecOfU256(builder)
  }
}

impl From<DynViewMethodBuilder<()>> for CallReturn {
  fn from(builder: DynViewMethodBuilder<()>) -> Self {
    Self::Void(builder)
  }
}

impl<T> From<T> for CallReturn
where
  T: Into<SendReturn>,
{
  fn from(builder: T) -> Self {
    match builder.into() {
      SendReturn::Bool(builder) => CallReturn::Bool(builder.view()),
      SendReturn::Void(builder) => CallReturn::Void(builder.view()),
    }
  }
}

pub enum SendReturn {
  Void(DynMethodBuilder<()>),
  Bool(DynMethodBuilder<bool>),
}

impl From<DynMethodBuilder<()>> for SendReturn {
  fn from(builder: DynMethodBuilder<()>) -> Self {
    Self::Void(builder)
  }
}

impl From<DynMethodBuilder<bool>> for SendReturn {
  fn from(builder: DynMethodBuilder<bool>) -> Self {
    Self::Bool(builder)
  }
}