1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
use ethabi::{Contract, Token};
use thiserror::Error;
use web3::{
api::Eth,
signing::keccak256,
types::{Bytes, CallRequest}, Transport
};
use crate::account::Address;
static MAGIC_EIP1271_VALUE: [u8; 4] = [22, 38, 186, 126];
static EIP1654_ABI: &[u8] = include_bytes!("../contracts/SignatureValidator.json");
#[derive(Error, Debug)]
pub enum RPCCallError {
#[error("rpc resolver not implemented")]
NotImplemented,
#[error("error encoding params: {0}")]
Encode(ethabi::Error),
#[error("error calling rpc: {0}")]
Call(web3::Error),
#[error("error decoding result: {0}")]
Decode(ethabi::Error),
}
/// A signature validator that receives an address, a message and a signature and validates it using local resources.
/// ```
/// use ethabi::Contract;
/// use web3::{Web3, transports::Http};
/// use dcl_crypto::util::rpc_call_is_valid_signature;
/// use dcl_crypto::account::{Address, EIP1271Signature};
///
/// # tokio_test::block_on(async {
/// let endpoint = env!("ETHEREUM_MAINNET_RPC");
/// let transport = Http::new(endpoint).unwrap();
/// let eth = Web3::new(transport).eth();
/// let address = Address::try_from("0x3b21028719a4aca7ebee35b0157a6f1b0cf0d0c5").unwrap();
/// // let address = Address::try_from("0xbdbee960fb7ce6267c467665dea046d0a4849cda").unwrap();
/// let message = "Decentraland Login\nEphemeral address: 0x69fBdE5Da06eb76e8E7F6Fd2FEEd968F28b951a5\nExpiration: Tue Aug 06 7112 10:14:51 GMT-0300 (Argentina Standard Time)".to_string();
/// let hash = EIP1271Signature::try_from("0x03524dbe44d19aacc8162b4d5d17820c370872de7bfd25d1add2b842adb1de546b454fc973b6d215883c30f4c21774ae71683869317d773f27e6bfaa9a2a05101b36946c3444914bb93f17a29d88e2449bcafdb6478b4835102c522197fa6f63d13ce5ab1d5c11c95db0c210fb4380995dff672392e5569c86d7c6bb2a44c53a151c").unwrap().to_vec();
///
/// let result = rpc_call_is_valid_signature(ð, address, message, hash).await.unwrap();
/// assert_eq!(result, true);
/// # })
/// ```
pub async fn rpc_call_is_valid_signature<T: Transport>(
eth: &Eth<T>,
address: Address,
message: String,
hash: Vec<u8>,
) -> Result<bool, RPCCallError> {
let contract = Contract::load(EIP1654_ABI).map_err(RPCCallError::Encode)?;
let func = contract.function("isValidSignature")
.map_err(RPCCallError::Encode)?;
let call = func.encode_input(&[
Token::FixedBytes(keccak256(message.as_bytes()).to_vec()),
Token::Bytes(hash),
]).map_err(RPCCallError::Encode)?;
let bytes = eth
.call(
CallRequest {
from: None,
to: Some(*address),
gas: None,
gas_price: None,
value: None,
data: Some(Bytes(call)),
transaction_type: None,
access_list: None,
max_fee_per_gas: None,
max_priority_fee_per_gas: None,
},
None,
)
.await
.map_err(RPCCallError::Call)?;
let output = func.decode_output(&bytes.0)
.map_err(RPCCallError::Decode)?;
if let Some(token) = output.first() {
if let Some(value) = token.clone().into_fixed_bytes() {
return Ok(value == MAGIC_EIP1271_VALUE);
}
}
Ok(false)
}