|
| 1 | +use std::borrow::Cow; |
| 2 | +use std::future::Future; |
| 3 | + |
| 4 | +use jsonrpsee::core::middleware::{self, Batch, Notification, RpcServiceT}; |
| 5 | +use jsonrpsee::core::traits::ToRpcParams; |
| 6 | +use jsonrpsee::types::Request; |
| 7 | +use jsonrpsee::MethodResponse; |
| 8 | +use katana_executor::ExecutorFactory; |
| 9 | +use katana_primitives::block::BlockIdOrTag; |
| 10 | +use katana_primitives::{ContractAddress, Felt}; |
| 11 | +use katana_rpc_types::broadcasted::BroadcastedTx; |
| 12 | +use serde::Deserialize; |
| 13 | +use starknet::core::types::SimulationFlagForEstimateFee; |
| 14 | +use tracing::trace; |
| 15 | + |
| 16 | +use super::{Error, Paymaster}; |
| 17 | +use crate::rpc::types::OutsideExecution; |
| 18 | + |
| 19 | +#[derive(Debug)] |
| 20 | +pub struct PaymasterLayer<EF: ExecutorFactory> { |
| 21 | + pub(crate) paymaster: Paymaster<EF>, |
| 22 | +} |
| 23 | + |
| 24 | +impl<EF: ExecutorFactory> Clone for PaymasterLayer<EF> { |
| 25 | + fn clone(&self) -> Self { |
| 26 | + Self { paymaster: self.paymaster.clone() } |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +impl<S, EF: ExecutorFactory> tower::Layer<S> for PaymasterLayer<EF> { |
| 31 | + type Service = PaymasterService<S, EF>; |
| 32 | + |
| 33 | + fn layer(&self, service: S) -> Self::Service { |
| 34 | + PaymasterService { service, paymaster: self.paymaster.clone() } |
| 35 | + } |
| 36 | +} |
| 37 | + |
| 38 | +#[derive(Debug)] |
| 39 | +pub struct PaymasterService<S, EF: ExecutorFactory> { |
| 40 | + service: S, |
| 41 | + paymaster: Paymaster<EF>, |
| 42 | +} |
| 43 | + |
| 44 | +impl<S, EF> PaymasterService<S, EF> |
| 45 | +where |
| 46 | + S: middleware::RpcServiceT + Send + Sync + Clone + 'static, |
| 47 | + EF: ExecutorFactory, |
| 48 | +{ |
| 49 | + fn intercept_estimate_fee(&self, request: &mut Request<'_>) { |
| 50 | + let params = request.params(); |
| 51 | + |
| 52 | + let (txs, simulation_flags, block_id) = if params.is_object() { |
| 53 | + #[derive(serde::Deserialize)] |
| 54 | + struct ParamsObject { |
| 55 | + request: Vec<BroadcastedTx>, |
| 56 | + #[serde(alias = "simulationFlags")] |
| 57 | + simulation_flags: Vec<SimulationFlagForEstimateFee>, |
| 58 | + #[serde(alias = "blockId")] |
| 59 | + block_id: BlockIdOrTag, |
| 60 | + } |
| 61 | + |
| 62 | + let parsed: ParamsObject = match params.parse() { |
| 63 | + Ok(p) => p, |
| 64 | + Err(..) => return, |
| 65 | + }; |
| 66 | + |
| 67 | + (parsed.request, parsed.simulation_flags, parsed.block_id) |
| 68 | + } else { |
| 69 | + let mut seq = params.sequence(); |
| 70 | + |
| 71 | + let request: Vec<BroadcastedTx> = match seq.next() { |
| 72 | + Ok(v) => v, |
| 73 | + Err(..) => return, |
| 74 | + }; |
| 75 | + |
| 76 | + let simulation_flags: Vec<SimulationFlagForEstimateFee> = match seq.next() { |
| 77 | + Ok(v) => v, |
| 78 | + Err(..) => return, |
| 79 | + }; |
| 80 | + |
| 81 | + let block_id: BlockIdOrTag = match seq.next() { |
| 82 | + Ok(v) => v, |
| 83 | + Err(..) => return, |
| 84 | + }; |
| 85 | + |
| 86 | + (request, simulation_flags, block_id) |
| 87 | + }; |
| 88 | + |
| 89 | + let new_txs = self.paymaster.handle_estimate_fees(block_id, txs).unwrap(); |
| 90 | + |
| 91 | + let new_params = { |
| 92 | + let mut params = jsonrpsee::core::params::ArrayParams::new(); |
| 93 | + params.insert(new_txs).unwrap(); |
| 94 | + params.insert(simulation_flags).unwrap(); |
| 95 | + params.insert(block_id).unwrap(); |
| 96 | + params |
| 97 | + }; |
| 98 | + |
| 99 | + let params = new_params.to_rpc_params().unwrap(); |
| 100 | + let params = params.map(Cow::Owned); |
| 101 | + request.params = params; |
| 102 | + } |
| 103 | + |
| 104 | + fn intercept_add_outside_execution(&self, request: &Request<'_>) -> Option<MethodResponse> { |
| 105 | + let params = request.params(); |
| 106 | + |
| 107 | + let (controller_address, ..) = if params.is_object() { |
| 108 | + #[derive(Deserialize)] |
| 109 | + struct ParamsObject { |
| 110 | + address: ContractAddress, |
| 111 | + #[serde(alias = "outsideExecution")] |
| 112 | + outside_execution: OutsideExecution, |
| 113 | + signature: Vec<Felt>, |
| 114 | + } |
| 115 | + |
| 116 | + let parsed: ParamsObject = match params.parse() { |
| 117 | + Ok(p) => p, |
| 118 | + Err(..) => return None, |
| 119 | + }; |
| 120 | + |
| 121 | + (parsed.address, parsed.outside_execution, parsed.signature) |
| 122 | + } else { |
| 123 | + let mut seq = params.sequence(); |
| 124 | + |
| 125 | + let address = match seq.next::<ContractAddress>() { |
| 126 | + Ok(v) => v, |
| 127 | + Err(..) => return None, |
| 128 | + }; |
| 129 | + |
| 130 | + let outside_execution = match seq.next::<OutsideExecution>() { |
| 131 | + Ok(v) => v, |
| 132 | + Err(..) => return None, |
| 133 | + }; |
| 134 | + |
| 135 | + let signature = match seq.next::<Vec<Felt>>() { |
| 136 | + Ok(v) => v, |
| 137 | + Err(..) => return None, |
| 138 | + }; |
| 139 | + |
| 140 | + (address, outside_execution, signature) |
| 141 | + }; |
| 142 | + |
| 143 | + match self.paymaster.deploy_controller(controller_address) { |
| 144 | + Ok(tx_hash) => { |
| 145 | + trace!( |
| 146 | + target: "paymaster", |
| 147 | + tx_hash = format!("{tx_hash:#x}"), |
| 148 | + "Controller deploy transaction submitted", |
| 149 | + ); |
| 150 | + |
| 151 | + None |
| 152 | + } |
| 153 | + Err(Error::ControllerNotFound(..)) => None, |
| 154 | + Err(error) => panic!("{error}"), |
| 155 | + } |
| 156 | + } |
| 157 | +} |
| 158 | + |
| 159 | +impl<S, EF> Clone for PaymasterService<S, EF> |
| 160 | +where |
| 161 | + S: Clone, |
| 162 | + EF: ExecutorFactory + Clone, |
| 163 | +{ |
| 164 | + fn clone(&self) -> Self { |
| 165 | + Self { service: self.service.clone(), paymaster: self.paymaster.clone() } |
| 166 | + } |
| 167 | +} |
| 168 | + |
| 169 | +impl<S, EF> RpcServiceT for PaymasterService<S, EF> |
| 170 | +where |
| 171 | + EF: ExecutorFactory, |
| 172 | + S: RpcServiceT< |
| 173 | + MethodResponse = MethodResponse, |
| 174 | + BatchResponse = MethodResponse, |
| 175 | + NotificationResponse = MethodResponse, |
| 176 | + > + Send |
| 177 | + + Sync |
| 178 | + + Clone |
| 179 | + + 'static, |
| 180 | +{ |
| 181 | + type MethodResponse = S::MethodResponse; |
| 182 | + type BatchResponse = S::BatchResponse; |
| 183 | + type NotificationResponse = S::NotificationResponse; |
| 184 | + |
| 185 | + fn call<'a>( |
| 186 | + &self, |
| 187 | + mut request: Request<'a>, |
| 188 | + ) -> impl Future<Output = Self::MethodResponse> + Send + 'a { |
| 189 | + async { |
| 190 | + if request.method_name() == "starknet_estimateFee" { |
| 191 | + self.intercept_estimate_fee(&mut request); |
| 192 | + } else if request.method_name() == "cartridge_addExecuteOutsideTransaction" { |
| 193 | + self.intercept_add_outside_execution(&request); |
| 194 | + } |
| 195 | + |
| 196 | + self.service.call(request).await |
| 197 | + } |
| 198 | + } |
| 199 | + |
| 200 | + fn batch<'a>( |
| 201 | + &self, |
| 202 | + requests: Batch<'a>, |
| 203 | + ) -> impl Future<Output = Self::BatchResponse> + Send + 'a { |
| 204 | + self.service.batch(requests) |
| 205 | + } |
| 206 | + |
| 207 | + fn notification<'a>( |
| 208 | + &self, |
| 209 | + n: Notification<'a>, |
| 210 | + ) -> impl Future<Output = Self::NotificationResponse> + Send + 'a { |
| 211 | + self.service.notification(n) |
| 212 | + } |
| 213 | +} |
0 commit comments