eas_sdk_rs/eas/
attest.rs

1use std::sync::Arc;
2
3use ethers::abi::{encode, Token};
4use ethers::contract::{EthLogDecode, Event};
5use ethers::providers::{Middleware, PendingTransaction, PubsubClient};
6
7use crate::contracts::eas::{
8    Attestation, AttestationRequest, AttestedFilter, MultiAttestationRequest,
9};
10use ethers::types::{BlockNumber, H256};
11
12use crate::contracts::AttestationData;
13
14use ::ethers::core::types::{Address, Bytes, U256};
15use futures::stream::BoxStream;
16use futures::{Stream, StreamExt};
17
18use tracing::debug;
19
20use crate::eas::{Error, EAS};
21use crate::transaction::{Error as TxError, PendingTx};
22
23pub struct AttestationDataBuilder {
24    pub recipient: Option<Address>,
25    pub expiration_time: Option<u64>,
26    pub revocable: bool,
27    pub ref_uid: Option<[u8; 32]>,
28    pub data: Option<Bytes>,
29    pub value: Option<U256>,
30}
31
32#[derive(Debug)]
33pub struct Attested {
34    pub recipient: Address,
35    pub attester: Address,
36    pub uid: H256,
37    pub schema: H256,
38}
39
40pub struct MultiAttestRequest {
41    pub schema: H256,
42    pub data: Vec<AttestationData>,
43}
44
45impl From<AttestedFilter> for Attested {
46    fn from(event: AttestedFilter) -> Self {
47        Self {
48            recipient: event.recipient,
49            attester: event.attester,
50            uid: H256::from_slice(&event.uid),
51            schema: H256::from_slice(&event.schema),
52        }
53    }
54}
55
56impl Default for AttestationDataBuilder {
57    fn default() -> Self {
58        Self::new()
59    }
60}
61
62impl AttestationDataBuilder {
63    pub fn new() -> Self {
64        Self {
65            recipient: None,
66            expiration_time: None,
67            revocable: false,
68            ref_uid: None,
69            data: None,
70            value: None,
71        }
72    }
73
74    pub fn recipient(mut self, recipient: Address) -> Self {
75        self.recipient = Some(recipient);
76        self
77    }
78
79    pub fn expiration_time(mut self, expiration_time: u64) -> Self {
80        self.expiration_time = Some(expiration_time);
81        self
82    }
83
84    pub fn revocable(mut self, revocable: bool) -> Self {
85        self.revocable = revocable;
86        self
87    }
88
89    pub fn ref_uid(mut self, ref_uid: [u8; 32]) -> Self {
90        self.ref_uid = Some(ref_uid);
91        self
92    }
93
94    pub fn data(mut self, data: &[Token]) -> Self {
95        self.data = Some(encode(data).into());
96        self
97    }
98
99    pub fn value(mut self, value: U256) -> Self {
100        self.value = Some(value);
101        self
102    }
103
104    pub fn build(self) -> AttestationData {
105        AttestationData {
106            recipient: self.recipient.unwrap_or(Address::zero()),
107            expiration_time: self.expiration_time.unwrap_or(0),
108            revocable: self.revocable,
109            ref_uid: self.ref_uid.unwrap_or([0; 32]),
110            data: self.data.unwrap_or_default(),
111            value: self.value.unwrap_or(U256::zero()),
112        }
113    }
114}
115
116impl<M: Middleware + 'static> EAS<M> {
117    pub async fn attest(
118        &self,
119        schema: &H256,
120        data: AttestationData,
121    ) -> Result<PendingTx<Attested>, Error<M>> {
122        let attestation_request = AttestationRequest {
123            schema: schema.to_fixed_bytes(),
124            data,
125        };
126
127        let binding = self.contract.attest(attestation_request);
128        let tx = binding.send().await?;
129
130        debug!("Transaction sent: {:?}", tx.tx_hash());
131
132        // PendingTransaction is not copy and it is dropped there, but it is recreated from tx_hash and provider
133        let tx_hash = tx.tx_hash();
134        let provider = self.contract.client().clone();
135
136        let future = async move {
137            let tx = PendingTransaction::new(tx_hash, provider.provider());
138            let receipt = tx.await?;
139            let receipt = receipt.ok_or_else(|| TxError::TransactionDropped(tx_hash))?;
140
141            debug!("Receipt: {:?}", receipt);
142
143            let log = receipt.logs.first().ok_or_else(|| TxError::MissingLogs)?;
144
145            let attested = AttestedFilter::decode_log(&log.clone().into())?;
146
147            Ok(attested.into())
148        };
149
150        Ok(PendingTx::new(future))
151    }
152
153    pub async fn get_attestation(&self, attestation_id: &H256) -> Result<Attestation, Error<M>> {
154        let binding = self
155            .contract
156            .get_attestation(attestation_id.to_fixed_bytes());
157
158        let attestation_data = binding.call().await?;
159        Ok(attestation_data)
160    }
161
162    pub async fn is_attestation_valid(&self, attestation_id: &H256) -> Result<bool, Error<M>> {
163        let binding = self
164            .contract
165            .is_attestation_valid(attestation_id.to_fixed_bytes());
166
167        let is_valid = binding.call().await?;
168        Ok(is_valid)
169    }
170
171    pub async fn multi_attest(
172        &self,
173        request: Vec<MultiAttestRequest>,
174    ) -> Result<PendingTx<Vec<Result<Attested, TxError>>>, Error<M>> {
175        let attestation_requests = request
176            .iter()
177            .map(|req| MultiAttestationRequest {
178                schema: req.schema.to_fixed_bytes(),
179                data: req.data.clone(),
180            })
181            .collect();
182
183        let binding = self.contract.multi_attest(attestation_requests);
184        let tx = binding.send().await?;
185
186        debug!("Transaction sent: {:?}", tx.tx_hash());
187
188        // PendingTransaction is not copy and it is dropped there, but it is recreated from tx_hash and provider
189        let tx_hash = tx.tx_hash();
190        let provider = self.contract.client().clone();
191
192        let future = async move {
193            let tx = PendingTransaction::new(tx_hash, provider.provider());
194            let receipt = tx.await?;
195            let receipt = receipt.ok_or_else(|| TxError::TransactionDropped(tx_hash))?;
196
197            debug!("Receipt: {:?}", receipt);
198
199            let attested: Vec<Result<Attested, TxError>> = receipt
200                .logs
201                .iter()
202                .map(|log| {
203                    AttestedFilter::decode_log(&log.clone().into())
204                        .map(Into::into)
205                        .map_err(TxError::from)
206                })
207                .collect();
208
209            Ok(attested)
210        };
211
212        Ok(PendingTx::new(future))
213    }
214
215    pub fn attested_event<T>(
216        &self,
217        start_block: T,
218        end_block: Option<T>,
219        uids: Option<Vec<H256>>,
220    ) -> AttestedEvent<M>
221    where
222        T: Into<BlockNumber>,
223    {
224        let mut event = self.contract.attested_filter().from_block(start_block);
225
226        if let Some(end_block) = end_block {
227            event = event.to_block(end_block);
228        }
229
230        if let Some(uids) = uids {
231            event = event.topic0(uids);
232        }
233
234        AttestedEvent::new(event)
235    }
236}
237
238pub struct AttestedEvent<M: Middleware + 'static> {
239    inner: Event<Arc<M>, M, AttestedFilter>,
240}
241
242impl<M: Middleware> AttestedEvent<M> {
243    pub fn new(filter: Event<Arc<M>, M, AttestedFilter>) -> Self {
244        Self { inner: filter }
245    }
246
247    pub async fn stream(&self) -> Result<BoxStream<Result<Attested, Error<M>>>, Error<M>> {
248        let stream = self
249            .inner
250            .stream()
251            .await
252            .map_err(Error::ContractError)?
253            .map(|e| Ok(Attested::from(e?)));
254        Ok(stream.boxed())
255    }
256
257    pub async fn query(&self) -> Result<Vec<Attested>, Error<M>> {
258        let logs = self
259            .inner
260            .query()
261            .await
262            .map_err(Error::ContractError)?
263            .iter()
264            .map(|e| Attested::from(e.clone()))
265            .collect();
266
267        Ok(logs)
268    }
269}
270
271impl<M: Middleware> AttestedEvent<M>
272where
273    M::Provider: PubsubClient,
274{
275    pub async fn subscribe(
276        &self,
277    ) -> Result<impl Stream<Item = Result<Attested, Error<M>>> + '_, Error<M>> {
278        let stream = self
279            .inner
280            .subscribe()
281            .await
282            .map_err(Error::ContractError)?
283            .map(|e| Ok(Attested::from(e?)));
284        Ok(stream)
285    }
286}