Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce IPFS_GATEWAY environment variable #78

Merged
merged 3 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/.k8s/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ spec:
value: redis://redis.enstate.svc.cluster.local:6379
- name: UNIVERSAL_RESOLVER
value: 0x8cab227b1162f03b8338331adaad7aadc83b895e
- name: IPFS_GATEWAY
value: https://cloudflare-ipfs.com/ipfs/
resources:
requests:
cpu: 100m
Expand Down
2 changes: 2 additions & 0 deletions .github/.k8s/deploy_goerli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ spec:
value: https://rpc.ankr.com/eth_goerli,https://ethereum-goerli.publicnode.com,https://goerli.gateway.tenderly.co
- name: UNIVERSAL_RESOLVER
value: 0xfc4AC75C46C914aF5892d6d3eFFcebD7917293F1
- name: IPFS_GATEWAY
value: https://cloudflare-ipfs.com/ipfs/
resources:
requests:
cpu: 100m
Expand Down
2 changes: 2 additions & 0 deletions .github/.k8s/deploy_sepolia.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ spec:
value: https://rpc.ankr.com/eth_sepolia,https://ethereum-sepolia.publicnode.com,https://sepolia.gateway.tenderly.co
- name: UNIVERSAL_RESOLVER
value: 0xBaBC7678D7A63104f1658c11D6AE9A21cdA09725
- name: IPFS_GATEWAY,
value: https://cloudflare-ipfs.com/ipfs/
resources:
requests:
cpu: 100m
Expand Down
1 change: 1 addition & 0 deletions server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ REDIS_URL=redis://localhost:6379
RPC_URL=https://rpc.ankr.com/eth
OPENSEA_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxx
UNIVERSAL_RESOLVER=0xc0497E381f536Be9ce14B0dD3817cBcAe57d2F62
IPFS_GATEWAY=https://ipfs.io/ipfs/

# Optionally you can specify a comma-seperated list PROFILE_RECORDS, however if not provided there are sensible defaults
# PROFILE_RECORDS=com.discord,com.twitter
8 changes: 6 additions & 2 deletions server/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use enstate_shared::cache::{CacheLayer, PassthroughCacheLayer};
use ethers_core::types::H160;
use std::env;
use std::sync::Arc;

use enstate_shared::cache::{CacheLayer, PassthroughCacheLayer};
use enstate_shared::core::ENSService;
use enstate_shared::models::{
multicoin::cointype::{coins::CoinType, Coins},
records::Records,
};
use ethers_core::types::H160;
use tracing::{info, warn};

