Skip to main content

eth_avatars/modules/ethereum/
resolver.rs

1use async_trait::async_trait;
2
3use super::{
4    ContractType, Ethereum,
5    abi::{IERC721, IERC1155},
6};
7use crate::{FetchError, Fetcher, Resource, modules::ethereum::nft::NftMetadataDecoder};
8
9use alloy::{primitives::ChainId, providers::DynProvider};
10
11/**
12* EthereumResolver resolves [`Resource::Ethereum`] to [`Resource::Decode`] for further processing.
13* It takes a [`DynProvider`] from [`alloy`].
14*/
15pub struct EthereumResolver {
16    provider: DynProvider,
17    network_id: ChainId,
18}
19
20impl EthereumResolver {
21    pub fn new(network_id: ChainId, provider: DynProvider) -> Self {
22        Self {
23            network_id,
24            provider,
25        }
26    }
27}
28
29#[async_trait]
30impl Fetcher for EthereumResolver {
31    type Locator = Ethereum;
32
33    fn accepts(&self, locator: &Ethereum) -> bool {
34        locator.network_id == self.network_id
35    }
36
37    async fn fetch(&self, locator: &Ethereum) -> Result<Resource, FetchError> {
38        let url = match locator.contract_type {
39            ContractType::ERC721 => {
40                IERC721::new(locator.contract, &self.provider)
41                    .tokenURI(locator.token_id)
42                    .call()
43                    .await
44            }
45            ContractType::ERC1155 => {
46                IERC1155::new(locator.contract, &self.provider)
47                    .uri(locator.token_id)
48                    .call()
49                    .await
50            }
51        }?;
52        let url = url.replace("{id}", &locator.token_id.to_string());
53
54        Ok(url.parse::<Resource>()?.decoded_by(NftMetadataDecoder))
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use std::str::FromStr;
61
62    use crate::{modules::http::Http, utils::test::get_test_provider};
63
64    use super::*;
65
66    #[tokio::test]
67    async fn eip155_opensea() {
68        let input: Ethereum = "eip155:1/erc1155:0x495f947276749ce646f68ac8c248420045cb7b5e/109791375735522898048150917964456965919994596086232976516654423066184641413121"
69            .parse()
70            .unwrap();
71        let provider = get_test_provider().await;
72
73        let gateway = EthereumResolver::new(1, provider);
74        let Resource::Decode { source, .. } = gateway.fetch(&input).await.unwrap() else {
75            panic!("expected a decode step");
76        };
77        let Resource::Http(url) = *source else {
78            panic!("expected an http metadata source");
79        };
80
81        assert_eq!(
82            url,
83            Http::from_str(
84                "https://api.opensea.io/api/v1/metadata/0x495f947276749Ce646f68AC8c248420045cb7b5e/0x109791375735522898048150917964456965919994596086232976516654423066184641413121"
85            )
86            .unwrap()
87        );
88    }
89}