use kuru_sdk_rs::{ self, kuru_forwarder::{ForwardRequest, KuruForwarder, KuruRequest}, orderbook::{LimitOrder, MarketOrder}, }; use std::{ env, error::Error, future::{pending, Future}, sync::Arc, }; use alloy::{ network::{Ethereum, EthereumWallet}, primitives::{ aliases::{U24, U96}, Address, Bytes, FixedBytes, U256, U8, }, providers::{ fillers::{ BlobGasFiller, ChainIdFiller, FillProvider, GasFiller, JoinFill, NonceFiller, WalletFiller, }, Identity, PendingTransaction, PendingTransactionBuilder, Provider, ProviderBuilder, RootProvider, }, rpc::types::{TransactionReceipt, TransactionRequest}, signers::local::PrivateKeySigner, sol, transports::{http::Http, BoxFuture, Transport}, }; use alloy_json_abi::{Function, JsonAbi}; use alloy_json_rpc::RequestPacket; use dotenv::dotenv; use eyre::Result; pub use kuru_sdk_rs::Token; use kuru_sdk_rs::{ orderbook::{self, Order, OrderSide}, process_orders, utils, MarginAccount, }; use orderbook::Orderbook; use rand::{random, Rng}; use reqwest::{Client, Url}; use tokio::{ runtime::Runtime, sync::{mpsc, watch, Mutex}, task, time::{sleep, Duration}, }; use tower_service::Service; use utils::{fetch_kuru_order_id, tx_hash_to_id}; fn setup() -> ( Arc< FillProvider< JoinFill< JoinFill< Identity, JoinFill< GasFiller, JoinFill>, >, >, WalletFiller, >, RootProvider>, Http, Ethereum, >, >, Arc, ) { dotenv().ok(); let pk = env::var("PK").expect("Private key not found in env"); let signer: PrivateKeySigner = pk.parse().expect("Error parsing private key"); let user = signer.address(); println!("Signer Address: {}", user); let wallet = EthereumWallet::from(signer.clone()); let rpc_url: Url = env::var("RPC_URL") .expect("RPC URL not found in env") .parse() .expect("Parsing URL unsuccessful"); let provider = Arc::new( ProviderBuilder::new() .with_recommended_fillers() .wallet(wallet.clone()) .on_http(rpc_url), ); (provider, Arc::new(wallet)) } fn generate_random_orders(num_orders: usize) -> Vec { let mut rng = rand::thread_rng(); let mut orders = Vec::with_capacity(num_orders); for _ in 0..num_orders { let price = format!("{:.2}", rng.gen_range(1.0..100.0)); let size = format!("{:.2}", rng.gen_range(1.0..100.0)); let order_type = if rng.gen_bool(0.5) { OrderSide::BuyOrder } else { OrderSide::SellOrder }; let min_amount_out = "0".to_string(); let is_margin = rng.gen_bool(0.5); let post_only = rng.gen_bool(0.5); // limit or market order if rng.gen_bool(0.5) { orders.push(Order::Limit(LimitOrder { price, size, order_type, post_only, })); } else { orders.push(Order::Market(MarketOrder { size, min_amount_out, is_margin, order_type, fill_or_kill: rng.gen_bool(0.5), })); }; } orders } enum RunType { Local, Devnet, } #[tokio::main] async fn main() -> Result<()> { let run_type: RunType = RunType::Local; let localhost_margin_acc = "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318"; let localhost_orderbook = "0x6620454f509ce29875953ecdb1765E8Bf54422CC"; let localhost_forwarder = "0x0165878A594ca255338adfa4d48449f69242Eb8F"; let localhost_token_a = "0x5FbDB2315678afecb367f032d93F642f64180aa3"; let localhost_token_b = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"; let devnet_margin_acc = "0x2A3e86DF318d37895e320Ff49b22B1610D0Eb8Dc"; let devnet_orderbook = "0x3935fd532c12ad0cc5410b5a07b827c2a0feb869"; let devnet_forwarder = ""; let devnet_token_a = "0x962EfdaB911D02F6c7a73D84200FEb9Fe763770c"; let devnet_token_b = "0x962EfdaB911D02F6c7a73D84200FEb9Fe763770c"; let (provider, wallet) = setup(); let user = wallet.default_signer().address(); let (margin_account, orderbook, forwarder, wbtc, usdc) = match run_type { RunType::Local => { let margin_account = MarginAccount::new("margin acc", localhost_margin_acc, provider.clone()).unwrap(); let wbtc = Token::new("wbtc", localhost_token_a, provider.clone()).unwrap(); let usdc = Token::new("usdc", localhost_token_b, provider.clone()).unwrap(); let orderbook = Arc::new( Orderbook::new(localhost_orderbook, Arc::clone(&provider)) .await .unwrap(), ); let forwarder = Arc::new( KuruForwarder::new(&localhost_forwarder, Arc::clone(&provider), wallet.clone()) .unwrap(), ); (margin_account, orderbook, forwarder, wbtc, usdc) } RunType::Devnet => { let margin_account = MarginAccount::new("margin acc", devnet_margin_acc, provider.clone()).unwrap(); let wbtc = Token::new("wbtc", devnet_token_a, provider.clone()).unwrap(); let usdc = Token::new("usdc", devnet_token_b, provider.clone()).unwrap(); let orderbook = Arc::new( Orderbook::new(devnet_orderbook, Arc::clone(&provider)) .await .unwrap(), ); let forwarder = Arc::new( KuruForwarder::new(&devnet_forwarder, Arc::clone(&provider), wallet.clone()) .unwrap(), ); (margin_account, orderbook, forwarder, wbtc, usdc) } }; let amount: alloy::primitives::Uint<256, 4> = U256::from(100000000) * U256::from(10).pow(U256::from(18)); let _ = margin_account.deposit(&user, &usdc, &amount).await; let _ = margin_account.deposit(&user, &wbtc, &amount).await; let data = "0x532c46db00000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"; let forward_request = ForwardRequest { from: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266".parse()?, market: "0x6620454f509ce29875953ecdb1765E8Bf54422CC".parse()?, value: U256::ZERO, nonce: U256::ZERO, data: data_str_to_bytes(data).unwrap(), }; let signature = "0x489f2e040775c5c03a11b52e45d0143d2525772537a6c97be31bd29a1f51d1f6022471b865c08fe3bfd686ac20cee45a6e5a46b32d25d3aa20e6515adf7bb31b1c"; let tx_receipt = forwarder .execute( KuruRequest::ForwardRequest(forward_request), signature.to_string(), ) .await .unwrap(); dbg!(tx_receipt); Ok(()) } fn data_str_to_bytes(data: &str) -> Result> { let stripped_data = data.strip_prefix("0x").unwrap_or(data); let bytes = hex::decode(stripped_data)?; let data_bytes = Bytes::from(bytes); Ok(data_bytes) }