From b293ccb9ac007450991223aaf7d6f2177acb4eea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20F=2E=20=C5=A0?= Date: Wed, 6 Dec 2023 12:53:11 +0100 Subject: [PATCH] Introduce CCIP urls in response (#30) --- server/Cargo.lock | 12 ++-- server/Cargo.toml | 2 +- shared/Cargo.lock | 2 +- .../src/models/multicoin/decoding/binance.rs | 2 - .../models/multicoin/decoding/bitcoin_cash.rs | 4 +- .../src/models/multicoin/decoding/dogecoin.rs | 2 - .../src/models/multicoin/decoding/hedera.rs | 2 - .../src/models/multicoin/decoding/litecoin.rs | 2 - .../src/models/multicoin/decoding/monacoin.rs | 2 - .../src/models/multicoin/decoding/monero.rs | 2 - .../src/models/multicoin/decoding/polkadot.rs | 2 - .../src/models/multicoin/decoding/ripple.rs | 2 - .../src/models/multicoin/decoding/solana.rs | 2 - .../src/models/multicoin/decoding/stellar.rs | 2 - shared/src/models/multicoin/decoding/tezos.rs | 2 - shared/src/models/profile/from_name.rs | 4 +- shared/src/models/profile/mod.rs | 2 + shared/src/models/universal_resolver/mod.rs | 63 ++++++++++++++++--- shared/src/utils/mod.rs | 5 +- shared/src/utils/vec.rs | 11 ++++ worker/Cargo.lock | 1 - 21 files changed, 84 insertions(+), 44 deletions(-) create mode 100644 shared/src/utils/vec.rs diff --git a/server/Cargo.lock b/server/Cargo.lock index db6918c..8d3580f 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -972,7 +972,7 @@ dependencies = [ "crc16", "crc32fast", "ethers", - "ethers-ccip-read 0.1.1 (git+https://github.com/ensdomains/ethers-ccip-read)", + "ethers-ccip-read 0.1.1 (git+https://github.com/v3xlabs/rust-ethers-ccip-read?branch=feat/caller-ccip-requests)", "ethers-contract", "ethers-core", "getrandom", @@ -1110,34 +1110,34 @@ dependencies = [ [[package]] name = "ethers-ccip-read" version = "0.1.1" -source = "git+https://github.com/ensdomains/ethers-ccip-read?branch=main#5669e42e13bca733d34bbcfb7896a68af5ca79a2" +source = "git+https://github.com/v3xlabs/rust-ethers-ccip-read?branch=feat/caller-ccip-requests#2f52e1c144e947c31d4d80c0a1f87ec339bc02a6" dependencies = [ "async-recursion", "async-trait", "ethers-core", "ethers-providers", "futures-util", + "getrandom", "reqwest", + "serde", "serde_json", "thiserror", + "tracing", ] [[package]] name = "ethers-ccip-read" version = "0.1.1" -source = "git+https://github.com/ensdomains/ethers-ccip-read#62688ad157d3d4f3e8abccd7f48c5b7957604824" +source = "git+https://github.com/ensdomains/ethers-ccip-read?branch=main#5669e42e13bca733d34bbcfb7896a68af5ca79a2" dependencies = [ "async-recursion", "async-trait", "ethers-core", "ethers-providers", "futures-util", - "getrandom", "reqwest", - "serde", "serde_json", "thiserror", - "tracing", ] [[package]] diff --git a/server/Cargo.toml b/server/Cargo.toml index 8c49556..6866af1 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -30,7 +30,7 @@ utoipa = { version = "4.1.0", features = ["axum_extras"] } utoipa-swagger-ui = { version = "4.0.0", features = ["axum"] } redis = { version = "0.23.0", features = ["connection-manager", "tokio-comp"] } ethers-ccip-read = { git = "https://github.com/ensdomains/ethers-ccip-read", branch = "main" } -tower-http = { version = "0.4.1", features = ["cors", "tracing", "trace"] } +tower-http = { version = "0.4.4", features = ["cors", "tracing", "trace"] } rand = "0.8.5" chrono = "0.4.31" ethers-contract = "2.0.9" diff --git a/shared/Cargo.lock b/shared/Cargo.lock index cc4ef82..14f8eb0 100644 --- a/shared/Cargo.lock +++ b/shared/Cargo.lock @@ -959,7 +959,7 @@ dependencies = [ [[package]] name = "ethers-ccip-read" version = "0.1.1" -source = "git+https://github.com/ensdomains/ethers-ccip-read#62688ad157d3d4f3e8abccd7f48c5b7957604824" +source = "git+https://github.com/v3xlabs/rust-ethers-ccip-read?branch=feat/caller-ccip-requests#2f52e1c144e947c31d4d80c0a1f87ec339bc02a6" dependencies = [ "async-recursion", "async-trait", diff --git a/shared/src/models/multicoin/decoding/binance.rs b/shared/src/models/multicoin/decoding/binance.rs index c8231b3..b7f8c4b 100644 --- a/shared/src/models/multicoin/decoding/binance.rs +++ b/shared/src/models/multicoin/decoding/binance.rs @@ -12,5 +12,3 @@ impl MulticoinDecoder for BinanceDecoder { }) } } - -// TODO: tests diff --git a/shared/src/models/multicoin/decoding/bitcoin_cash.rs b/shared/src/models/multicoin/decoding/bitcoin_cash.rs index 8843b41..200647e 100644 --- a/shared/src/models/multicoin/decoding/bitcoin_cash.rs +++ b/shared/src/models/multicoin/decoding/bitcoin_cash.rs @@ -1,4 +1,4 @@ -use super::{MulticoinDecoder, MulticoinDecoderError, p2pkh::P2PKHDecoder, p2sh::P2SHDecoder}; +use super::{p2pkh::P2PKHDecoder, p2sh::P2SHDecoder, MulticoinDecoder, MulticoinDecoderError}; pub struct BitcoinCashDecoder {} @@ -15,5 +15,3 @@ impl MulticoinDecoder for BitcoinCashDecoder { Err(MulticoinDecoderError::InvalidStructure(String::new())) } } - -// TODO: tests diff --git a/shared/src/models/multicoin/decoding/dogecoin.rs b/shared/src/models/multicoin/decoding/dogecoin.rs index 7f440a6..d177395 100644 --- a/shared/src/models/multicoin/decoding/dogecoin.rs +++ b/shared/src/models/multicoin/decoding/dogecoin.rs @@ -11,5 +11,3 @@ impl MulticoinDecoder for DogecoinDecoder { } } } - -// TODO: tests diff --git a/shared/src/models/multicoin/decoding/hedera.rs b/shared/src/models/multicoin/decoding/hedera.rs index aff6e68..2a77243 100644 --- a/shared/src/models/multicoin/decoding/hedera.rs +++ b/shared/src/models/multicoin/decoding/hedera.rs @@ -15,5 +15,3 @@ impl MulticoinDecoder for HederaDecoder { Ok(format!("{shard}.{realm}.{account}")) } } - -// TODO: tests diff --git a/shared/src/models/multicoin/decoding/litecoin.rs b/shared/src/models/multicoin/decoding/litecoin.rs index c8a6c5c..c1a63ab 100644 --- a/shared/src/models/multicoin/decoding/litecoin.rs +++ b/shared/src/models/multicoin/decoding/litecoin.rs @@ -27,5 +27,3 @@ impl MulticoinDecoder for LitecoinDecoder { Err(MulticoinDecoderError::InvalidStructure(String::new())) } } - -// TODO: tests diff --git a/shared/src/models/multicoin/decoding/monacoin.rs b/shared/src/models/multicoin/decoding/monacoin.rs index 1752f66..06b2852 100644 --- a/shared/src/models/multicoin/decoding/monacoin.rs +++ b/shared/src/models/multicoin/decoding/monacoin.rs @@ -11,5 +11,3 @@ impl MulticoinDecoder for MonacoinDecoder { } } } - -// TODO: tests diff --git a/shared/src/models/multicoin/decoding/monero.rs b/shared/src/models/multicoin/decoding/monero.rs index 9936c29..0751af7 100644 --- a/shared/src/models/multicoin/decoding/monero.rs +++ b/shared/src/models/multicoin/decoding/monero.rs @@ -8,5 +8,3 @@ impl MulticoinDecoder for MoneroDecoder { Err(MulticoinDecoderError::NotSupported) } } - -// TODO: tests diff --git a/shared/src/models/multicoin/decoding/polkadot.rs b/shared/src/models/multicoin/decoding/polkadot.rs index 1207d77..b889507 100644 --- a/shared/src/models/multicoin/decoding/polkadot.rs +++ b/shared/src/models/multicoin/decoding/polkadot.rs @@ -17,5 +17,3 @@ impl MulticoinDecoder for PolkadotDecoder { .into_string()) } } - -// TODO: tests diff --git a/shared/src/models/multicoin/decoding/ripple.rs b/shared/src/models/multicoin/decoding/ripple.rs index 13ad3b0..61d1bb3 100644 --- a/shared/src/models/multicoin/decoding/ripple.rs +++ b/shared/src/models/multicoin/decoding/ripple.rs @@ -16,5 +16,3 @@ impl MulticoinDecoder for RippleDecoder { .into_string()) } } - -// TODO: tests diff --git a/shared/src/models/multicoin/decoding/solana.rs b/shared/src/models/multicoin/decoding/solana.rs index 963a8a3..c3bc685 100644 --- a/shared/src/models/multicoin/decoding/solana.rs +++ b/shared/src/models/multicoin/decoding/solana.rs @@ -7,5 +7,3 @@ impl MulticoinDecoder for SolanaDecoder { Ok(bs58::encode(data).into_string()) } } - -// TODO: tests diff --git a/shared/src/models/multicoin/decoding/stellar.rs b/shared/src/models/multicoin/decoding/stellar.rs index c0d593c..9597c83 100644 --- a/shared/src/models/multicoin/decoding/stellar.rs +++ b/shared/src/models/multicoin/decoding/stellar.rs @@ -19,5 +19,3 @@ impl MulticoinDecoder for StellarDecoder { Ok(base32::encode(Alphabet::RFC4648 { padding: false }, full.as_slice())) } } - -// TODO: tests diff --git a/shared/src/models/multicoin/decoding/tezos.rs b/shared/src/models/multicoin/decoding/tezos.rs index 348ad37..6b7666b 100644 --- a/shared/src/models/multicoin/decoding/tezos.rs +++ b/shared/src/models/multicoin/decoding/tezos.rs @@ -43,5 +43,3 @@ impl MulticoinDecoder for TezosDecoder { ) } } - -// TODO: tests diff --git a/shared/src/models/profile/from_name.rs b/shared/src/models/profile/from_name.rs index c403d0f..c41d6b2 100644 --- a/shared/src/models/profile/from_name.rs +++ b/shared/src/models/profile/from_name.rs @@ -97,7 +97,8 @@ impl Profile { // ens CCIP unwrapper is limited to 50 sub-requests, i.e. per request let calldata_chunks = calldata.chunks(50).collect::>(); - let (mut data, resolver) = resolve_universal(name, calldata_chunks[0], &rpc).await?; + let (mut data, resolver, ccip_urls) = + resolve_universal(name, calldata_chunks[0], &rpc).await?; for &chunk in &calldata_chunks[1..] { data = [data, resolve_universal(name, chunk, &rpc).await?.0].concat(); @@ -183,6 +184,7 @@ impl Profile { chains, fresh: chrono::offset::Utc::now().timestamp_millis(), resolver: EIP55Address(resolver), + ccip_urls, errors, }; diff --git a/shared/src/models/profile/mod.rs b/shared/src/models/profile/mod.rs index 356230a..6bbe454 100644 --- a/shared/src/models/profile/mod.rs +++ b/shared/src/models/profile/mod.rs @@ -31,6 +31,8 @@ pub struct Profile { pub fresh: i64, // Resolver the information was fetched from pub resolver: EIP55Address, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub ccip_urls: Vec, // Errors encountered while fetching & decoding pub errors: BTreeMap, } diff --git a/shared/src/models/universal_resolver/mod.rs b/shared/src/models/universal_resolver/mod.rs index 97bdddf..7a8d3f1 100644 --- a/shared/src/models/universal_resolver/mod.rs +++ b/shared/src/models/universal_resolver/mod.rs @@ -1,15 +1,19 @@ +use std::vec; + use ethers::prelude::ProviderError::JsonRpcClientError; use ethers::{ - providers::{namehash, Http, Middleware, Provider}, + providers::{namehash, Http, Provider}, types::{transaction::eip2718::TypedTransaction, Address, Bytes}, }; -use ethers_ccip_read::{CCIPReadMiddleware, CCIPReadMiddlewareError}; +use ethers_ccip_read::{CCIPReadMiddleware, CCIPReadMiddlewareError, CCIPRequest}; use ethers_contract::abigen; +use ethers_core::abi; use ethers_core::abi::{ParamType, Token}; use lazy_static::lazy_static; use crate::models::lookup::ENSLookup; use crate::utils::dns::dns_encode; +use crate::utils::vec::dedup_ord; use super::profile::error::ProfileError; @@ -34,7 +38,7 @@ pub async fn resolve_universal( name: &str, data: &[Box], provider: &CCIPReadMiddleware>, -) -> Result<(Vec>, Address), ProfileError> { +) -> Result<(Vec>, Address, Vec), ProfileError> { let name_hash = namehash(name); // Prepare the variables @@ -46,8 +50,7 @@ pub async fn resolve_universal( .map(Token::Bytes) .collect(); - let encoded_data = - ethers_core::abi::encode(&[Token::Bytes(dns_encoded_node), Token::Array(wildcard_data)]); + let encoded_data = abi::encode(&[Token::Bytes(dns_encoded_node), Token::Array(wildcard_data)]); // resolve(bytes node, bytes[] data) let resolve_selector = hex_literal::hex!("206c74c9").to_vec(); @@ -63,8 +66,8 @@ pub async fn resolve_universal( typed_transaction.set_data(Bytes::from(transaction_data)); // Call the transaction - let res = provider - .call(&typed_transaction, None) + let (res, ccip_requests) = provider + .call_ccip(&typed_transaction, None) .await .map_err(|err| { let CCIPReadMiddlewareError::MiddlewareError(provider_error) = err else { @@ -116,9 +119,55 @@ pub async fn resolve_universal( .map(|t| t.into_bytes().expect("result[0] elements should be bytes")) .collect(), resolver, + dedup_ord( + &ccip_requests + .iter() + .flat_map(urls_from_request) + .collect::>(), + ), )) } +fn urls_from_request(request: &CCIPRequest) -> Vec { + if request.calldata.len() < 4 { + return Vec::new(); + } + + let decoded = abi::decode( + &[ParamType::Array(Box::new(ParamType::Tuple(vec![ + ParamType::Address, + ParamType::Array(Box::new(ParamType::String)), + ParamType::Bytes, + ])))], + &request.calldata[4..], + ) + .unwrap_or_default(); + + let Some(Token::Array(requests)) = decoded.get(0) else { + return Vec::new(); + }; + + requests + .iter() + .flat_map(|request| { + let Token::Tuple(request) = request else { + return Vec::new(); + }; + + let Some(Token::Array(urls)) = request.get(1) else { + return Vec::new(); + }; + + urls.iter() + .filter_map(|url| match url { + Token::String(url) => Some(url.clone()), + _ => None, + }) + .collect::>() + }) + .collect() +} + #[cfg(test)] mod tests { use std::str::FromStr; diff --git a/shared/src/utils/mod.rs b/shared/src/utils/mod.rs index 7982878..457a5c4 100644 --- a/shared/src/utils/mod.rs +++ b/shared/src/utils/mod.rs @@ -1,3 +1,4 @@ -pub mod sha256; -pub mod eip55; pub mod dns; +pub mod eip55; +pub mod sha256; +pub mod vec; diff --git a/shared/src/utils/vec.rs b/shared/src/utils/vec.rs new file mode 100644 index 0000000..e597c17 --- /dev/null +++ b/shared/src/utils/vec.rs @@ -0,0 +1,11 @@ +use std::collections::HashSet; +use std::hash::Hash; + +pub fn dedup_ord(src: &[T]) -> Vec { + let mut set = HashSet::new(); + + let mut copy = src.to_vec(); + copy.retain(|item| set.insert(item.clone())); + + copy +} diff --git a/worker/Cargo.lock b/worker/Cargo.lock index 86ceac6..f5a3435 100644 --- a/worker/Cargo.lock +++ b/worker/Cargo.lock @@ -1006,7 +1006,6 @@ dependencies = [ [[package]] name = "ethers-ccip-read" version = "0.1.1" -source = "git+https://github.com/ensdomains/ethers-ccip-read#62688ad157d3d4f3e8abccd7f48c5b7957604824" dependencies = [ "async-recursion", "async-trait",