Skip to main content

hypercall_db/traits/
directive_outbox.rs

1//! Directive outbox persistence traits.
2
3use alloy::primitives::Address;
4use anyhow::Result;
5use async_trait::async_trait;
6use hypercall_types::WalletAddress;
7use rust_decimal::Decimal;
8
9use crate::{
10    DepositMonitoringRow, DirectiveOutboxDeliveryMetricsRow, DirectiveOutboxRecentRow,
11    DirectiveOutboxRow, DirectiveStatusRow, HypercoreCashLedgerApply,
12    HypercoreCashLedgerApplyResult, OptionInstrumentForCredit, RsmDepositCreditClaimInput,
13    RsmDepositCreditClaimRecord, RsmUsdcDepositMatch,
14};
15
16pub trait DirectiveOutboxReader: Send + Sync {
17    fn claim_next_directive_outbox_item_sync(&self) -> Result<Option<DirectiveOutboxRow>>;
18
19    /// Look up the delivery status of a single directive by its ID.
20    fn get_directive_status_sync(&self, directive_id: &str) -> Result<Option<DirectiveStatusRow>>;
21
22    /// Return withdrawal directive history for a wallet, most recent first.
23    fn get_withdrawal_history_sync(
24        &self,
25        wallet: &WalletAddress,
26        limit: i64,
27    ) -> Result<Vec<DirectiveStatusRow>>;
28
29    /// Return retryable directive delivery backlog grouped by stable low-cardinality labels.
30    fn list_directive_outbox_delivery_metrics_sync(
31        &self,
32    ) -> Result<Vec<DirectiveOutboxDeliveryMetricsRow>>;
33
34    /// Return recent directive outbox rows for operator debugging.
35    fn list_recent_directive_outbox_rows_sync(
36        &self,
37        limit: i64,
38        offset: i64,
39    ) -> Result<Vec<DirectiveOutboxRecentRow>>;
40}
41
42#[async_trait]
43pub trait AsyncDirectiveOutboxReader: Send + Sync {
44    /// Check whether a directive outbox row exists for the given directive_id.
45    async fn directive_outbox_exists(&self, directive_id: &str) -> Result<bool>;
46
47    /// Look up the delivery status of a single directive by its ID.
48    async fn get_directive_status(&self, directive_id: &str) -> Result<Option<DirectiveStatusRow>>;
49
50    /// Return withdrawal directive history for a wallet, most recent first.
51    async fn get_withdrawal_history(
52        &self,
53        wallet: &WalletAddress,
54        limit: i64,
55    ) -> Result<Vec<DirectiveStatusRow>>;
56
57    /// Return retryable directive delivery backlog grouped by stable low-cardinality labels.
58    async fn list_directive_outbox_delivery_metrics(
59        &self,
60    ) -> Result<Vec<DirectiveOutboxDeliveryMetricsRow>>;
61
62    /// Return recent directive outbox rows for operator debugging.
63    async fn list_recent_directive_outbox_rows(
64        &self,
65        limit: i64,
66        offset: i64,
67    ) -> Result<Vec<DirectiveOutboxRecentRow>>;
68}
69
70pub trait DirectiveOutboxWriter: DirectiveOutboxReader {
71    fn mark_directive_outbox_delivery_failed_sync(
72        &self,
73        outbox_seq: i64,
74        error: &str,
75    ) -> Result<()>;
76
77    fn mark_directive_outbox_dead_lettered_sync(&self, outbox_seq: i64, error: &str) -> Result<()>;
78
79    /// Stop retrying a directive whose on-chain outcome is unknown.
80    ///
81    /// This intentionally does not transition the domain status to failed and
82    /// must not restore withdrawal debits.
83    fn mark_directive_outbox_manual_reconciliation_sync(
84        &self,
85        outbox_seq: i64,
86        error: &str,
87    ) -> Result<()>;
88
89    /// Store the submitter-owned submission pointer for a directive.
90    ///
91    /// The directive outbox does not own transaction attempts, receipts, or
92    /// replacement state. It stores only the submitter identity and nonce needed
93    /// to query submitter-owned state.
94    fn record_directive_submitter_submission_sync(
95        &self,
96        request_id: &str,
97        submitter_address: &Address,
98        submitter_nonce: u64,
99    ) -> Result<()>;
100
101    /// Persist transaction-submitter status updates for a directive.
102    ///
103    /// Failed and expired withdrawal directives are terminal for automatic
104    /// delivery, but remain operator-reconciled. This method must not refund
105    /// or otherwise restore withdrawal debits.
106    fn persist_directive_transaction_update_sync(
107        &self,
108        request_id: &str,
109        status: hypercall_types::TransactionStatus,
110        tx_hash: Option<&str>,
111        error: Option<&str>,
112    ) -> Result<()>;
113}
114
115#[async_trait]
116pub trait AsyncDirectiveOutboxWriter: AsyncDirectiveOutboxReader {
117    /// Store the submitter-owned submission pointer for a directive.
118    ///
119    /// The directive outbox does not own transaction attempts, receipts, or
120    /// replacement state. It stores only the submitter identity and nonce needed
121    /// to query submitter-owned state.
122    async fn record_directive_submitter_submission(
123        &self,
124        request_id: &str,
125        submitter_address: &Address,
126        submitter_nonce: u64,
127    ) -> Result<()>;
128
129    /// Persist transaction-submitter status updates for a directive.
130    ///
131    /// Failed and expired withdrawal directives are terminal for automatic
132    /// delivery, but remain operator-reconciled. This method must not refund
133    /// or otherwise restore withdrawal debits.
134    async fn persist_directive_transaction_update(
135        &self,
136        request_id: &str,
137        status: hypercall_types::TransactionStatus,
138        tx_hash: Option<&str>,
139        error: Option<&str>,
140    ) -> Result<()>;
141}
142
143#[async_trait]
144pub trait RsmCreditReader: Send + Sync {
145    /// Check whether a directive outbox row exists for the given directive_id.
146    fn directive_outbox_exists_sync(&self, directive_id: &str) -> Result<bool>;
147
148    /// Return the replay watermark for RSM deposit credits.
149    async fn get_max_rsm_deposit_credit_observed_block(&self) -> Result<Option<u64>>;
150
151    /// Return the replay watermark for exchange cash ledger events.
152    fn get_exchange_cash_ledger_watermark_sync(&self) -> Result<Option<i64>>;
153
154    /// Look up an option instrument by its on-chain token address.
155    async fn get_option_instrument_for_credit(
156        &self,
157        token: &WalletAddress,
158    ) -> Result<Option<OptionInstrumentForCredit>>;
159
160    /// Return recent cash deposit attribution rows for admin monitoring.
161    async fn list_recent_cash_deposit_monitoring_rows(
162        &self,
163        limit: i64,
164        offset: i64,
165    ) -> Result<Vec<DepositMonitoringRow>>;
166}
167
168#[async_trait]
169pub trait RsmCreditWriter: RsmCreditReader {
170    /// Ensure an observed deposit account has a tier row before crediting.
171    async fn ensure_observed_deposit_account(&self, account: &WalletAddress) -> Result<()>;
172
173    /// Claim an observed RSM deposit credit idempotently.
174    async fn claim_rsm_deposit_credit(
175        &self,
176        input: &RsmDepositCreditClaimInput,
177    ) -> Result<RsmDepositCreditClaimRecord>;
178
179    /// Mark an RSM deposit credit as submitted to the engine/journal path.
180    async fn mark_rsm_deposit_credit_submitted(&self, request_id: &str) -> Result<()>;
181
182    /// Mark an RSM deposit credit as failed so observers can quarantine invalid public events.
183    async fn mark_rsm_deposit_credit_failed(&self, request_id: &str, error: &str) -> Result<()>;
184
185    /// Match a pending Exchange.UsdcDeposit event to one observed HyperCore cash deposit.
186    async fn pending_rsm_usdc_deposit_for_amount(
187        &self,
188        amount_wei: &str,
189    ) -> Result<Option<RsmUsdcDepositMatch>>;
190
191    /// Match a pending Exchange.UsdcDeposit event to a CoreWriter writer-action EVM tx hash.
192    async fn pending_rsm_usdc_deposit_for_evm_tx_hash(
193        &self,
194        evm_tx_hash: &str,
195        amount_wei: &str,
196    ) -> Result<Option<RsmUsdcDepositMatch>>;
197
198    /// Recover a still-pending Exchange.UsdcDeposit request for an already credited HyperCore event.
199    async fn pending_rsm_usdc_deposit_for_credited_hypercore_event(
200        &self,
201        event_hash: &str,
202        amount_wei: &str,
203    ) -> Result<Option<RsmUsdcDepositMatch>>;
204
205    /// Return the wallet already credited for a HyperCore cash deposit event, if any.
206    async fn credited_wallet_for_hypercore_cash_event(
207        &self,
208        event_hash: &str,
209    ) -> Result<Option<WalletAddress>>;
210
211    /// Return true when a HyperCore cash deposit event is already recorded as non-crediting.
212    async fn non_crediting_hypercore_cash_event(
213        &self,
214        event_hash: &str,
215        amount_usdc: Decimal,
216    ) -> Result<bool>;
217
218    /// Apply a HyperCore cash deposit idempotently.
219    async fn apply_hypercore_cash_deposit(
220        &self,
221        input: &HypercoreCashLedgerApply,
222    ) -> Result<HypercoreCashLedgerApplyResult>;
223
224    /// Persist a replayable cash deposit row when margin mode is temporarily unavailable.
225    async fn record_hypercore_cash_deposit_pending_margin_mode(
226        &self,
227        input: &HypercoreCashLedgerApply,
228    ) -> Result<()>;
229}