bevy-web3 0.1.0

Eth web3 plugin for Bevy game engine.
Documentation
use async_channel::{unbounded, Receiver, Sender, TryRecvError};
use bevy::{
    prelude::*,
    tasks::{IoTaskPool, TaskPool},
};
use web3::{transports::eip_1193, types::TransactionRequest};

pub use web3::types::{H160, H256, H520, U256};

pub enum RecvError {
    Empty,
    Closed,
}

impl From<TryRecvError> for RecvError {
    fn from(e: TryRecvError) -> RecvError {
        match e {
            TryRecvError::Empty => RecvError::Empty,
            TryRecvError::Closed => RecvError::Closed,
        }
    }
}

pub struct WalletPlugin;

impl Plugin for WalletPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Startup, init_wallet);
    }
}

#[derive(Resource)]
pub struct WalletChannel {
    account_tx: Sender<(H160, U256)>,
    account_rx: Receiver<(H160, U256)>,
    signature_tx: Sender<H520>,
    signature_rx: Receiver<H520>,
    transaction_tx: Sender<H256>,
    transaction_rx: Receiver<H256>,
}

fn init_wallet(mut commands: Commands) {
    let (account_tx, account_rx) = unbounded();
    let (signature_tx, signature_rx) = unbounded();
    let (transaction_tx, transaction_rx) = unbounded();

    commands.insert_resource(WalletChannel {
        account_tx,
        account_rx,
        signature_tx,
        signature_rx,
        transaction_tx,
        transaction_rx,
    });
}

impl WalletChannel {
    pub fn connect(&self) {
        let tx = self.account_tx.clone();
        IoTaskPool::get_or_init(TaskPool::new)
            .spawn(async move {
                let provider = eip_1193::Provider::default().unwrap().unwrap();
                let transport = eip_1193::Eip1193::new(provider);
                let web3 = web3::Web3::new(transport);

                let addrs = web3.eth().request_accounts().await.unwrap();
                let chain = web3.eth().chain_id().await.unwrap();

                if !addrs.is_empty() {
                    info!("addrs: {:?}", addrs);
                    let _ = tx.send((addrs[0], chain)).await;
                }
            })
            .detach();
    }

    pub fn sign(&self, account: H160, msg: String) {
        let tx = self.signature_tx.clone();
        IoTaskPool::get_or_init(TaskPool::new)
            .spawn(async move {
                let provider = eip_1193::Provider::default().unwrap().unwrap();
                let transport = eip_1193::Eip1193::new(provider);
                let web3 = web3::Web3::new(transport);

                let msg = web3::types::Bytes(msg.as_bytes().to_vec());
                let signature = web3.eth().sign(account, msg).await.unwrap();
                let _ = tx.send(signature).await;
            })
            .detach();
    }

    pub fn send(&self, from: H160, to: H160, data: Vec<u8>) {
        let tx = self.transaction_tx.clone();
        IoTaskPool::get_or_init(TaskPool::new)
            .spawn(async move {
                let provider = eip_1193::Provider::default().unwrap().unwrap();
                let transport = eip_1193::Eip1193::new(provider);
                let web3 = web3::Web3::new(transport);

                let mut txr = TransactionRequest::default();
                txr.from = from;
                txr.to = Some(to);
                txr.data = Some(data.into());

                let hash = web3.eth().send_transaction(txr).await.unwrap();
                let _ = tx.send(hash).await;
            })
            .detach();
    }

    pub fn recv_account(&self) -> Result<(H160, U256), RecvError> {
        Ok(self.account_rx.try_recv()?)
    }

    pub fn recv_signature(&self) -> Result<H520, RecvError> {
        Ok(self.signature_rx.try_recv()?)
    }

    pub fn recv_transaction(&self) -> Result<H256, RecvError> {
        Ok(self.transaction_rx.try_recv()?)
    }
}