Skip to content

Commit

Permalink
feat: add cartridge_addExecuteOutsideTransaction rpc endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
MartianGreed committed Mar 3, 2025
1 parent 9b93308 commit c7d162a
Show file tree
Hide file tree
Showing 14 changed files with 198 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/katana/chain-spec/src/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use katana_primitives::genesis::Genesis;
use katana_primitives::state::StateUpdatesWithClasses;
use katana_primitives::utils::split_u256;
use katana_primitives::version::CURRENT_STARKNET_VERSION;
use katana_primitives::Felt;
use katana_primitives::{felt, Felt};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use starknet::core::utils::cairo_short_string_to_felt;
Expand Down Expand Up @@ -137,6 +137,7 @@ lazy_static! {

let accounts = DevAllocationsGenerator::new(10)
.with_balance(U256::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE))
.with_class(felt!("0x024a9edbfa7082accfceabf6a92d7160086f346d622f28741bf1c651c412c9ab"))
.generate();

chain_spec.genesis.extend_allocations(accounts.into_iter().map(|(k, v)| (k, v.into())));
Expand Down
2 changes: 1 addition & 1 deletion crates/katana/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dojo-utils.workspace = true
serde.workspace = true
serde_json = "1.0.132"
shellexpand = "3.1.0"
starknet.workspace = true
tokio.workspace = true
toml.workspace = true
tracing.workspace = true
Expand All @@ -32,7 +33,6 @@ url.workspace = true

[dev-dependencies]
assert_matches.workspace = true
starknet.workspace = true

[features]
default = [ "server", "slot" ]
Expand Down
7 changes: 7 additions & 0 deletions crates/katana/cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ impl NodeArgs {
// Ensures the `--dev` flag enabled the dev module.
if self.development.dev {
modules.add(RpcModuleKind::Dev);
modules.add(RpcModuleKind::Cartridge);
}

modules
Expand Down Expand Up @@ -283,6 +284,12 @@ impl NodeArgs {
katana_slot_controller::add_controller_account(&mut chain_spec.genesis)?;
}

let paymaster_account = DevAllocationsGenerator::new(1)
.with_balance(U256::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE))
.with_class(katana_primitives::felt!("0x024a9edbfa7082accfceabf6a92d7160086f346d622f28741bf1c651c412c9ab"))
.generate();
chain_spec.genesis.extend_allocations(paymaster_account.into_iter().map(|(k, v)| (k, v.into())));

Ok((Arc::new(ChainSpec::Dev(chain_spec)), None))
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/katana/cli/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,10 @@ PREFUNDED ACCOUNTS
println!(
r"
| Account address | {addr}
| Account ch | {:#x}
| Private key | {pk:#x}
| Public key | {:#x}",
account.class_hash(),
account.public_key()
)
} else {
Expand Down
2 changes: 2 additions & 0 deletions crates/katana/node/src/config/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub enum RpcModuleKind {
Torii,
Saya,
Dev,
Cartridge,
}

/// Configuration for the RPC server.
Expand Down Expand Up @@ -95,6 +96,7 @@ impl RpcModulesList {
RpcModuleKind::Torii,
RpcModuleKind::Saya,
RpcModuleKind::Dev,
RpcModuleKind::Cartridge
]))
}

Expand Down
7 changes: 7 additions & 0 deletions crates/katana/node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ use katana_pool::ordering::FiFo;
use katana_pool::TxPool;
use katana_primitives::block::GasPrices;
use katana_primitives::env::{CfgEnv, FeeTokenAddressses};
use katana_rpc::cartridge::CartridgeApi;
use katana_rpc::cors::Cors;
use katana_rpc::dev::DevApi;
use katana_rpc::saya::SayaApi;
use katana_rpc::starknet::forking::ForkedClient;
use katana_rpc::starknet::{StarknetApi, StarknetApiConfig};
use katana_rpc::torii::ToriiApi;
use katana_rpc::{RpcServer, RpcServerHandle};
use katana_rpc_api::cartridge::CartridgeApiServer;
use katana_rpc_api::dev::DevApiServer;
use katana_rpc_api::saya::SayaApiServer;
use katana_rpc_api::starknet::{StarknetApiServer, StarknetTraceApiServer, StarknetWriteApiServer};
Expand Down Expand Up @@ -301,6 +303,11 @@ pub async fn build(mut config: Config) -> Result<Node> {

if config.rpc.apis.contains(&RpcModuleKind::Dev) {
let api = DevApi::new(backend.clone(), block_producer.clone());
rpc_modules.merge(DevApiServer::into_rpc(api))?;
}

if config.rpc.apis.contains(&RpcModuleKind::Cartridge) {
let api = CartridgeApi::new(backend.clone(), block_producer.clone(), pool.clone());
rpc_modules.merge(api.into_rpc())?;
}

Expand Down
11 changes: 11 additions & 0 deletions crates/katana/rpc/rpc-api/src/cartridge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use jsonrpsee::core::RpcResult;
use jsonrpsee::proc_macros::rpc;
use katana_primitives::{ContractAddress, Felt};
use katana_rpc_types::transaction::{ExecuteOutside, InvokeTxResult};

#[cfg_attr(not(feature = "client"), rpc(server, namespace = "cartridge"))]
#[cfg_attr(feature = "client", rpc(client, server, namespace = "cartridge"))]
pub trait CartridgeApi {
#[method(name = "addExecuteOutsideTransaction")]
async fn add_execute_outside_transaction(&self, address: ContractAddress, outside_execution: ExecuteOutside, signature: Vec<Felt>) -> RpcResult<InvokeTxResult>;
}
1 change: 1 addition & 0 deletions crates/katana/rpc/rpc-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod cartridge;
pub mod dev;
pub mod saya;
pub mod starknet;
Expand Down
3 changes: 3 additions & 0 deletions crates/katana/rpc/rpc-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,8 @@ flate2.workspace = true
serde_json_pythonic = "0.1.2"
similar-asserts.workspace = true

cainome-cairo-serde.workspace = true
cainome.workspace = true

[dev-dependencies]
rstest.workspace = true
58 changes: 58 additions & 0 deletions crates/katana/rpc/rpc-types/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,64 @@ pub struct DeclareTxResult(DeclareTransactionResult);
#[serde(transparent)]
pub struct InvokeTxResult(InvokeTransactionResult);

#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Call {
pub to: ContractAddress,
pub selector: Felt,
pub calldata: Vec<Felt>,

}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct OutsideExecutionV2 {
pub caller: cainome::cairo_serde::ContractAddress,
pub nonce: starknet::core::types::Felt,
#[serde(
serialize_with = "cainome::cairo_serde::serialize_as_hex",
deserialize_with = "cainome::cairo_serde::deserialize_from_hex"
)]
pub execute_after: u64,
#[serde(
serialize_with = "cainome::cairo_serde::serialize_as_hex",
deserialize_with = "cainome::cairo_serde::deserialize_from_hex"
)]
pub execute_before: u64,
pub calls: Vec<Call>,
}

#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct OutsideExecutionV3 {
pub caller: cainome::cairo_serde::ContractAddress,
#[serde(
serialize_with = "cainome::cairo_serde::serialize_as_hex_t2",
deserialize_with = "cainome::cairo_serde::deserialize_from_hex_t2"
)]
pub nonce: (starknet::core::types::Felt, u128),
#[serde(
serialize_with = "cainome::cairo_serde::serialize_as_hex",
deserialize_with = "cainome::cairo_serde::deserialize_from_hex"
)]
pub execute_after: u64,
#[serde(
serialize_with = "cainome::cairo_serde::serialize_as_hex",
deserialize_with = "cainome::cairo_serde::deserialize_from_hex"
)]
pub execute_before: u64,
pub calls: Vec<Call>,
}

#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum ExecuteOutside {
V2(OutsideExecutionV2),
V3(OutsideExecutionV3),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AddExecuteOutsideTransaction {
pub address: Felt,
pub outside_execution: ExecuteOutside,
pub signature: Vec<Felt>,
}

