1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use crate::{
    execution::*,
    metrics::publish_expected_bitcoin_balance,
    service::{spawn_cancelable, ShutdownSender},
    system::VaultIdManager,
    Error,
};
use runtime::{InterBtcParachain, RedeemPallet, RequestRedeemEvent};
use std::time::Duration;
/// Listen for RequestRedeemEvent directed at this vault; upon reception, transfer
/// bitcoin and call execute_redeem
///
/// # Arguments
///
/// * `parachain_rpc` - the parachain RPC handle
/// * `btc_rpc` - the bitcoin RPC handle
/// * `network` - network the bitcoin network used (i.e. regtest/testnet/mainnet)
/// * `num_confirmations` - the number of bitcoin confirmation to await
pub async fn listen_for_redeem_requests(
    shutdown_tx: ShutdownSender,
    parachain_rpc: InterBtcParachain,
    vault_id_manager: VaultIdManager,
    num_confirmations: u32,
    payment_margin: Duration,
    auto_rbf: bool,
) -> Result<(), Error> {
    parachain_rpc
        .on_event::<RequestRedeemEvent, _, _, _>(
            |event| async {
                let vault = match vault_id_manager.get_vault(&event.vault_id).await {
                    Some(x) => x,
                    None => return, // event not directed at this vault
                };

                tracing::info!("Received redeem request: {:?}", event);

                let _ = publish_expected_bitcoin_balance(&vault, parachain_rpc.clone()).await;

                // within this event callback, we captured the arguments of listen_for_redeem_requests
                // by reference. Since spawn requires static lifetimes, we will need to capture the
                // arguments by value rather than by reference, so clone these:
                let parachain_rpc = parachain_rpc.clone();
                // Spawn a new task so that we handle these events concurrently
                spawn_cancelable(shutdown_tx.subscribe(), async move {
                    tracing::info!("Executing redeem #{:?}", event.redeem_id);
                    let result = async {
                        let request = Request::from_redeem_request(
                            *event.redeem_id,
                            parachain_rpc.get_redeem_request(*event.redeem_id).await?,
                            payment_margin,
                        )?;
                        request
                            .pay_and_execute(parachain_rpc, vault, num_confirmations, auto_rbf)
                            .await
                    }
                    .await;

                    match result {
                        Ok(_) => tracing::info!(
                            "Completed redeem request #{} with amount {}",
                            *event.redeem_id,
                            event.amount
                        ),
                        Err(e) => tracing::error!(
                            "Failed to process redeem request #{}: {}",
                            *event.redeem_id,
                            e.to_human()
                        ),
                    }
                });
            },
            |error| tracing::error!("Error reading redeem event: {}", error.to_human()),
        )
        .await?;
    Ok(())
}