use std::env;
use std::sync::Arc;
use ethers::prelude::k256::ecdsa::SigningKey;
use ethers::prelude::{ContractCall, SignerMiddleware};
use ethers::providers::{JsonRpcClient, Provider};
use ethers::signers::{Signer, Wallet};
use ethers::types::{Address, Bytes, U256};
use switchboard_common::{
ChainResultInfo, EVMFunctionResult, EvmTransaction, FunctionResult, Gramine,
};
use crate::bindings::error::SwitchboardClientError;
use crate::bindings::{eip712, switchboard};
use crate::utils::{generate_signer, load_env_address};
pub type EVMMiddleware<T> = SignerMiddleware<Provider<T>, Wallet<SigningKey>>;
#[derive(Clone)]
pub struct EVMFunctionRunner {
pub function_id: Address,
pub enclave_wallet: Wallet<SigningKey>,
pub signer: Address,
pub verifying_contract: Address,
pub chain_id: u64,
}
impl std::fmt::Display for EVMFunctionRunner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"SwitchboardFunctionRunner: signer: {}, verifying_contract: {}, function_id: {}",
self.signer,
self.verifying_contract.to_string(),
self.function_id.to_string(),
)
}
}
impl EVMFunctionRunner {
pub fn new() -> Result<EVMFunctionRunner, SwitchboardClientError> {
let enclave_wallet = generate_signer();
let signer = enclave_wallet.address();
let chain_id = env::var("CHAIN_ID")?;
let verifying_contract = load_env_address("VERIFYING_CONTRACT")?;
let function_id = load_env_address("FUNCTION_KEY")?;
Ok(Self {
function_id,
enclave_wallet,
signer,
verifying_contract,
chain_id: chain_id.parse().unwrap_or(1),
})
}
pub fn get_result<T: JsonRpcClient>(
&self,
to: Address,
expiration_time_seconds: U256,
gas_limit: U256,
calls: Vec<ContractCall<EVMMiddleware<T>, ()>>, ) -> Result<FunctionResult, SwitchboardClientError> {
let (evm_txns, signatures): (Vec<EvmTransaction>, Vec<Bytes>) = calls
.iter()
.map(|c| {
let transaction = switchboard::Transaction {
expiration_time_seconds,
gas_limit,
value: U256::from(0),
to: c.tx.from().unwrap_or(&to).clone(),
from: self.enclave_wallet.address(),
data: c.tx.data().unwrap().clone(),
};
let eip712_hash = eip712::get_transaction_hash(
"Switchboard".to_string(),
"0.0.1".to_string(),
self.chain_id,
self.verifying_contract,
transaction,
)
.unwrap();
let evm_txn = EvmTransaction {
expiration_time_seconds: expiration_time_seconds.as_u64(),
gas_limit: gas_limit.to_string(),
data: c.tx.data().unwrap().clone().to_vec(),
from: self.enclave_wallet.address().as_bytes().to_vec(),
to: c.tx.from().unwrap_or(&to).clone().as_bytes().to_vec(),
value: c.tx.value().unwrap_or(&U256::from(0)).to_string(),
};
(
evm_txn,
Bytes::from(
self.enclave_wallet
.sign_hash(ethers::types::H256::from(eip712_hash))
.unwrap()
.to_vec(),
),
)
})
.unzip();
let chain_result_info = ChainResultInfo::Evm(EVMFunctionResult {
txs: evm_txns.clone(),
signatures: signatures.iter().map(|s| s.to_vec()).collect(),
});
let quote_raw =
Gramine::generate_quote(&self.enclave_wallet.address().as_bytes()).unwrap_or_default();
Ok(FunctionResult {
version: 1,
quote: quote_raw,
fn_key: self.function_id.as_bytes().to_vec(),
signer: self.enclave_wallet.address().as_bytes().to_vec(),
fn_request_key: Vec::new(),
fn_request_hash: Vec::new(),
chain_result_info,
})
}
pub fn emit<T: JsonRpcClient>(
&self,
to: Address,
expiration_time_seconds: U256,
gas_limit: U256,
calls: Vec<ContractCall<EVMMiddleware<T>, ()>>, ) -> Result<(), SwitchboardClientError> {
self.get_result(to, expiration_time_seconds, gas_limit, calls)
.map_err(|e| SwitchboardClientError::CustomError {
message: "failed to run function verify".to_string(),
source: Arc::new(e),
})
.unwrap()
.emit();
Ok(())
}
}