Created
May 21, 2025 14:57
-
-
Save ssadler/e2830d6f969d23eeb3f571877344c53a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use bincode::deserialize; | |
| use litesvm::types::{FailedTransactionMetadata, SimulatedTransactionInfo, TransactionResult}; | |
| use litesvm::LiteSVM; | |
| use solana_transaction::Transaction; | |
| #[get("/")] | |
| async fn index() -> &'static str { | |
| "Hi!" | |
| } | |
| #[get("/latest_blockhash")] | |
| async fn latest_blockhash(litesvm: Session) -> Json<String> { | |
| Json(litesvm.lock().latest_blockhash().to_string()) | |
| } | |
| #[get("/get_sigverify")] | |
| async fn get_sigverify(litesvm: Session) -> Json<bool> { | |
| Json(litesvm.lock().get_sigverify()) | |
| } | |
| #[get("/get_clock")] | |
| async fn get_clock(litesvm: Session) -> Json<solana_clock::Clock> { | |
| Json(litesvm.lock().get_sysvar::<solana_clock::Clock>()) | |
| } | |
| #[get("/get_account")] | |
| async fn get_account(litesvm: Session, args: Query<GetAccount>) -> Json<Option<solana_account::Account>> { | |
| let account = litesvm.lock().get_account(&args.pubkey); | |
| eprintln!("{:?}", account); | |
| Json(litesvm.lock().get_account(&args.pubkey)) | |
| } | |
| #[derive(serde::Deserialize)] | |
| struct GetAccount { pubkey: Pubkey } | |
| #[post("/airdrop")] | |
| async fn airdrop(litesvm: Session, args: Query<Airdrop>) -> Json<TransactionResult> { | |
| Json(litesvm.lock().airdrop(&args.pubkey, args.lamports)) | |
| } | |
| #[derive(serde::Deserialize)] | |
| struct Airdrop { pubkey: Pubkey, lamports: u64 } | |
| #[post("/expire_blockhash")] | |
| async fn expire_blockhash(litesvm: Session) -> Json<()> { | |
| Json(litesvm.lock().expire_blockhash()) | |
| } | |
| #[post("/simulate_transaction")] | |
| async fn simulate_transaction(litesvm: Session, input: Bytes) | |
| -> Json<Result<SimulatedTransactionInfo, FailedTransactionMetadata>> | |
| { | |
| let litesvm = litesvm.lock().clone().with_sigverify(false); | |
| let tx: Transaction = deserialize(&input).unwrap(); | |
| let res = litesvm.simulate_transaction(tx); | |
| Json(res) | |
| } | |
| #[post("/send_transaction")] | |
| async fn send_transaction(litesvm: Session, input: Bytes) -> Json<TransactionResult> { | |
| let tx: Transaction = deserialize(&input).unwrap(); | |
| let res = litesvm.lock().send_transaction(tx); | |
| Json(res) | |
| } | |
| #[post("/add_program_from_file")] | |
| async fn add_program_from_file(litesvm: Session, args: Query<AddProgramFromFile>) -> &'static str { | |
| litesvm.lock().add_program_from_file(*args.program_id, &args.path).unwrap(); | |
| "" | |
| } | |
| #[derive(serde::Deserialize)] | |
| struct AddProgramFromFile { program_id: Pubkey, path: String } | |
| use std::str::FromStr; | |
| use std::collections::HashMap; | |
| use std::future::ready; | |
| use std::sync::Arc; | |
| use parking_lot::Mutex; | |
| struct Session(Arc<Mutex<LiteSVM>>); | |
| impl std::ops::Deref for Session { | |
| type Target = Arc<Mutex<LiteSVM>>; | |
| fn deref(&self) -> &Self::Target { &self.0 } | |
| } | |
| struct Sessions(Mutex<HashMap<String, Arc<Mutex<LiteSVM>>>>); | |
| impl Sessions { | |
| pub fn new() -> Self { | |
| Sessions(Default::default()) | |
| } | |
| } | |
| use std::{convert::Infallible, future::Ready}; | |
| use actix_web::web::{Bytes, Data, Json, Query}; | |
| use actix_web::middleware::Logger; | |
| use actix_web::{get, post, web, App, FromRequest, HttpServer}; | |
| impl FromRequest for Session { | |
| type Future = Ready<Result<Session, Infallible>>; | |
| type Error = Infallible; | |
| fn from_request(req: &actix_web::HttpRequest, _payload: &mut actix_web::dev::Payload) -> Self::Future { | |
| let session_id = req.match_info().get("session_id").unwrap(); | |
| let state = req.app_data::<Data<Sessions>>().unwrap(); | |
| let m = state.0.lock() | |
| .entry(session_id.to_string()) | |
| .or_insert_with(|| Arc::new(Mutex::new(LiteSVM::new()))).clone(); | |
| ready(Ok(Session(m))) | |
| } | |
| } | |
| #[actix_web::main] | |
| async fn main() -> std::io::Result<()> { | |
| env_logger::init_from_env(env_logger::Env::default().default_filter_or("info")); | |
| let sessions = Data::new(Sessions::new()); | |
| HttpServer::new(move || { | |
| App::new() | |
| .wrap(Logger::default()) | |
| .app_data(sessions.clone()) | |
| .service(index) | |
| .service( | |
| web::scope("/chain/{session_id}") | |
| .service(latest_blockhash) | |
| .service(get_sigverify) | |
| .service(get_clock) | |
| .service(get_account) | |
| .service(airdrop) | |
| .service(expire_blockhash) | |
| .service(add_program_from_file) | |
| .service(simulate_transaction) | |
| .service(send_transaction) | |
| .service(add_program_from_file) | |
| ) | |
| }) | |
| .bind(("127.0.0.1", 8088))? | |
| .run() | |
| .await | |
| } | |
| struct Pubkey(solana_pubkey::Pubkey); | |
| impl<'de> serde::Deserialize<'de> for Pubkey { | |
| fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { | |
| let s = String::deserialize(deserializer)?; | |
| solana_pubkey::Pubkey::from_str(s.as_str()).map(Pubkey) | |
| .map_err(|_| serde::de::Error::custom("invalid pubkey")) | |
| } | |
| } | |
| impl std::ops::Deref for Pubkey { | |
| type Target = solana_pubkey::Pubkey; | |
| fn deref(&self) -> &Self::Target { &self.0 } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment