1use std::str::FromStr;
36
37use hopr_primitive_types::prelude::*;
38use serde::{Deserialize, Serialize};
39use strum::{Display, EnumString, VariantNames};
40
41use crate::{
42 Strategy::AutoRedeeming, auto_funding::AutoFundingStrategyConfig, auto_redeeming::AutoRedeemingStrategyConfig,
43 channel_finalizer::ClosureFinalizerStrategyConfig, strategy::MultiStrategyConfig,
44};
45
46pub mod auto_funding;
47pub mod auto_redeeming;
48pub mod channel_finalizer;
49pub mod errors;
50pub mod strategy;
51
52#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Display, EnumString, VariantNames)]
54#[strum(serialize_all = "snake_case")]
55pub enum Strategy {
56 AutoRedeeming(AutoRedeemingStrategyConfig),
57 AutoFunding(AutoFundingStrategyConfig),
58 ClosureFinalizer(ClosureFinalizerStrategyConfig),
59 Multi(MultiStrategyConfig),
60 Passive,
61}
62
63pub fn hopr_default_strategies() -> MultiStrategyConfig {
68 MultiStrategyConfig {
69 on_fail_continue: true,
70 allow_recursive: false,
71 execution_interval: 60,
72 strategies: vec![
73 AutoRedeeming(AutoRedeemingStrategyConfig {
78 redeem_all_on_close: true,
79 minimum_redeem_ticket_value: HoprBalance::from_str("1 wxHOPR").unwrap(),
80 redeem_on_winning: true,
81 }),
82 ],
83 }
84}
85
86impl Default for Strategy {
87 fn default() -> Self {
88 Self::Multi(hopr_default_strategies())
89 }
90}
91
92pub type StrategyConfig = MultiStrategyConfig;
94
95#[cfg(test)]
96pub(crate) mod tests {
97 use futures::{FutureExt, Stream, StreamExt, future::BoxFuture, stream::BoxStream};
98 use hopr_api::{
99 chain::{
100 ChainReadChannelOperations, ChainReceipt, ChainWriteChannelOperations, ChainWriteTicketOperations,
101 ChannelSelector,
102 },
103 db::TicketSelector,
104 };
105 use hopr_internal_types::{
106 channels::{ChannelEntry, ChannelId, ChannelStatus},
107 prelude::AcknowledgedTicket,
108 };
109 use hopr_primitive_types::{balance::HoprBalance, prelude::Address};
110
111 use crate::errors::StrategyError;
112
113 #[mockall::automock]
116 pub trait TestActions {
117 fn me(&self) -> &Address;
118 fn fund_channel(&self, channel_id: &ChannelId, amount: HoprBalance) -> Result<ChainReceipt, StrategyError>;
119 fn close_channel(&self, channel_id: &ChannelId) -> Result<(ChannelStatus, ChainReceipt), StrategyError>;
120 fn redeem_with_selector(&self, selector: TicketSelector) -> Vec<ChainReceipt>;
121 fn stream_channels(&self, selector: ChannelSelector) -> impl Stream<Item = ChannelEntry> + Send;
122 fn channel_by_id(&self, channel_id: &ChannelId) -> Option<ChannelEntry>;
123 }
124
125 pub struct MockChainActions<T>(pub std::sync::Arc<T>);
126
127 impl<T> Clone for MockChainActions<T> {
128 fn clone(&self) -> Self {
129 Self(self.0.clone())
130 }
131 }
132
133 #[async_trait::async_trait]
134 impl<T: TestActions + Send + Sync> ChainReadChannelOperations for MockChainActions<T> {
135 type Error = StrategyError;
136
137 fn me(&self) -> &Address {
138 self.0.me()
139 }
140
141 async fn channel_by_parties(&self, _: &Address, _: &Address) -> Result<Option<ChannelEntry>, Self::Error> {
142 unimplemented!()
143 }
144
145 async fn channel_by_id(&self, channel_id: &ChannelId) -> Result<Option<ChannelEntry>, Self::Error> {
146 Ok(self.0.channel_by_id(channel_id))
147 }
148
149 async fn stream_channels<'a>(
150 &'a self,
151 selector: ChannelSelector,
152 ) -> Result<BoxStream<'a, ChannelEntry>, Self::Error> {
153 Ok(self.0.stream_channels(selector).boxed())
154 }
155 }
156
157 #[async_trait::async_trait]
158 impl<T: TestActions + Send + Sync> ChainWriteChannelOperations for MockChainActions<T> {
159 type Error = StrategyError;
160
161 async fn open_channel<'a>(
162 &'a self,
163 _: &'a Address,
164 _: HoprBalance,
165 ) -> Result<BoxFuture<'a, Result<(ChannelId, ChainReceipt), Self::Error>>, Self::Error> {
166 unimplemented!()
167 }
168
169 async fn fund_channel<'a>(
170 &'a self,
171 channel_id: &'a ChannelId,
172 amount: HoprBalance,
173 ) -> Result<BoxFuture<'a, Result<ChainReceipt, Self::Error>>, Self::Error> {
174 Ok(futures::future::ready(self.0.fund_channel(channel_id, amount)).boxed())
175 }
176
177 async fn close_channel<'a>(
178 &'a self,
179 channel_id: &'a ChannelId,
180 ) -> Result<BoxFuture<'a, Result<(ChannelStatus, ChainReceipt), Self::Error>>, Self::Error> {
181 Ok(futures::future::ready(self.0.close_channel(channel_id)).boxed())
182 }
183 }
184
185 #[async_trait::async_trait]
186 impl<T: TestActions + Send + Sync> ChainWriteTicketOperations for MockChainActions<T> {
187 type Error = StrategyError;
188
189 async fn redeem_ticket(
190 &self,
191 _: AcknowledgedTicket,
192 ) -> Result<BoxFuture<'_, Result<ChainReceipt, Self::Error>>, Self::Error> {
193 unimplemented!()
194 }
195
196 async fn redeem_tickets_via_selector(
197 &self,
198 selector: TicketSelector,
199 ) -> Result<Vec<BoxFuture<'_, Result<ChainReceipt, Self::Error>>>, Self::Error> {
200 let receipts = self.0.redeem_with_selector(selector);
201 Ok(receipts
202 .into_iter()
203 .map(|r| futures::future::ready(Ok(r)).boxed())
204 .collect())
205 }
206 }
207}