use crate::versatus_rust::{
Address, ContractResult, FunctionInputs, SmartContract, SmartContractInputs,
SmartContractOutputs,
};
use anyhow::{anyhow, Result};
use ethnum::U256;
use serde_derive::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub enum Erc20Inputs {
Name(),
Symbol(),
Decimals(),
TotalSupply(),
BalanceOf {
address: Address,
},
Transfer {
address: Address,
value: U256,
},
TransferFrom {
from: Address,
to: Address,
value: U256,
},
Approve {
address: Address,
value: U256,
},
Allowance {
owner: Address,
spender: Address,
},
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub enum Erc20Result {
Name(String),
Symbol(String),
Decimals(u8),
TotalSupply(U256),
BalanceOf(U256),
Transfer(Erc20TransferEvent),
TransferFrom(Erc20TransferEvent),
Approve(Erc20ApprovalEvent),
Allowance(U256),
}
pub trait Erc20 {
fn name(&self) -> Result<String>;
fn symbol(&self) -> Result<String>;
fn decimals(&self) -> Result<u8>;
fn total_supply(&self) -> Result<U256>;
fn balance_of(&self, owner: Address) -> Result<U256>;
fn transfer(&self, to: Address, value: U256) -> Result<Erc20TransferEvent>;
fn transfer_from(&self, from: Address, to: Address, value: U256) -> Result<Erc20TransferEvent>;
fn approve(&self, spender: Address, value: U256) -> Result<Erc20ApprovalEvent>;
fn allowance(&self, owner: Address, spender: Address) -> Result<U256>;
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Erc20TransferEvent {
pub from: Address,
pub to: Address,
pub value: U256,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Erc20ApprovalEvent {
pub owner: Address,
pub spender: Address,
pub value: U256,
}
pub fn process_erc20<T: Erc20 + SmartContract>(contract: &mut T) -> Result<()> {
let mut input = SmartContractInputs::gather()?;
contract.receive_inputs(&mut input)?;
let result: Erc20Result;
match input.contract_input.contract_fn.as_str() {
"allowance" => {
let owner: Address;
let spender: Address;
result = {
match input.contract_input.function_inputs {
FunctionInputs::Erc20(Erc20Inputs::Allowance {
owner: in_owner,
spender: in_spender,
}) => {
owner = in_owner;
spender = in_spender;
}
_ => return Err(anyhow!("Contract inputs don't match allowance function")),
}
Erc20Result::Allowance(contract.allowance(owner, spender)?)
}
}
"approve" => {
let spender: Address;
let value: U256;
result = {
match input.contract_input.function_inputs {
FunctionInputs::Erc20(Erc20Inputs::Approve {
address: in_spender,
value: in_value,
}) => {
spender = in_spender;
value = in_value;
}
_ => return Err(anyhow!("Contract inputs don't match approve function")),
}
Erc20Result::Approve(contract.approve(spender, value)?)
}
}
"balance_of" => {
let addr: Address;
result = {
match input.contract_input.function_inputs {
FunctionInputs::Erc20(Erc20Inputs::BalanceOf { address: in_addr }) => {
addr = in_addr;
}
_ => return Err(anyhow!("Contract inputs don't match balance_of function")),
}
Erc20Result::BalanceOf(contract.balance_of(addr)?)
}
}
"total_supply" => {
result = Erc20Result::TotalSupply(contract.total_supply()?);
}
"transfer" => {
let to: Address;
let value: U256;
result = {
match input.contract_input.function_inputs {
FunctionInputs::Erc20(Erc20Inputs::Transfer {
address: in_to,
value: in_value,
}) => {
to = in_to;
value = in_value;
}
_ => return Err(anyhow!("Contract inputs don't match transfer function")),
}
Erc20Result::Transfer(contract.transfer(to, value)?)
}
}
"transfer_from" => {
let from: Address;
let to: Address;
let value: U256;
result = {
match input.contract_input.function_inputs {
FunctionInputs::Erc20(Erc20Inputs::TransferFrom {
from: in_from,
to: in_to,
value: in_value,
}) => {
from = in_from;
to = in_to;
value = in_value;
}
_ => {
return Err(anyhow!(
"Contract inputs don't match transfer_from function"
))
}
}
Erc20Result::TransferFrom(contract.transfer_from(from, to, value)?)
}
}
"name" => {
result = Erc20Result::Name(contract.name()?);
}
"symbol" => {
result = Erc20Result::Symbol(contract.symbol()?);
}
"decimals" => {
result = Erc20Result::Decimals(contract.decimals()?);
}
_ => {
return Err(anyhow!(
"Invalid contract function: {}",
&input.contract_input.contract_fn
))
}
}
let output = SmartContractOutputs {
result: vec![ContractResult::Erc20(result)],
};
output.commit()?;
Ok(())
}