1use std::iter::successors;
2use std::str::FromStr;
3use std::time::Duration;
4
5use async_recursion::async_recursion;
6use async_trait::async_trait;
7use ethers_core::abi::Detokenize;
8use ethers_core::types::{Address, BlockNumber, Selector, TransactionRequest, H160, U256};
9use ethers_core::{
10 abi::{self, ParamType, Token},
11 types::{transaction::eip2718::TypedTransaction, BlockId, Bytes, NameOrAddress},
12 utils::hex,
13};
14use ethers_providers::{ens, erc, Middleware, MiddlewareError};
15use futures_util::try_join;
16use hex::FromHex;
17use reqwest::Url;
18use serde_json::Value;
19
20use crate::ccip::handle_ccip;
21use crate::utils::{build_reqwest, decode_bytes, dns_encode};
22use crate::{CCIPReadMiddlewareError, CCIPRequest};
23
24#[derive(Debug, Clone)]
25pub struct CCIPReadMiddleware<M> {
26 provider: M,
27 ens: Address,
28 reqwest_client: reqwest::Client,
29 max_redirect_attempt: u8,
30}
31
32pub struct CCIPReadMiddlewareBuilder<M> {
33 provider: Option<M>,
34 ens: Option<Address>,
35 timeout: Option<Duration>,
36 max_redirect_attempt: Option<u8>,
37}
38
39impl<M> Default for CCIPReadMiddlewareBuilder<M> {
40 fn default() -> Self {
41 CCIPReadMiddlewareBuilder {
42 provider: None,
43 ens: None,
44 timeout: None,
45 max_redirect_attempt: None,
46 }
47 }
48}
49
50impl<M: Middleware> CCIPReadMiddlewareBuilder<M> {
51 pub fn with_provider(mut self, provider: M) -> Self {
52 self.provider = Some(provider);
53 self
54 }
55
56 pub fn with_timeout(mut self, timeout: Duration) -> Self {
57 self.timeout = Some(timeout);
58 self
59 }
60
61 pub fn with_max_redirect_attempt(mut self, max_redirect_attempt: u8) -> Self {
62 self.max_redirect_attempt = Some(max_redirect_attempt);
63 self
64 }
65
66 pub fn build(self) -> Result<CCIPReadMiddleware<M>, String> {
67 Ok(CCIPReadMiddleware {
68 provider: self.provider.ok_or("provider is required".to_string())?,
69 ens: self.ens.unwrap_or(ens::ENS_ADDRESS),
70 reqwest_client: build_reqwest(self.timeout.unwrap_or(Duration::from_secs(10))),
71 max_redirect_attempt: self.max_redirect_attempt.unwrap_or(10),
72 })
73 }
74}
75
76static OFFCHAIN_LOOKUP_SELECTOR: &[u8] = &[0x55, 0x6f, 0x18, 0x30];
77
78impl<M: Middleware> CCIPReadMiddleware<M> {
79 pub fn new(inner: M) -> Self {
82 Self::builder().with_provider(inner).build().unwrap()
83 }
84
85 pub fn builder() -> CCIPReadMiddlewareBuilder<M> {
86 CCIPReadMiddlewareBuilder::default()
87 }
88
89 pub async fn supports_wildcard(
101 &self,
102 resolver_address: H160,
103 ) -> Result<bool, CCIPReadMiddlewareError<M>> {
104 let data = Some(
107 "0x01ffc9a79061b92300000000000000000000000000000000000000000000000000000000"
108 .parse()
109 .unwrap(),
110 );
111
112 let _tx_request = TransactionRequest {
113 data,
114 to: Some(NameOrAddress::Address(resolver_address)),
115 ..Default::default()
116 };
117
118 let _tx_result: Result<Bytes, _> = self.call(&_tx_request.into(), None).await;
119 let _tx = match _tx_result {
120 Ok(_tx) => _tx,
121 Err(_error) => {
122 println!("Error calling: {:?}", _error);
123 Bytes::from([])
124 }
125 };
126
127 if _tx.0.is_empty() {
129 return Ok(false);
130 }
131
132 let data: U256 = decode_bytes(ParamType::Uint(256), &_tx.0)?;
134
135 Ok(data == U256::one())
137 }
138
139 async fn query_resolver<T: Detokenize>(
140 &self,
141 param: ParamType,
142 ens_name: &str,
143 selector: Selector,
144 ) -> Result<T, CCIPReadMiddlewareError<M>> {
145 self.query_resolver_parameters(param, ens_name, selector, None)
146 .await
147 }
148
149 async fn query_resolver_parameters<T: Detokenize>(
150 &self,
151 param: ParamType,
152 ens_name: &str,
153 selector: Selector,
154 parameters: Option<&[u8]>,
155 ) -> Result<T, CCIPReadMiddlewareError<M>> {
156 let resolver_address = self.get_resolver(ens_name).await?;
157
158 let mut tx: TypedTransaction =
159 ens::resolve(resolver_address, selector, ens_name, parameters).into();
160
161 let mut parse_bytes = false;
162 if self.supports_wildcard(resolver_address).await? {
163 parse_bytes = true;
164
165 let dns_encode_token = Token::Bytes(dns_encode(ens_name).unwrap());
166 let tx_data_token = Token::Bytes(tx.data().unwrap().to_vec());
167
168 let tokens = vec![dns_encode_token, tx_data_token];
169
170 let encoded_data = abi::encode(&tokens);
171
172 let resolve_selector = "9061b923";
173
174 tx.set_data(Bytes::from(
176 [hex::decode(resolve_selector).unwrap(), encoded_data].concat(),
177 ));
178 }
179
180 let mut data = self.call(&tx, None).await?;
182 if parse_bytes {
183 data = decode_bytes(ParamType::Bytes, &data)?;
184 }
185
186 Ok(decode_bytes(param, &data)?)
187 }
188
189 pub async fn get_resolver(&self, ens_name: &str) -> Result<H160, CCIPReadMiddlewareError<M>> {
190 let ens_addr = self.ens;
191
192 let names: Vec<&str> =
193 successors(Some(ens_name), |&last| last.split_once('.').map(|it| it.1)).collect();
194
195 for name in names {
196 if name.is_empty() || name.eq(".") {
197 return Ok(H160::zero());
198 }
199
200 if !ens_name.eq("eth") && name.eq("eth") {
201 return Ok(H160::zero());
202 }
203
204 let data = self
205 .call(&ens::get_resolver(ens_addr, name).into(), None)
206 .await?;
207
208 if data.0.is_empty() {
209 return Ok(H160::zero());
210 }
211
212 let resolver_address: Address = decode_bytes(ParamType::Address, &data)?;
213
214 if resolver_address != Address::zero() {
215 if name != ens_name && !self.supports_wildcard(resolver_address).await? {
216 return Ok(H160::zero());
217 }
218 return Ok(resolver_address);
219 }
220 }
221
222 Ok(H160::zero())
223 }
224
225 #[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
226 #[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
227 async fn _call(
228 &self,
229 transaction: &TypedTransaction,
230 block_id: Option<BlockId>,
231 attempt: u8,
232 requests_buffer: &mut Vec<CCIPRequest>,
233 ) -> Result<(Bytes, Vec<CCIPRequest>), CCIPReadMiddlewareError<M>> {
234 if attempt >= self.max_redirect_attempt {
235 return Err(CCIPReadMiddlewareError::MaxRedirectionError);
237 }
238
239 let tx_sender = match transaction.to().unwrap() {
240 NameOrAddress::Name(ens_name) => self.resolve_name(ens_name).await?,
241 NameOrAddress::Address(addr) => *addr,
242 };
243
244 let result = self
245 .inner()
246 .call(transaction, block_id)
247 .await
248 .or_else(|err| {
249 let Some(rpc_err) = err.as_error_response() else {
250 return Err(CCIPReadMiddlewareError::MiddlewareError(err));
251 };
252
253 let Some(Value::String(data)) = rpc_err.clone().data else {
254 return Err(CCIPReadMiddlewareError::MiddlewareError(err));
255 };
256
257 let bytes = Bytes::from_hex(data)?;
258
259 if !bytes.starts_with(OFFCHAIN_LOOKUP_SELECTOR) {
260 return Err(CCIPReadMiddlewareError::MiddlewareError(err));
261 }
262
263 Ok(bytes)
264 })?;
265
266 if !matches!(block_id.unwrap_or(BlockId::Number(BlockNumber::Latest)), BlockId::Number(block) if block.is_latest())
267 {
268 return Ok((result, requests_buffer.to_vec()));
269 }
270
271 if tx_sender.is_zero() || result.len() % 32 != 4 {
272 return Ok((result, requests_buffer.to_vec()));
273 }
274
275 let output_types = vec![
276 ParamType::Address, ParamType::Array(Box::new(ParamType::String)), ParamType::Bytes, ParamType::FixedBytes(4), ParamType::Bytes, ];
282
283 let decoded_data: Vec<Token> = abi::decode(&output_types, &result[4..])?;
284
285 #[allow(clippy::get_first)]
286 let (
287 Some(Token::Address(sender)),
288 Some(Token::Array(urls)),
289 Some(Token::Bytes(calldata)),
290 Some(Token::FixedBytes(callback_selector)),
291 Some(Token::Bytes(extra_data)),
292 ) = (
293 decoded_data.get(0),
294 decoded_data.get(1),
295 decoded_data.get(2),
296 decoded_data.get(3),
297 decoded_data.get(4),
298 )
299 else {
300 return Ok((result, requests_buffer.to_vec()));
301 };
302
303 let urls: Vec<String> = urls
304 .iter()
305 .cloned()
306 .filter_map(|t| t.into_string())
310 .collect();
311
312 if !sender.eq(&tx_sender) {
313 return Err(CCIPReadMiddlewareError::SenderError {
314 sender: format!("0x{:x}", sender),
315 });
316 }
317
318 let (ccip_result, requests) =
319 handle_ccip(&self.reqwest_client, sender, transaction, calldata, urls).await?;
320
321 requests_buffer.extend(requests);
322
323 if ccip_result.is_empty() {
324 return Err(CCIPReadMiddlewareError::GatewayNotFoundError);
325 }
326
327 let ccip_result_token = Token::Bytes(ethers_core::abi::Bytes::from(ccip_result.as_ref()));
328 let extra_data_token = Token::Bytes(extra_data.clone());
329
330 let encoded_data = abi::encode(&[ccip_result_token, extra_data_token]);
331
332 let mut callback_tx = transaction.clone();
333 callback_tx.set_data(Bytes::from(
334 [callback_selector.clone(), encoded_data.clone()].concat(),
335 ));
336
337 self._call(&callback_tx, block_id, attempt + 1, requests_buffer)
338 .await
339 }
340
341 pub async fn call_ccip(
344 &self,
345 tx: &TypedTransaction,
346 block: Option<BlockId>,
347 ) -> Result<(Bytes, Vec<CCIPRequest>), CCIPReadMiddlewareError<M>> {
348 let mut requests = Vec::new();
349 self._call(tx, block, 0, &mut requests).await
350 }
351}
352
353#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
355#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
356impl<M> Middleware for CCIPReadMiddleware<M>
357where
358 M: Middleware,
359{
360 type Error = CCIPReadMiddlewareError<M>;
361 type Provider = M::Provider;
362 type Inner = M;
363
364 fn inner(&self) -> &M {
366 &self.provider
367 }
368
369 async fn call(
371 &self,
372 tx: &TypedTransaction,
373 block: Option<BlockId>,
374 ) -> Result<Bytes, Self::Error> {
375 Ok(self.call_ccip(tx, block).await?.0)
376 }
377
378 async fn resolve_field(&self, ens_name: &str, field: &str) -> Result<String, Self::Error> {
384 let field: String = self
385 .query_resolver_parameters(
386 ParamType::String,
387 ens_name,
388 ens::FIELD_SELECTOR,
389 Some(&ens::parameterhash(field)),
390 )
391 .await?;
392 Ok(field)
393 }
394
395 async fn resolve_avatar(&self, ens_name: &str) -> Result<Url, Self::Error> {
397 let (field, owner) = try_join!(
398 self.resolve_field(ens_name, "avatar"),
399 self.resolve_name(ens_name)
400 )?;
401 let url = Url::from_str(&field)
402 .map_err(|e| CCIPReadMiddlewareError::URLParseError(e.to_string()))?;
403 match url.scheme() {
404 "https" | "data" => Ok(url),
405 "ipfs" => erc::http_link_ipfs(url).map_err(CCIPReadMiddlewareError::URLParseError),
406 "eip155" => {
407 let token = erc::ERCNFT::from_str(url.path())
408 .map_err(CCIPReadMiddlewareError::URLParseError)?;
409 match token.type_ {
410 erc::ERCNFTType::ERC721 => {
411 let tx = TransactionRequest {
412 data: Some(
413 [&erc::ERC721_OWNER_SELECTOR[..], &token.id].concat().into(),
414 ),
415 to: Some(NameOrAddress::Address(token.contract)),
416 ..Default::default()
417 };
418 let data = self.call(&tx.into(), None).await?;
419 if decode_bytes::<Address>(ParamType::Address, &data)? != owner {
420 return Err(CCIPReadMiddlewareError::NFTOwnerError(
421 "Incorrect owner.".to_string(),
422 ));
423 }
424 }
425 erc::ERCNFTType::ERC1155 => {
426 let tx = TransactionRequest {
427 data: Some(
428 [
429 &erc::ERC1155_BALANCE_SELECTOR[..],
430 &[0x0; 12],
431 &owner.0,
432 &token.id,
433 ]
434 .concat()
435 .into(),
436 ),
437 to: Some(NameOrAddress::Address(token.contract)),
438 ..Default::default()
439 };
440 let data = self.call(&tx.into(), None).await?;
441 if decode_bytes::<u64>(ParamType::Uint(64), &data)? == 0 {
442 return Err(CCIPReadMiddlewareError::NFTOwnerError(
443 "Incorrect balance.".to_string(),
444 ));
445 }
446 }
447 }
448
449 let image_url = self.resolve_nft(token).await?;
450 match image_url.scheme() {
451 "https" | "data" => Ok(image_url),
452 "ipfs" => erc::http_link_ipfs(image_url)
453 .map_err(CCIPReadMiddlewareError::URLParseError),
454 _ => Err(CCIPReadMiddlewareError::UnsupportedURLSchemeError),
455 }
456 }
457 _ => Err(CCIPReadMiddlewareError::UnsupportedURLSchemeError),
458 }
459 }
460
461 async fn resolve_name(&self, ens_name: &str) -> Result<Address, Self::Error> {
463 self.query_resolver(ParamType::Address, ens_name, ens::ADDR_SELECTOR)
464 .await
465 }
466
467 async fn lookup_address(&self, address: Address) -> Result<String, Self::Error> {
469 let ens_name = ens::reverse_address(address);
470 let domain: String = self
471 .query_resolver(ParamType::String, &ens_name, ens::NAME_SELECTOR)
472 .await?;
473 let reverse_address = self.resolve_name(&domain).await?;
474 if address != reverse_address {
475 Err(CCIPReadMiddlewareError::EnsNotOwned(domain))
476 } else {
477 Ok(domain)
478 }
479 }
480}
481
482#[cfg(test)]
483mod tests {
484 use ethers_core::types::TransactionRequest;
485 use ethers_providers::{JsonRpcError, MockResponse, Provider, MAINNET};
486
487 use super::*;
488
489 #[tokio::test]
490 async fn test_eip_2544_ens_wildcards() {
491 let provider = CCIPReadMiddleware::new(MAINNET.provider());
492
493 let ens_name = "1.offchainexample.eth";
494 let resolver_address = provider.get_resolver(ens_name).await.unwrap();
495 assert_eq!(
496 resolver_address,
497 Address::from_str("0xC1735677a60884ABbCF72295E88d47764BeDa282").unwrap(),
498 "Expected resolver_address to be 0xC1735677a60884ABbCF72295E88d47764BeDa282, but got {}",
499 resolver_address
500 );
501
502 let supports_wildcard = provider.supports_wildcard(resolver_address).await.unwrap();
503 assert!(
504 supports_wildcard,
505 "Wildcard is not supported, expected to be true"
506 );
507
508 let resolved_address = provider.resolve_name(ens_name).await.unwrap();
509 assert_eq!(
510 resolved_address,
511 Address::from_str("0x41563129cDbbD0c5D3e1c86cf9563926b243834d").unwrap(),
512 "Expected resolved_address to be 0x41563129cDbbD0c5D3e1c86cf9563926b243834d, but got {}",
513 resolved_address
514 );
515 }
516
517 #[tokio::test]
518 async fn test_ccip_call() {
519 let resolver_address = "0xC1735677a60884ABbCF72295E88d47764BeDa282";
520 let email = "nick@ens.domains";
521
522 let provider = CCIPReadMiddleware::new(MAINNET.provider());
523
524 let tx = TransactionRequest {
525 data: Some(Bytes::from(hex::decode("9061b92300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001701310f6f6666636861696e6578616d706c650365746800000000000000000000000000000000000000000000000000000000000000000000000000000000008459d1d43c1c9fb8c1fe76f464ccec6d2c003169598fdfcbcb6bbddf6af9c097a39fa0048c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000005656d61696c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap())),
529 to: Some(resolver_address.into()),
530 ..Default::default()
531 }.into();
532
533 let result = provider.call(&tx, None).await.unwrap();
534
535 let data: Bytes = decode_bytes(ParamType::Bytes, &result).unwrap();
536 let record: String = decode_bytes(ParamType::String, &data).unwrap();
537
538 assert_eq!(record, email);
539 }
540
541 #[tokio::test]
542 async fn test_mismatched_sender() {
543 let resolver_address = "0xC1735677a60884ABbCF72295E88d47764BeDa282";
544
545 let (provider, mock) = Provider::mocked();
546 let provider = CCIPReadMiddleware::new(provider);
547
548 let tx: TypedTransaction = TransactionRequest {
549 data: Some(Bytes::from(hex::decode("9061b92300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001701310f6f6666636861696e6578616d706c650365746800000000000000000000000000000000000000000000000000000000000000000000000000000000008459d1d43c1c9fb8c1fe76f464ccec6d2c003169598fdfcbcb6bbddf6af9c097a39fa0048c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000005656d61696c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap())),
553 to: Some(resolver_address.into()),
554 ..Default::default()
555 }.into();
556
557 let error_code = 3;
558 let error_data = r#""0x556f1830000000000000000000000000c1735677a60884abbcf72295e88d47764beda28300000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000160f4d4d2f80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004768747470733a2f2f6f6666636861696e2d7265736f6c7665722d6578616d706c652e75632e722e61707073706f742e636f6d2f7b73656e6465727d2f7b646174617d2e6a736f6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001449061b92300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001701310f6f6666636861696e6578616d706c650365746800000000000000000000000000000000000000000000000000000000000000000000000000000000008459d1d43c1c9fb8c1fe76f464ccec6d2c003169598fdfcbcb6bbddf6af9c097a39fa0048c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000005656d61696c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001449061b92300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001701310f6f6666636861696e6578616d706c650365746800000000000000000000000000000000000000000000000000000000000000000000000000000000008459d1d43c1c9fb8c1fe76f464ccec6d2c003169598fdfcbcb6bbddf6af9c097a39fa0048c00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000005656d61696c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000""#;
560 let error_message = "execution reverted";
561 let error = JsonRpcError {
562 code: error_code,
563 data: Some(serde_json::from_str(error_data).unwrap()),
564 message: error_message.to_string(),
565 };
566 mock.push_response(MockResponse::Error(error.clone()));
567
568 let result = provider.call(&tx, None).await;
569 assert!(result.is_err());
570 assert_eq!(
571 result.unwrap_err().to_string(),
572 format!(
573 "CCIP Read sender did not match {}",
574 "0xc1735677a60884abbcf72295e88d47764beda283"
575 )
576 );
577 }
578}