use dioxus::document;
use gloo_timers::future::sleep;
use std::time::Duration;
use crate::{
adapters::ether_hash_adapter::EtherHashAdapter,
errors::{error::Error, ether_error::EtherError},
ports::{
ether::{
ether_accounts_port::EtherAccountsPort, ether_block_number_port::EtherBlockNumberPort,
ether_call_port::EtherCallPort, ether_chain_id_port::EtherChainIdPort,
ether_error_port::EtherErrorPort, ether_gas_price_port::EtherGasPricePort,
ether_get_balance_port::EtherGetBalancerPort,
ether_get_transaction_receipt_port::EtherGetTransactionReceiptPort,
ether_request_accounts_port::EtherRequestAccountsPort,
ether_send_transaction_port::EtherSendTransactionPort,
personal_sign_port::PersonalSignPort, wallet_is_connected_port::WalletIsConnectedPort,
wallet_is_metamask_port::WalletIsMetamaskPort,
wallet_switch_ethereum_chain_port::WalletSwitchEthereumChainPort,
},
ether_hash_port::EtherHashPort,
},
};
#[derive(serde::Serialize, serde::Deserialize)]
struct EtherWrapper<D: Default, E = Error> {
pub method: String,
pub success: bool,
pub data: Option<D>,
pub error: Option<E>,
}
pub struct EtherAdapter {
ether_hash: EtherHashAdapter,
key: String,
}
impl EtherAdapter {
pub fn new() -> Self {
Self {
ether_hash: EtherHashAdapter::new(),
key: String::from("window.dxEvalProvider"),
}
}
pub async fn wait_js_scripts(&self) -> Result<(), Error> {
loop {
let mut eval = document::eval(&format!("await dioxus.send(typeof {})", self.key));
let recv = eval.recv::<String>().await.unwrap_or_default();
if recv == "object" {
break;
}
sleep(Duration::from_millis(25)).await;
}
Ok(())
}
fn unwrap<D: Default, E: EtherErrorPort>(
&self,
target: EtherWrapper<D, E>,
) -> Result<D, Error> {
if !target.success {
let error = match target.error {
Some(r) => EtherError {
message: r.message(),
stack: r.stack(),
},
_ => EtherError::new_unknown(),
};
return Err(format!("[{}]: {}", target.method, error.message).into());
}
Ok(target.data.unwrap_or_default())
}
}
impl EtherRequestAccountsPort for EtherAdapter {
type Output = Vec<String>;
type Error = Error;
async fn eth_request_accounts(&self) -> Result<Self::Output, Self::Error> {
let mut eval = document::eval(&format!(
"await dioxus.send({}.eth_requestAccounts())",
self.key
));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
let data = self.unwrap(recv)?;
Ok(data.iter().map(|i| i.to_lowercase().to_string()).collect())
}
}
impl EtherAccountsPort for EtherAdapter {
type Output = Vec<String>;
type Error = Error;
async fn eth_accounts(&self) -> Result<Self::Output, Self::Error> {
let mut eval = document::eval(&format!("await dioxus.send({}.eth_accounts())", self.key));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
let data = self.unwrap(recv)?;
Ok(data.iter().map(|i| i.to_lowercase().to_string()).collect())
}
}
impl EtherChainIdPort for EtherAdapter {
type Output = String;
type Error = Error;
async fn eth_chain_id(&self) -> Result<Self::Output, Self::Error> {
let mut eval = document::eval(&format!("await dioxus.send({}.eth_chainId())", self.key));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
let data = self.unwrap(recv)?;
self.ether_hash.decode_to_number(&data)
}
}
impl WalletSwitchEthereumChainPort for EtherAdapter {
type Output = ();
type Error = Error;
async fn wallet_switch_ethereum_chain(
&self,
chain_id: &str,
) -> Result<Self::Output, Self::Error> {
let chain_id = self.ether_hash.encode_to_number(chain_id)?;
let mut eval = document::eval(&format!(
"await dioxus.send({}.wallet_switchEthereumChain('{}'))",
self.key, chain_id
));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
self.unwrap(recv)
}
}
impl EtherBlockNumberPort for EtherAdapter {
type Output = String;
type Error = Error;
async fn eth_block_number(&self) -> Result<Self::Output, Self::Error> {
let mut eval = document::eval(&format!(
"await dioxus.send({}.eth_blockNumber())",
self.key
));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
let data = self.unwrap(recv)?;
self.ether_hash.decode_to_number(&data)
}
}
impl EtherGasPricePort for EtherAdapter {
type Output = String;
type Error = Error;
async fn eth_gas_price(&self) -> Result<Self::Output, Self::Error> {
let mut eval = document::eval(&format!("await dioxus.send({}.eth_gasPrice())", self.key));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
let data = self.unwrap(recv)?;
self.ether_hash.decode_to_number(&data)
}
}
impl PersonalSignPort for EtherAdapter {
type Output = String;
type Error = Error;
async fn personal_sign(
&self,
message: &str,
address: &str,
) -> Result<Self::Output, Self::Error> {
let mut eval = document::eval(&format!(
"await dioxus.send({}.personal_sign('{}', '{}'))",
self.key, message, address
));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
self.unwrap(recv)
}
}
impl EtherGetBalancerPort for EtherAdapter {
type Output = String;
type Error = Error;
async fn eth_get_balance(
&self,
address: &str,
block: Option<String>,
) -> Result<Self::Output, Self::Error> {
let block = block.unwrap_or(String::from("latest"));
let mut eval = document::eval(&format!(
"await dioxus.send({}.eth_getBalance('{}', '{}'))",
self.key, address, block
));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
let data = self.unwrap(recv)?;
self.ether_hash.decode_to_number(&data)
}
}
impl EtherCallPort for EtherAdapter {
type Output = String;
type Error = Error;
async fn eth_call(
&self,
address: &str,
calldata: &str,
block: Option<String>,
) -> Result<Self::Output, Self::Error> {
let block = block.unwrap_or(String::from("latest"));
let mut eval = document::eval(&format!(
"await dioxus.send({}.eth_call('{address}', '{calldata}', '{block}'))",
self.key
));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
self.unwrap(recv)
}
}
#[derive(Default, serde::Serialize, serde::Deserialize)]
pub struct EtherTransactionReceipt {
#[serde(rename(serialize = "block_hash", deserialize = "blockHash"))]
pub block_hash: String,
#[serde(rename(serialize = "block_number", deserialize = "blockNumber"))]
pub block_number: String,
#[serde(rename(serialize = "contract_address", deserialize = "contractAddress"))]
pub contract_address: Option<String>,
#[serde(rename(serialize = "cumulative_gas_used", deserialize = "cumulativeGasUsed"))]
pub cumulative_gas_used: String,
pub from: String,
#[serde(rename(serialize = "gas_used", deserialize = "gasUsed"))]
pub gas_used: String,
#[serde(rename(serialize = "blob_gas_used", deserialize = "blobGasUsed"))]
pub blob_gas_used: Option<String>,
#[serde(rename(serialize = "effective_gas_price", deserialize = "effectiveGasPrice"))]
pub effective_gas_price: String,
#[serde(rename(serialize = "blob_gas_price", deserialize = "blobGasPrice"))]
pub blob_gas_price: Option<String>,
#[serde(rename(serialize = "logs_bloom", deserialize = "logsBloom"))]
pub logs_bloom: String,
pub status: String,
pub to: String,
#[serde(rename(serialize = "transaction_hash", deserialize = "transactionHash"))]
pub transaction_hash: String,
#[serde(rename(serialize = "transaction_index", deserialize = "transactionIndex"))]
pub transaction_index: String,
}
impl EtherGetTransactionReceiptPort for EtherAdapter {
type Output = EtherTransactionReceipt;
type Error = Error;
async fn eth_get_transaction_receipt(
&self,
transaction_hash: &str,
) -> Result<Self::Output, Self::Error> {
let mut eval = document::eval(&format!(
"await dioxus.send({}.eth_getTransactionReceipt('{transaction_hash}'))",
self.key
));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
let receipt = self.unwrap(recv)?;
Ok(receipt)
}
}
impl EtherSendTransactionPort for EtherAdapter {
type Output = String;
type Error = Error;
async fn eth_send_transaction(
&self,
from: &str,
to: &str,
value: Option<String>,
calldata: Option<String>,
gas: Option<String>,
gas_price: Option<String>,
) -> Result<Self::Output, Self::Error> {
let value = value.unwrap_or(String::new());
let calldata = calldata.unwrap_or(String::new());
let gas = gas.unwrap_or(String::new());
let gas_price = gas_price.unwrap_or(String::new());
let mut eval = document::eval(&format!(
"await dioxus.send({}.eth_sendTransaction('{from}', '{to}', '{value}', '{calldata}', '{gas}', '{gas_price}'))",
self.key
));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
let r = self.unwrap(recv);
Ok(r?)
}
}
impl WalletIsMetamaskPort for EtherAdapter {
type Output = bool;
type Error = Error;
async fn wallet_is_metamask(&self) -> Result<Self::Output, Self::Error> {
let mut eval = document::eval(&format!(
"await dioxus.send({}.wallet_isMetamask())",
self.key
));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
self.unwrap(recv)
}
}
impl WalletIsConnectedPort for EtherAdapter {
type Output = bool;
type Error = Error;
async fn wallet_is_connected(&self) -> Result<Self::Output, Self::Error> {
let mut eval = document::eval(&format!(
"await dioxus.send({}.wallet_isConnected())",
self.key
));
let recv = eval
.recv::<EtherWrapper<Self::Output, EtherError>>()
.await?;
self.unwrap(recv)
}
}