use crate::provider::RoundRobin;
Expand Down Expand Up @@ -63,6 +63,9 @@ impl AppState {
let opensea_api_key =
env::var("OPENSEA_API_KEY").expect("OPENSEA_API_KEY should've been set");

let ipfs_gateway =
env::var("IPFS_GATEWAY").unwrap_or_else(|_| "https://ipfs.io/ipfs/".to_string());

let universal_resolver = env::var("UNIVERSAL_RESOLVER")
.expect("UNIVERSAL_RESOLVER should've been set")
.parse::<H160>()
Expand All @@ -73,6 +76,7 @@ impl AppState {
cache,
rpc: Box::new(provider),
opensea_api_key,
ipfs_gateway,
profile_records: Arc::from(profile_records),
profile_chains: Arc::from(multicoin_chains),
universal_resolver,
Expand Down
1 change: 1 addition & 0 deletions shared/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub struct ENSService {
pub cache: Box<dyn crate::cache::CacheLayer>,
pub rpc: Box<dyn Factory<Arc<Provider<Http>>>>,
pub opensea_api_key: String,
pub ipfs_gateway: String,
pub profile_records: Arc<[String]>,
pub profile_chains: Arc<[CoinType]>,
pub universal_resolver: H160,
Expand Down
1 change: 1 addition & 0 deletions shared/src/core/records.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ impl ENSService {
let lookup_state = LookupState {
rpc,
opensea_api_key: self.opensea_api_key.clone(),
ipfs_gateway: self.ipfs_gateway.clone(),
};

// Assume results & calldata have the same length
Expand Down
38 changes: 26 additions & 12 deletions shared/src/models/eip155/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use thiserror::Error;
use tracing::info;

use crate::models::ipfs::{URLFetchError, OPENSEA_BASE_PREFIX};
use crate::models::lookup::LookupState;
use crate::models::multicoin::cointype::evm::ChainId;
use crate::core::CCIPProvider;

use super::ipfs::IPFSURLUnparsed;

Expand Down Expand Up @@ -56,8 +56,7 @@ pub async fn resolve_eip155(
contract_type: EIP155ContractType,
contract_address: &str,
token_id: U256,
provider: &CCIPProvider,
opensea_api_key: &str,
state: &LookupState,
) -> Result<String, EIP155Error> {
let chain_id: u64 = chain_id.into();

Expand Down Expand Up @@ -86,7 +85,7 @@ pub async fn resolve_eip155(
typed_transaction.set_to(contract_h160);
typed_transaction.set_data(Bytes::from(transaction_data));

let res = provider.provider().call_raw(&typed_transaction).await?;
let res = state.rpc.provider().call_raw(&typed_transaction).await?;

let res_data = res.to_vec();

Expand Down Expand Up @@ -115,13 +114,13 @@ pub async fn resolve_eip155(
// TODO: Validate URL here
let token_metadata_url = IPFSURLUnparsed::from_unparsed(token_metadata_url);

let token_metadata = token_metadata_url.fetch(opensea_api_key).await?;
let token_metadata = token_metadata_url.fetch(state).await?;

let image = token_metadata.image.ok_or(EIP155Error::Other)?;

info!("Image: {}", image);

let token_image_url = IPFSURLUnparsed::from_unparsed(image).to_url_or_gateway();
let token_image_url = IPFSURLUnparsed::from_unparsed(image).to_url_or_gateway(state);

Ok(token_image_url)
}
Expand All @@ -144,13 +143,18 @@ mod tests {
.wrap_into(|it| CCIPReadMiddleware::new(Arc::from(it)));
let opensea_api_key = env::var("OPENSEA_API_KEY").unwrap().to_string();

let state = LookupState {
rpc: Arc::new(provider),
opensea_api_key,
ipfs_gateway: "https://ipfs.io/ipfs/".to_string(),
};

let data = resolve_eip155(
ChainId::Ethereum,
EIP155ContractType::ERC721,
"0xc92ceddfb8dd984a89fb494c376f9a48b999aafc",
U256::from_dec_str("2257").unwrap(),
&provider,
&opensea_api_key,
&state,
)
.await
.unwrap();
Expand All @@ -165,13 +169,18 @@ mod tests {
.wrap_into(|it| CCIPReadMiddleware::new(Arc::from(it)));
let opensea_api_key = env::var("OPENSEA_API_KEY").unwrap().to_string();

let state = LookupState {
rpc: Arc::new(provider),
opensea_api_key,
ipfs_gateway: "https://ipfs.io/ipfs/".to_string(),
};

let data = resolve_eip155(
ChainId::Ethereum,
EIP155ContractType::ERC1155,
"0xb32979486938aa9694bfc898f35dbed459f44424",
U256::from_dec_str("10063").unwrap(),
&provider,
&opensea_api_key,
&state,
)
.await
.unwrap();
Expand All @@ -190,6 +199,12 @@ mod tests {
.wrap_into(|it| CCIPReadMiddleware::new(Arc::from(it)));
let opensea_api_key = env::var("OPENSEA_API_KEY").unwrap().to_string();

let state = LookupState {
rpc: Arc::new(provider),
opensea_api_key,
ipfs_gateway: "https://ipfs.io/ipfs/".to_string(),
};

let data = resolve_eip155(
ChainId::Ethereum,
EIP155ContractType::ERC1155,
Expand All @@ -198,8 +213,7 @@ mod tests {
"8112316025873927737505937898915153732580103913704334048512380490797008551937",
)
.unwrap(),
&provider,
&opensea_api_key,
&state,
)
.await
.unwrap();
Expand Down
23 changes: 10 additions & 13 deletions shared/src/models/ipfs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use lazy_static::lazy_static;
use reqwest::header::HeaderValue;
use thiserror::Error;

use crate::models::lookup::image::IPFS_REGEX;
use crate::models::lookup::LookupState;

use super::erc721::metadata::NFTMetadata;

#[derive(Debug, PartialEq)]
Expand All @@ -26,10 +29,6 @@ lazy_static! {
static ref RAW_IPFS_REGEX: regex::Regex =
regex::Regex::new(r"^Qm[1-9A-HJ-NP-Za-km-z]{44,}|b[A-Za-z2-7]{58,}|B[A-Z2-7]{58,}|z[1-9A-HJ-NP-Za-km-z]{48,}|F[0-9A-F]{50,}$")
.expect("should be a valid regex");

static ref IPFS_REGEX: regex::Regex =
regex::Regex::new(r"^ipfs://(ip[fn]s/)?([0-9a-zA-Z]+(/.*)?)")
.expect("should be a valid regex");
}

impl IPFSURLUnparsed {
Expand All @@ -49,26 +48,24 @@ impl IPFSURLUnparsed {
IPFSURLUnparsed::URL(value)
}

pub fn from_ipfs(value: String) -> Self {
Self::from_unparsed(value)
}

// This function turns the unparsed
pub fn to_url_or_gateway(&self) -> String {
pub fn to_url_or_gateway(&self, state: &LookupState) -> String {
match self {
IPFSURLUnparsed::URL(url) => url.to_string(),
IPFSURLUnparsed::IPFS(hash) => format!("https://ipfs.io/ipfs/{}", hash),
IPFSURLUnparsed::IPFS(hash) => {
format!("{gateway}/{hash}", gateway = state.ipfs_gateway)
}
}
}

pub async fn fetch(&self, opensea_api_key: &str) -> Result<NFTMetadata, URLFetchError> {
let url = self.to_url_or_gateway();
pub async fn fetch(&self, state: &LookupState) -> Result<NFTMetadata, URLFetchError> {
let url = self.to_url_or_gateway(state);
let mut client_headers = reqwest::header::HeaderMap::new();

if url.starts_with(OPENSEA_BASE_PREFIX) {
client_headers.insert(
"X-API-KEY",
HeaderValue::from_str(opensea_api_key)
HeaderValue::from_str(&state.opensea_api_key)
.unwrap_or_else(|_| HeaderValue::from_static("")),
);
}
Expand Down
19 changes: 8 additions & 11 deletions shared/src/models/lookup/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ use crate::models::multicoin::cointype::evm::ChainId;
use super::{abi_decode_universal_ccip, ENSLookupError, LookupState};

lazy_static! {
static ref IPFS_REGEX: regex::Regex =
regex::Regex::new(r"ipfs://([0-9a-zA-Z]+)").expect("should be a valid regex");
pub static ref IPFS_REGEX: regex::Regex =
regex::Regex::new(r"^ipfs://(ip[fn]s/)?([0-9a-zA-Z]+(/.*)?)")
.expect("should be a valid regex");
static ref EIP155_REGEX: regex::Regex =
regex::Regex::new(r"eip155:([0-9]+)/(erc1155|erc721):0x([0-9a-fA-F]{40})/([0-9]+)")
.expect("should be a valid regex");
}
const IPFS_GATEWAY: &str = "https://ipfs.io/ipfs/";

#[derive(Error, Debug)]
enum ImageLookupError {
#[error("Format error: {0}")]
Expand Down Expand Up @@ -54,12 +53,10 @@ pub async fn decode(data: &[u8], state: &LookupState) -> Result<String, ENSLooku
return Err(ENSLookupError::AbiDecodeError);
};

let opensea_api_key = state.opensea_api_key.clone();

if let Some(captures) = IPFS_REGEX.captures(value) {
let hash = captures.get(1).unwrap().as_str();
let hash = captures.get(2).unwrap().as_str();

return Ok(format!("{}{hash}", IPFS_GATEWAY));
return Ok(format!("{gateway}{hash}", gateway = state.ipfs_gateway));
}

let Some(captures) = EIP155_REGEX.captures(value) else {
Expand Down Expand Up @@ -104,8 +101,7 @@ pub async fn decode(data: &[u8], state: &LookupState) -> Result<String, ENSLooku
contract_type,
contract_address,
token_id,
&state.rpc,
&opensea_api_key,
state,
)
.await?;

Expand All @@ -114,9 +110,10 @@ pub async fn decode(data: &[u8], state: &LookupState) -> Result<String, ENSLooku

#[cfg(test)]
mod tests {
use crate::models::lookup::ENSLookup;
use ethers::providers::namehash;

use crate::models::lookup::ENSLookup;

#[test]
fn test_calldata_avatar() {
assert_eq!(
Expand Down
1 change: 1 addition & 0 deletions shared/src/models/lookup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ impl ENSLookup {
pub struct LookupState {
pub rpc: Arc<CCIPProvider>,
pub opensea_api_key: String,
pub ipfs_gateway: String,
}

lazy_static! {
Expand Down
12 changes: 9 additions & 3 deletions shared/src/models/multicoin/decoding/p2pkh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ impl MulticoinDecoder for P2PKHDecoder {
fn decode(&self, data: &[u8]) -> Result<String, MulticoinDecoderError> {
let bytes_len = data.len();
if bytes_len < 3 {
return Err(MulticoinDecoderError::InvalidStructure("len < 3".to_string()));
return Err(MulticoinDecoderError::InvalidStructure(
"len < 3".to_string(),
));
}

if data[..2] != [0x76, 0xa9] {
return Err(MulticoinDecoderError::InvalidStructure("invalid header".to_string()));
return Err(MulticoinDecoderError::InvalidStructure(
"invalid header".to_string(),
));
}

let len = data[2] as usize;
Expand All @@ -29,7 +33,9 @@ impl MulticoinDecoder for P2PKHDecoder {
}

if data[bytes_len - 2..bytes_len] != [0x88, 0xac] {
return Err(MulticoinDecoderError::InvalidStructure("invalid end".to_string()));
return Err(MulticoinDecoderError::InvalidStructure(
"invalid end".to_string(),
));
}

let pub_key_hash = &data[3..3 + len];
Expand Down
Loading
Loading