diff --git a/apps/indexer/Cargo.lock b/apps/indexer/Cargo.lock index 38bbdec2..8c7a4134 100644 --- a/apps/indexer/Cargo.lock +++ b/apps/indexer/Cargo.lock @@ -3109,6 +3109,7 @@ dependencies = [ "env_logger", "futures", "log", + "mongodb", "serde", "serde_json", "starklane_indexer", diff --git a/apps/indexer/refund/Cargo.toml b/apps/indexer/refund/Cargo.toml index 9fde5038..555a02c9 100644 --- a/apps/indexer/refund/Cargo.toml +++ b/apps/indexer/refund/Cargo.toml @@ -9,11 +9,12 @@ anyhow = "1.0" clap = { version = "4.3.19", features = ["derive", "env", "string"] } csv = "1.3.0" env_logger = "0.10.0" -log = "0.4.17" futures = "0.3.28" +log = "0.4.17" +mongodb = "2.6.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +starknet = "0.10.0" tokio = { version = "1", features = ["full"] } starklane_indexer = { version = "0.1.0", path= ".."} -starknet = "0.10.0" diff --git a/apps/indexer/refund/src/bin/send_refund.rs b/apps/indexer/refund/src/bin/send_refund.rs index 3c4d65b1..56010aa6 100644 --- a/apps/indexer/refund/src/bin/send_refund.rs +++ b/apps/indexer/refund/src/bin/send_refund.rs @@ -3,7 +3,10 @@ use std::time::Duration; use anyhow::{anyhow, Result}; use clap::Parser; +use mongodb::{bson::doc, options::ClientOptions, Client, Collection}; use refund::Refund; +use serde::{Deserialize, Serialize}; +use starklane_indexer::{storage::extract_database_name, utils::normalize_hex}; use starknet::{ accounts::{Account, Call, ConnectedAccount, ExecutionEncoding, SingleOwnerAccount}, core::{ @@ -14,6 +17,49 @@ use starknet::{ signers::{LocalWallet, SigningKey}, }; +#[derive(Debug, Serialize, Deserialize)] +struct RefundSend { + pub l1_hash: String, + pub l2_hash: String, + pub refund: Refund, +} + +struct MongoStore { + refunded: Collection, +} + +impl MongoStore { + pub async fn new(connection_string: &str, db_name: &str) -> Result { + let client_options = ClientOptions::parse(connection_string).await?; + let client = Client::with_options(client_options)?; + let db = client.database(db_name); + let refunded = db.collection::("refunded"); + + Ok(MongoStore { refunded }) + } + + pub async fn add(&self, l2_hash: String, refund: &Refund) -> Result<()> { + let doc = RefundSend { + l1_hash: refund.tx_hash.clone(), + l2_hash: normalize_hex(&l2_hash).unwrap(), + refund: refund.clone(), + }; + self.refunded.insert_one(doc, None).await?; + Ok(()) + } + + pub async fn check(&self, l1_hash: String) -> Result> { + match self + .refunded + .find_one(doc! { "l1_hash": l1_hash}, None) + .await + { + Ok(r) => Ok(r), + Err(e) => Err(anyhow!("Failed to query DB: {:?}", e)), + } + } +} + #[derive(Parser, Debug)] #[clap(about = "Send refund")] struct Args { @@ -49,9 +95,6 @@ fn refund_to_call(refund: &Refund) -> Call { let decimals = 8; let amount = refund.amount * (10_u64.pow(decimals) as f64); let amount = (amount as u128) * (10_u64.pow(token_decimals - decimals) as u128); - // FIXME: for testing purpose!! - let amount = amount / 100; - // FIXME let amount = FieldElement::from(amount); Call { to: token_address, @@ -94,7 +137,6 @@ where } } - #[tokio::main] async fn main() -> Result<()> { env_logger::init(); @@ -105,6 +147,9 @@ async fn main() -> Result<()> { let address = args.address; let private_key = args.private_key; + let dbname = extract_database_name(&args.mongodb) + .expect("Database name couldn't be extracted from the connection string"); + let provider = JsonRpcClient::new(HttpTransport::new(Url::parse(&rpc).unwrap())); let signer = LocalWallet::from(SigningKey::from_secret_scalar( FieldElement::from_hex_be(&private_key).unwrap(), @@ -114,28 +159,49 @@ async fn main() -> Result<()> { let account = SingleOwnerAccount::new(provider, signer, address, chain_id, ExecutionEncoding::New); + log::debug!("Account address: {:?}", account.address()); let mut calls: Vec = vec![]; + let mut refunds: Vec = vec![]; + + let mongo = MongoStore::new(&args.mongodb, dbname).await?; let mut rdr = csv::Reader::from_path(input)?; for elem in rdr.deserialize() { let refund: Refund = elem?; - log::debug!("{:?}", refund); + if let Ok(Some(_check)) = mongo.check(refund.clone().tx_hash).await { + log::debug!("Already send: {:?}", refund); + continue; + } + log::debug!("To send: {:?}", refund); let call = refund_to_call(&refund); calls.push(call); + refunds.push(refund); + } + + if calls.is_empty() { + log::info!("No refund to send"); + return Ok(()); } - // log::debug!("Calls: {:?}", calls); - let invoke = account - .execute(calls) - .send() - .await - .unwrap(); + let invoke = account.execute(calls).send().await.unwrap(); let provider = account.provider(); log::debug!("Wait for transaction: {:?}", invoke.transaction_hash); - let result = watch_tx(provider, invoke.transaction_hash, Duration::from_millis(10000)).await; + let result = watch_tx( + provider, + invoke.transaction_hash, + Duration::from_millis(10000), + ) + .await; match result { - Ok(_) => Ok(()), - Err(e) => Err(anyhow!("Transaction failed! {:?}", e)) + Ok(_) => { + for refund in refunds { + let l2_hash = format!("{:#064x}", invoke.transaction_hash); + let l2_hash = normalize_hex(&l2_hash).unwrap(); + mongo.add(l2_hash, &refund).await? + } + Ok(()) + } + Err(e) => Err(anyhow!("Transaction failed! {:?}", e)), } }