mtv_crypto/
util.rs

1use ethabi::{Contract, Token};
2use thiserror::Error;
3use web3::{
4    api::Eth,
5    signing::keccak256,
6    types::{Bytes, CallRequest}, Transport
7};
8
9use crate::account::Address;
10
11static MAGIC_EIP1271_VALUE: [u8; 4] = [22, 38, 186, 126];
12static EIP1654_ABI: &[u8] = include_bytes!("../contracts/SignatureValidator.json");
13
14#[derive(Error, Debug)]
15pub enum RPCCallError {
16
17    #[error("rpc resolver not implemented")]
18    NotImplemented,
19
20    #[error("error encoding params: {0}")]
21    Encode(ethabi::Error),
22
23    #[error("error calling rpc: {0}")]
24    Call(web3::Error),
25
26    #[error("error decoding result: {0}")]
27    Decode(ethabi::Error),
28}
29
30/// A signature validator that receives an address, a message and a signature and validates it using local resources.
31pub async fn rpc_call_is_valid_signature<T: Transport>(
32    eth: &Eth<T>,
33    address: Address,
34    message: String,
35    hash: Vec<u8>,
36) -> Result<bool, RPCCallError> {
37    let contract = Contract::load(EIP1654_ABI).map_err(RPCCallError::Encode)?;
38    let func = contract.function("isValidSignature")
39        .map_err(RPCCallError::Encode)?;
40
41    let call = func.encode_input(&[
42        Token::FixedBytes(keccak256(message.as_bytes()).to_vec()),
43        Token::Bytes(hash),
44    ]).map_err(RPCCallError::Encode)?;
45
46    let bytes = eth
47        .call(
48            CallRequest {
49                from: None,
50                to: Some(*address),
51                gas: None,
52                gas_price: None,
53                value: None,
54                data: Some(Bytes(call)),
55                transaction_type: None,
56                access_list: None,
57                max_fee_per_gas: None,
58                max_priority_fee_per_gas: None,
59            },
60            None,
61        )
62        .await
63        .map_err(RPCCallError::Call)?;
64
65    let output = func.decode_output(&bytes.0)
66        .map_err(RPCCallError::Decode)?;
67
68    if let Some(token) = output.first() {
69        if let Some(value) = token.clone().into_fixed_bytes() {
70            return Ok(value == MAGIC_EIP1271_VALUE);
71        }
72    }
73
74    Ok(false)
75}