pub mod factory;
use factory::{CreatedFilter, DsProxyFactory, ADDRESS_BOOK};
use super::{Transformer, TransformerError};
use ethers_contract::{builders::ContractCall, BaseContract, ContractError};
use ethers_core::{
abi::parse_abi,
types::{transaction::eip2718::TypedTransaction, *},
utils::id,
};
use ethers_providers::Middleware;
use std::sync::Arc;
const DS_PROXY_EXECUTE_TARGET: &str =
"function execute(address target, bytes memory data) public payable returns (bytes memory response)";
const DS_PROXY_EXECUTE_CODE: &str =
"function execute(bytes memory code, bytes memory data) public payable returns (address target, bytes memory response)";
#[derive(Clone, Debug)]
pub struct DsProxy {
address: Address,
contract: BaseContract,
}
impl DsProxy {
pub fn new(address: Address) -> Self {
let contract = parse_abi(&[DS_PROXY_EXECUTE_TARGET, DS_PROXY_EXECUTE_CODE])
.expect("could not parse ABI")
.into();
Self { address, contract }
}
pub fn address(&self) -> Address {
self.address
}
}
impl DsProxy {
pub async fn build<M: Middleware, C: Into<Arc<M>>>(
client: C,
factory: Option<Address>,
owner: Address,
) -> Result<Self, ContractError<M>> {
let client = client.into();
let factory: Address = match factory {
Some(addr) => addr,
None => {
let chain_id =
client.get_chainid().await.map_err(ContractError::from_middleware_error)?;
match ADDRESS_BOOK.get(&chain_id) {
Some(addr) => *addr,
None => panic!(
"Must either be a supported Network ID or provide DsProxyFactory contract address"
),
}
}
};
let ds_proxy_factory = DsProxyFactory::new(factory, client);
let tx_receipt = ds_proxy_factory
.build(owner)
.legacy()
.send()
.await?
.await?
.ok_or(ContractError::ContractNotDeployed)?;
if tx_receipt.status == Some(U64::from(1u64)) {
let log = tx_receipt
.logs
.iter()
.find(|i| i.address == factory)
.ok_or(ContractError::ContractNotDeployed)?;
let created_filter: CreatedFilter =
ds_proxy_factory.decode_event("Created", log.topics.clone(), log.data.clone())?;
let contract = parse_abi(&[DS_PROXY_EXECUTE_TARGET, DS_PROXY_EXECUTE_CODE])
.expect("could not parse ABI")
.into();
Ok(Self { address: created_filter.proxy, contract })
} else {
Err(ContractError::ContractNotDeployed)
}
}
}
impl DsProxy {
pub fn execute<M: Middleware, C: Into<Arc<M>>, T: Into<AddressOrBytes>>(
&self,
client: C,
target: T,
data: Bytes,
) -> Result<ContractCall<M, Bytes>, ContractError<M>> {
let ds_proxy = self.contract.clone().into_contract(self.address, client.into());
match target.into() {
AddressOrBytes::Address(addr) => {
let selector = id("execute(address,bytes)");
let args = (addr, data);
Ok(ds_proxy.method_hash(selector, args)?)
}
AddressOrBytes::Bytes(code) => {
let selector = id("execute(bytes,bytes)");
let args = (code, data);
Ok(ds_proxy.method_hash(selector, args)?)
}
}
}
}
impl Transformer for DsProxy {
fn transform(&self, tx: &mut TypedTransaction) -> Result<(), TransformerError> {
let target =
*tx.to_addr().ok_or_else(|| TransformerError::MissingField("to".to_string()))?;
let data = tx.data().cloned().unwrap_or_else(|| vec![].into());
let selector = id("execute(address,bytes)");
let encoded_data = self.contract.encode_with_selector(selector, (target, data))?;
tx.set_data(encoded_data);
tx.set_to(self.address);
Ok(())
}
}