Skip to content

Commit 9ea6cb4

Browse files
kariyremybar
authored andcommitted
refactor(paymaster): paymaster component as a tower layer
1 parent fa5edec commit 9ea6cb4

File tree

30 files changed

+2721
-160
lines changed

30 files changed

+2721
-160
lines changed

Cargo.lock

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cartridge/Cargo.toml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
[package]
2+
build = "build.rs"
23
edition.workspace = true
34
license.workspace = true
45
name = "cartridge"
56
repository.workspace = true
67
version.workspace = true
7-
build = "build.rs"
88

99
[dependencies]
10+
katana-chain-spec.workspace = true
1011
katana-contracts.workspace = true
12+
katana-core.workspace = true
13+
katana-executor.workspace = true
14+
katana-pool.workspace = true
1115
katana-primitives.workspace = true
16+
katana-provider.workspace = true
17+
katana-rpc.workspace = true
18+
katana-rpc-api.workspace = true
19+
katana-rpc-types.workspace = true
20+
katana-tasks.workspace = true
1221

1322
anyhow.workspace = true
1423
ark-ec = "0.4"
@@ -22,3 +31,12 @@ tracing.workspace = true
2231
url.workspace = true
2332
# Use a specific revision of stark-vrf to avoid unwanted breaking changes.
2433
stark-vrf = { git = "https://github.com/dojoengine/stark-vrf.git", rev = "96d6d2a" }
34+
35+
jsonrpsee = { workspace = true, features = [ "server" ] }
36+
tower.workspace = true
37+
38+
cainome.workspace = true
39+
cainome-cairo-serde.workspace = true
40+
41+
[dev-dependencies]
42+
similar-asserts.workspace = true

crates/cartridge/src/client.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ impl Client {
5858
}
5959
}
6060
}
61+
6162
/// Response from the Cartridge API to fetch the calldata for the constructor of the given
6263
/// controller address.
6364
#[derive(Debug, Clone, Deserialize)]

crates/cartridge/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
22

33
pub mod client;
4+
pub mod paymaster;
5+
pub mod rpc;
46
pub mod vrf;
57

68
pub use client::Client;
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
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

Comments
 (0)