-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
774 additions
and
460 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
use ethers_core::{ | ||
abi::{ParamType, Token}, | ||
types::H256, | ||
}; | ||
use hex_literal::hex; | ||
|
||
use super::{ENSLookup, ENSLookupError}; | ||
|
||
pub struct Addr {} | ||
|
||
impl ENSLookup for Addr { | ||
fn calldata(&self, namehash: &H256) -> Vec<u8> { | ||
let fn_selector = hex!("3b3b57de").to_vec(); | ||
|
||
let data = | ||
ethers_core::abi::encode(&[Token::FixedBytes(namehash.as_fixed_bytes().to_vec())]); | ||
|
||
[fn_selector, data].concat() | ||
} | ||
|
||
fn decode(&self, data: &[u8]) -> Result<String, ENSLookupError> { | ||
let decoded_abi = ethers_core::abi::decode(&[ParamType::Address], data) | ||
.map_err(|_| ENSLookupError::AbiError)?; | ||
let address = decoded_abi | ||
.get(0) | ||
.ok_or(ENSLookupError::AbiError)? | ||
.clone() | ||
.into_address() | ||
.ok_or(ENSLookupError::InvalidPayload("yup".to_string()))?; | ||
|
||
Ok(format!("{address:?}")) | ||
} | ||
|
||
fn name(&self) -> String { | ||
"addr".to_string() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use ethers::providers::namehash; | ||
use hex_literal::hex; | ||
|
||
#[tokio::test] | ||
async fn test_calldata_address() { | ||
assert_eq!( | ||
Addr {}.calldata(&namehash("eth")), | ||
hex!("3b3b57de93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae") | ||
); | ||
|
||
assert_eq!( | ||
Addr {}.calldata(&namehash("foo.eth")), | ||
hex!("3b3b57dede9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f") | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
use super::{ENSLookup, ENSLookupError}; | ||
|
||
use ethers_core::{ | ||
abi::{ParamType, Token}, | ||
types::H256, | ||
}; | ||
use hex_literal::hex; | ||
use tracing::info; | ||
|
||
pub struct Avatar { | ||
pub ipfs_gateway: String, | ||
pub name: String, | ||
} | ||
|
||
impl Avatar {} | ||
|
||
impl ENSLookup for Avatar { | ||
fn calldata(&self, namehash: &H256) -> Vec<u8> { | ||
let fn_selector = hex!("59d1d43c").to_vec(); | ||
|
||
let data = ethers_core::abi::encode(&[ | ||
Token::FixedBytes(namehash.as_fixed_bytes().to_vec()), | ||
Token::String("avatar".to_string()), | ||
]); | ||
|
||
[fn_selector, data].concat() | ||
} | ||
|
||
fn decode(&self, data: &[u8]) -> Result<String, ENSLookupError> { | ||
let decoded_abi = ethers_core::abi::decode(&[ParamType::String], data) | ||
.map_err(|_| ENSLookupError::AbiError)?; | ||
let value = decoded_abi.get(0).ok_or(ENSLookupError::AbiError)?; | ||
let value = value.to_string(); | ||
|
||
// If IPFS | ||
let ipfs = regex::Regex::new(r"ipfs://([0-9a-zA-Z]+)").unwrap(); | ||
if let Some(captures) = ipfs.captures(&value) { | ||
let hash = captures.get(1).unwrap().as_str(); | ||
|
||
return Ok(format!("{}{hash}", self.ipfs_gateway)); | ||
} | ||
|
||
// If the raw value is eip155 url | ||
let eip155 = | ||
regex::Regex::new(r"eip155:([0-9]+)/(erc1155|erc712):0x([0-9a-fA-F]{40})/([0-9]+)") | ||
.unwrap(); | ||
|
||
if let Some(captures) = eip155.captures(&value) { | ||
let chain_id = captures.get(1).unwrap().as_str(); | ||
let contract_type = captures.get(2).unwrap().as_str(); | ||
let contract_address = captures.get(3).unwrap().as_str(); | ||
let token_id = captures.get(4).unwrap().as_str(); | ||
|
||
info!( | ||
"Encountered Avatar: {chain_id} {contract_type} {contract_address} {token_id}", | ||
chain_id = chain_id, | ||
contract_type = contract_type, | ||
contract_address = contract_address, | ||
token_id = token_id | ||
); | ||
|
||
// TODO: Remove naive approach | ||
return Ok(format!( | ||
"https://metadata.ens.domains/mainnet/avatar/{}", | ||
self.name | ||
)); | ||
} | ||
|
||
Ok(value) | ||
} | ||
|
||
fn name(&self) -> String { | ||
"avatar".to_string() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use ethers::providers::namehash; | ||
|
||
#[tokio::test] | ||
async fn test_calldata_avatar() { | ||
assert_eq!( | ||
Avatar{ | ||
ipfs_gateway: "https://ipfs.io/ipfs/".to_string(), | ||
name: "luc.eth".to_string(), | ||
}.calldata(&namehash("luc.eth")), | ||
hex_literal::hex!("59d1d43ce1e7bcf2ca33c28a806ee265cfedf02fedf1b124ca73b2203ca80cc7c91a02ad000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000066176617461720000000000000000000000000000000000000000000000000000") | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use ethers_core::types::H256; | ||
use thiserror::Error; | ||
|
||
pub mod addr; | ||
pub mod avatar; | ||
pub mod multicoin; | ||
pub mod text; | ||
|
||
#[derive(Error, Debug)] | ||
pub enum ENSLookupError { | ||
#[error("ABI error")] | ||
AbiError, | ||
|
||
#[error("Invalid payload: {0}")] | ||
InvalidPayload(String), | ||
|
||
#[error("Unsupported: {0}")] | ||
Unsupported(String), | ||
|
||
#[error(transparent)] | ||
Unknown(#[from] anyhow::Error), | ||
} | ||
|
||
pub trait ENSLookup { | ||
fn calldata(&self, namehash: &H256) -> Vec<u8>; | ||
fn decode(&self, data: &[u8]) -> Result<String, ENSLookupError>; | ||
fn name(&self) -> String; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
use anyhow::anyhow; | ||
use ethers_core::{ | ||
abi::{ParamType, Token}, | ||
k256::U256, | ||
types::{H160, H256}, | ||
}; | ||
use hex_literal::hex; | ||
use tracing::info; | ||
|
||
use crate::models::multicoin::cointype::{coins::CoinType, evm::ChainId, slip44::SLIP44}; | ||
|
||
use super::{ENSLookup, ENSLookupError}; | ||
|
||
pub struct Multicoin { | ||
pub coin_type: CoinType, | ||
} | ||
|
||
impl ENSLookup for Multicoin { | ||
fn calldata(&self, namehash: &H256) -> Vec<u8> { | ||
let fn_selector = hex!("f1cb7e06").to_vec(); | ||
|
||
let data = ethers_core::abi::encode(&[ | ||
Token::FixedBytes(namehash.as_fixed_bytes().to_vec()), | ||
Token::Uint(self.coin_type.clone().into()), | ||
]); | ||
|
||
[fn_selector, data].concat() | ||
} | ||
|
||
fn decode(&self, data: &[u8]) -> Result<String, ENSLookupError> { | ||
info!("Decoding: {:?}", data); | ||
|
||
let decoded_abi = ethers_core::abi::decode(&[ParamType::Bytes], data) | ||
.map_err(|_| ENSLookupError::AbiError)?; | ||
let value = decoded_abi | ||
.get(0) | ||
.ok_or(ENSLookupError::AbiError)? | ||
.clone() | ||
.into_bytes(); | ||
|
||
let value = value.unwrap(); | ||
|
||
// TODO: If value is empty | ||
|
||
match &self.coin_type { | ||
// SLIP-044 Chain Address Decoding (see ensip-9) | ||
CoinType::Slip44(slip44) => match slip44 { | ||
// Bitcoin Decoding | ||
SLIP44::Bitcoin => Ok(format!("btc:{}", bs58::encode(value).into_string())), | ||
// Lightcoin Decoding | ||
SLIP44::Litecoin => { | ||
Err(ENSLookupError::Unknown(anyhow!( | ||
"Litecoin Decoding Not Implemented" | ||
))) | ||
// Ok(format!("ltc:{}", bs58::encode(value).into_string())) | ||
} | ||
|
||
// Unsupported SLIP44 Chain | ||
_ => { | ||
// Raw Dump | ||
// Ok(format!("SLIP-{:?}", value)) | ||
|
||
// Unsupported | ||
Err(ENSLookupError::Unsupported("Chain Not Supported".to_string())) | ||
} | ||
}, | ||
// Implement EVM Chain Address Decoding (mostly ChecksummedHex, sometimes ChecksummedHex(chainId)) (see ensip-11) | ||
CoinType::Evm(evm) => match evm { | ||
// TODO: EVM Exceptions go here | ||
// ChainId::Ethereum => { | ||
// // Verify length is 20 bytes | ||
// if value.len() != 20 { | ||
// // TODO: throw invalid length | ||
// return Ok("Invalid Length".to_string()); | ||
// } | ||
|
||
// let address = hex::encode(value); | ||
|
||
// Ok(format!("0x{address}")) | ||
// }, | ||
|
||
// Every EVM Chain | ||
_ => { | ||
// Verify length is 20 bytes | ||
if value.len() != 20 { | ||
// TODO: throw invalid length | ||
return Ok("Invalid Length".to_string()); | ||
} | ||
|
||
let address = hex::encode(value); | ||
|
||
Ok(format!("0x{address}")) | ||
} | ||
}, | ||
} | ||
} | ||
|
||
fn name(&self) -> String { | ||
format!("chains.{:?}", self.coin_type) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use ethers_core::{ | ||
abi::{ParamType, Token}, | ||
types::H256, | ||
}; | ||
use hex_literal::hex; | ||
|
||
use super::{ENSLookup, ENSLookupError}; | ||
pub struct Text { | ||
key: String, | ||
} | ||
|
||
impl Text { | ||
pub const fn new(key: String) -> Self { | ||
Self { key } | ||
} | ||
} | ||
|
||
impl ENSLookup for Text { | ||
fn calldata(&self, namehash: &H256) -> Vec<u8> { | ||
let fn_selector = hex!("59d1d43c").to_vec(); | ||
|
||
let data = ethers_core::abi::encode(&[ | ||
Token::FixedBytes(namehash.as_fixed_bytes().to_vec()), | ||
Token::String(self.key.to_string()), | ||
]); | ||
|
||
[fn_selector, data].concat() | ||
} | ||
|
||
fn decode(&self, data: &[u8]) -> Result<String, ENSLookupError> { | ||
let decoded_abi = ethers_core::abi::decode(&[ParamType::String], data) | ||
.map_err(|_| ENSLookupError::AbiError)?; | ||
let value = decoded_abi.get(0).ok_or(ENSLookupError::AbiError)?; | ||
let value = value.to_string(); | ||
|
||
Ok(value) | ||
} | ||
|
||
fn name(&self) -> String { | ||
format!("records.{}", self.key) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
pub mod lookup; | ||
pub mod multicoin; | ||
pub mod profile; | ||
pub mod records; | ||
pub mod universal_resolver; |
Oops, something went wrong.