impl From<TxWithHash> for Tx {
fn from(value: TxWithHash) -> Self {
use katana_primitives::transaction::Tx as InternalTx;
Expand Down
100 changes: 100 additions & 0 deletions crates/katana/rpc/rpc/src/cartridge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use core::panic;
use std::sync::Arc;

use jsonrpsee::core::{async_trait, RpcResult};
use katana_core::backend::Backend;
use katana_core::service::block_producer::{BlockProducer, BlockProducerMode, PendingExecutor};
use katana_executor::ExecutorFactory;
use katana_pool::{TxPool, TransactionPool};
use katana_primitives::{ContractAddress, Felt};
use katana_primitives::transaction::{ExecutableTx,ExecutableTxWithHash, InvokeTx, InvokeTxV1};
use katana_rpc_api::dev::DevApiServer;
use katana_rpc_api::cartridge::CartridgeApiServer;
use katana_rpc_types::error::{dev::DevApiError, starknet::StarknetApiError};
use katana_rpc_types::transaction::{ExecuteOutside, InvokeTxResult};
use katana_tasks::TokioTaskSpawner;
use starknet::core::types::InvokeTransactionResult;
use starknet::core::types::{BlockId, BlockTag};
use starknet::accounts::SingleOwnerAccount;

#[allow(missing_debug_implementations)]
pub struct CartridgeApi<EF: ExecutorFactory> {
backend: Arc<Backend<EF>>,
block_producer: BlockProducer<EF>,
pool: TxPool,
}

impl<EF> Clone for CartridgeApi<EF>
where
EF: ExecutorFactory,
{
fn clone(&self) -> Self {
Self { backend: Arc::clone(&self.backend), block_producer: self.block_producer.clone(), pool: self.pool.clone() }
}
}

impl<EF: ExecutorFactory> CartridgeApi<EF> {
pub fn new(backend: Arc<Backend<EF>>, block_producer: BlockProducer<EF>, pool: TxPool) -> Self {
Self { backend, block_producer, pool }
}
pub async fn execute_outside(&self, address: ContractAddress, nonce: Option<Felt>,outside_execution: ExecuteOutside, signature: Vec<Felt>) -> Result<InvokeTxResult, StarknetApiError> {
self.on_io_blocking_task(move |this| {
let tx = match outside_execution {
ExecuteOutside::V2(v2) => InvokeTx::V1(InvokeTxV1 {
chain_id: this.backend.chain_spec.id(),
nonce: nonce.unwrap_or(Felt::ZERO),
calldata: v2.calls[0].calldata.clone(),
signature: signature,
sender_address: address.into(),
max_fee: 0,
}),
ExecuteOutside::V3(v3) => InvokeTx::V1(InvokeTxV1 {
chain_id: this.backend.chain_spec.id(),
nonce: nonce.unwrap_or(Felt::ZERO),
calldata: v3.calls[0].calldata.clone(),
signature: signature,
sender_address: address.into(),
max_fee: 0,
}),
};
let tx = ExecutableTxWithHash::new(ExecutableTx::Invoke(tx));
let hash = this.pool.add_transaction(tx)?;
Ok(InvokeTxResult::new(hash))
}).await
}

async fn on_io_blocking_task<F, T>(&self, func: F) -> T
where
F: FnOnce(Self) -> T + Send + 'static,
T: Send + 'static,
{
let this = self.clone();
TokioTaskSpawner::new().unwrap().spawn_blocking(move || func(this)).await.unwrap()
}
}


#[async_trait]
impl<EF: ExecutorFactory> CartridgeApiServer for CartridgeApi<EF> {
async fn add_execute_outside_transaction(&self, address: ContractAddress, outside_execution: ExecuteOutside, signature: Vec<Felt>) -> RpcResult<InvokeTxResult> {
let (addr, alloc) = self.backend.chain_spec.genesis().accounts().take(1).next().unwrap();
println!(
"{:#?}", outside_execution
);

println!(
"{:#?}\n\n{:#?}", addr, alloc
);

// let accounts = DevAllocationsGenerator::new(self.development.total_accounts)
// .with_seed(parse_seed(&self.development.seed))
// .with_balance(U256::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE))
// .with_class(katana_primitives::felt!("0x024a9edbfa7082accfceabf6a92d7160086f346d622f28741bf1c651c412c9ab"))
// .generate();
// self.backend.executor_factory
// let mut account = SingleOwnerAccount::new(addr.clone(), alloc.balance().unwrap());
// account.set_block_id(BlockId::Tag(BlockTag::Pending));

Ok(self.execute_outside(addr.clone(), alloc.nonce(), outside_execution, signature).await?)
}
}
2 changes: 1 addition & 1 deletion crates/katana/rpc/rpc/src/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,4 @@ impl<EF: ExecutorFactory> DevApiServer for DevApi<EF> {
async fn predeployed_accounts(&self) -> Result<Vec<Account>, Error> {
Ok(self.backend.chain_spec.genesis().accounts().map(|e| Account::new(*e.0, e.1)).collect())
}
}
}
1 change: 1 addition & 0 deletions crates/katana/rpc/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use jsonrpsee::RpcModule;
use tower::ServiceBuilder;
use tracing::info;

pub mod cartridge;
pub mod cors;
pub mod dev;
pub mod health;
Expand Down

0 comments on commit c7d162a

Please sign in to comment.