ethexe-ethereum 2.0.0

Ethereum contract bindings and transaction helpers for ethexe
Documentation
// Copyright (C) Gear Technologies Inc.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

use crate::{IWrappedVara, decode_log, wvara::WVaraQuery};
use alloy::{
    contract::Event,
    primitives::{Address as AlloyAddress, B256},
    providers::{Provider, RootProvider},
    rpc::types::{Filter, Log, Topic},
    sol_types::{Error, SolEvent},
};
use anyhow::Result;
use ethexe_common::{
    Address,
    events::{
        WVaraEvent,
        wvara::{ApprovalEvent, TransferEvent},
    },
};
use futures::{Stream, StreamExt};
use signatures::*;

pub mod signatures {
    use super::*;

    crate::signatures_consts! {
        IWrappedVara;
        TRANSFER: Transfer,
        APPROVAL: Approval,
    }

    pub const REQUESTS: &[B256] = &[TRANSFER];
}

pub fn try_extract_event(log: &Log) -> Result<Option<WVaraEvent>> {
    let Some(topic0) = log.topic0().filter(|&v| ALL.contains(v)) else {
        return Ok(None);
    };

    let event = match *topic0 {
        TRANSFER => WVaraEvent::Transfer(decode_log::<IWrappedVara::Transfer>(log)?.into()),
        APPROVAL => WVaraEvent::Approval(decode_log::<IWrappedVara::Approval>(log)?.into()),
        _ => unreachable!("filtered above"),
    };

    Ok(Some(event))
}

pub struct AllEventsBuilder<'a> {
    query: &'a WVaraQuery,
}

impl<'a> AllEventsBuilder<'a> {
    pub(crate) fn new(query: &'a WVaraQuery) -> Self {
        Self { query }
    }

    pub async fn subscribe(self) -> Result<impl Stream<Item = Result<WVaraEvent>> + Unpin + use<>> {
        let filter = Filter::new()
            .address(*self.query.0.address())
            .event_signature(Topic::from_iter([
                signatures::TRANSFER,
                signatures::APPROVAL,
            ]));
        Ok(self
            .query
            .0
            .provider()
            .subscribe_logs(&filter)
            .await?
            .into_stream()
            .map(|log| try_extract_event(&log).transpose().expect("infallible")))
    }
}

pub struct TransferEventBuilder<'a> {
    event: Event<&'a RootProvider, IWrappedVara::Transfer>,
    from: Option<Address>,
    to: Option<Address>,
}

impl<'a> TransferEventBuilder<'a> {
    pub(crate) fn new(query: &'a WVaraQuery) -> Self {
        Self {
            event: query.0.Transfer_filter(),
            from: None,
            to: None,
        }
    }

    pub fn from(mut self, from: Address) -> Self {
        self.from = Some(from);
        self
    }

    pub fn to(mut self, to: Address) -> Self {
        self.to = Some(to);
        self
    }

    pub async fn subscribe(
        self,
    ) -> Result<impl Stream<Item = Result<(TransferEvent, Log), Error>> + Unpin + use<>> {
        let mut event = self.event;
        if let Some(from) = self.from {
            let from: AlloyAddress = from.into();
            event = event.topic1(from);
        }
        if let Some(to) = self.to {
            let to: AlloyAddress = to.into();
            event = event.topic2(to);
        }
        Ok(event
            .subscribe()
            .await?
            .into_stream()
            .map(|result| result.map(|(event, log)| (event.into(), log))))
    }
}

pub struct ApprovalEventBuilder<'a> {
    event: Event<&'a RootProvider, IWrappedVara::Approval>,
    owner: Option<Address>,
    spender: Option<Address>,
}

impl<'a> ApprovalEventBuilder<'a> {
    pub(crate) fn new(query: &'a WVaraQuery) -> Self {
        Self {
            event: query.0.Approval_filter(),
            owner: None,
            spender: None,
        }
    }

    pub fn owner(mut self, owner: Address) -> Self {
        self.owner = Some(owner);
        self
    }

    pub fn spender(mut self, spender: Address) -> Self {
        self.spender = Some(spender);
        self
    }

    pub async fn subscribe(
        self,
    ) -> Result<impl Stream<Item = Result<(ApprovalEvent, Log), Error>> + Unpin + use<>> {
        let mut event = self.event;
        if let Some(owner) = self.owner {
            let owner: AlloyAddress = owner.into();
            event = event.topic1(owner);
        }
        if let Some(spender) = self.spender {
            let spender: AlloyAddress = spender.into();
            event = event.topic2(spender);
        }
        Ok(event
            .subscribe()
            .await?
            .into_stream()
            .map(|result| result.map(|(event, log)| (event.into(), log))))
    }
}