Skip to content

Instantly share code, notes, and snippets.

@tilacog
Created October 20, 2022 22:49
Show Gist options
  • Save tilacog/3e370a7ecd3d2c103c018979ea8db41c to your computer and use it in GitHub Desktop.
Save tilacog/3e370a7ecd3d2c103c018979ea8db41c to your computer and use it in GitHub Desktop.
Transaction manager
use std::time::{Duration, Instant};
/// How many confirmations to wait for
const REQUIRED_CONFIRMATIONS: u32 = 1;
const WAIT_TIME_IN_SECONDS: u64 = 60;
const HARD_TIMEOUT_FACTOR: u64 = 10;
/// Time before giving up on waiting for transaction confirmation. After this time, we expect the
/// Transaction Monitor to bump the gas price and re-broadcast the transaction.
const LIGHT_TIMEOUT: Duration = Duration::from_secs(WAIT_TIME_IN_SECONDS);
/// Hard time out to give up monitoring this transaction at all. After this time, we expect the
/// Transaction Monitor to return an error to the Oracle runner.
const HARD_TIMEOUT: Duration = Duration::from_secs(WAIT_TIME_IN_SECONDS * HARD_TIMEOUT_FACTOR);
/// Interval used to check for confirmation. Should be smaller than [`TIMEOUT`]
const SLEEP: Duration = Duration::from_secs(10);
///
const GAS_BUMP_FACTOR: f32 = 1.2;
// stubs
struct ClientError;
enum MonitorError {
Client(ClientError),
TimedOut { confirmations: u32 },
}
struct Client;
struct TransactionHash;
struct Transaction {}
/// Necessary data to create a signed transaction
struct TransactionInputs {
calldata: Vec<u8>,
signing_key: (),
gas_price: u64,
}
impl TransactionInputs {
fn sign(&self) -> Transaction {
Transaction {}
}
}
/// Monitors transaction confirmation.
struct TransactionMonitor {
/// Necessary to recreate the transaction in case gas price goes up
transaction_inputs: TransactionInputs,
/// JSON RPC client
client: Client,
}
impl TransactionMonitor {
fn broadcast(_transaction: Transaction) -> Result<TransactionHash, MonitorError> {
Ok(TransactionHash) // fake
}
/// Recalculates the gas price
fn bump_gas(&mut self, factor: f32) {
self.transaction_inputs.gas_price =
(self.transaction_inputs.gas_price as f32 * factor) as u64
}
/// Fetches how many confirmations the transaction hash has
fn confirmations(&self, _transaction_hash: &TransactionHash) -> Result<u32, MonitorError> {
Ok(1) // fake
}
fn wait_for_confirmation(&mut self, hash: &TransactionHash) -> Result<(), MonitorError> {
let start = Instant::now();
loop {
let confirmations = self.confirmations(&hash)?;
if confirmations > REQUIRED_CONFIRMATIONS {
return Ok(());
}
// check if we timed out.
// we allow failing even if we have confirmations.
// it is up to the caller to decide if it should wait a little more or not
let elapsed = start.elapsed();
if elapsed > LIGHT_TIMEOUT {
return Err(MonitorError::TimedOut { confirmations });
}
// sleep and wait some more time
std::thread::sleep(SLEEP);
}
}
fn monitor(&mut self, hash: &TransactionHash) -> Result<(), MonitorError> {
let start = Instant::now();
loop {
match self.wait_for_confirmation(hash) {
Ok(()) => break Ok(()),
Err(MonitorError::Client(client_error)) => {
// what should we do here?
// we might want to retry depending on the error's nature.
// remember that we use exponential backoff for JRPC calls.
todo!()
}
Err(MonitorError::TimedOut { confirmations }) => {
let elapsed = start.elapsed();
let within_treshold = elapsed < HARD_TIMEOUT;
if confirmations == 0 && within_treshold {
// This is the typical case where we want to bump the gas price and
// re-broadcast the transaction.
self.bump_gas(GAS_BUMP_FACTOR);
let new_transaction = self.transaction_inputs.sign();
Self::broadcast(new_transaction)?;
} else if within_treshold {
// what should we do if we time out with confirmations?
// maybe we can wait some more time before declare the failure of this
// transaction.
todo!()
} else {
// This is the worst case we can get. At this point, we tried to bump gas
// many times and still got no confirmations.
break Err(MonitorError::TimedOut { confirmations });
}
}
}
}
}
}
fn main() {
println!("Hello, world!");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment