@@ -317,6 +317,10 @@ pub struct State {
317317 // reset in case of a successful response
318318 backoff_info : BackoffInfo ,
319319
320+ /// Allows the sender to go over escrow balance
321+ /// limited to `max_amount_willing_to_lose_grt`
322+ trusted_sender : bool ,
323+
320324 // Config forwarded to [SenderAllocation]
321325 config : & ' static SenderAccountConfig ,
322326}
@@ -343,6 +347,9 @@ pub struct SenderAccountConfig {
343347 ///
344348 /// This is reached if the database is too slow
345349 pub tap_sender_timeout : Duration ,
350+ /// Senders that are allowed to spend up to `max_amount_willing_to_lose_grt`
351+ /// over the escrow balance
352+ pub trusted_senders : HashSet < Address > ,
346353}
347354
348355impl SenderAccountConfig {
@@ -357,6 +364,7 @@ impl SenderAccountConfig {
357364 trigger_value : config. tap . get_trigger_value ( ) ,
358365 rav_request_timeout : config. tap . rav_request . request_timeout_secs ,
359366 tap_sender_timeout : config. tap . sender_timeout_secs ,
367+ trusted_senders : config. tap . trusted_senders . clone ( ) ,
360368 }
361369 }
362370}
@@ -531,14 +539,22 @@ impl State {
531539 fn deny_condition_reached ( & self ) -> bool {
532540 let pending_ravs = self . rav_tracker . get_total_fee ( ) ;
533541 let unaggregated_fees = self . sender_fee_tracker . get_total_fee ( ) ;
534- let pending_fees_over_balance =
535- U256 :: from ( pending_ravs + unaggregated_fees) >= self . sender_balance ;
536542 let max_amount_willing_to_lose = self . config . max_amount_willing_to_lose_grt ;
543+
544+ // if it's a trusted sender, allow to spend up to max_amount_willing_to_lose
545+ let balance = if self . trusted_sender {
546+ self . sender_balance + U256 :: from ( max_amount_willing_to_lose)
547+ } else {
548+ self . sender_balance
549+ } ;
550+
551+ let pending_fees_over_balance = U256 :: from ( pending_ravs + unaggregated_fees) >= balance;
537552 let invalid_receipt_fees = self . invalid_receipts_tracker . get_total_fee ( ) ;
538553 let total_fee_over_max_value =
539554 unaggregated_fees + invalid_receipt_fees >= max_amount_willing_to_lose;
540555
541556 tracing:: trace!(
557+ trusted_sender = %self . trusted_sender,
542558 %pending_fees_over_balance,
543559 %total_fee_over_max_value,
544560 "Verifying if deny condition was reached." ,
@@ -550,6 +566,7 @@ impl State {
550566 /// Will update [`State::denied`], as well as the denylist table in the database.
551567 async fn add_to_denylist ( & mut self ) {
552568 tracing:: warn!(
569+ trusted_sender = %self . trusted_sender,
553570 fee_tracker = self . sender_fee_tracker. get_total_fee( ) ,
554571 rav_tracker = self . rav_tracker. get_total_fee( ) ,
555572 max_amount_willing_to_lose = self . config. max_amount_willing_to_lose_grt,
@@ -841,6 +858,7 @@ impl Actor for SenderAccount {
841858 aggregator_v1,
842859 aggregator_v2,
843860 backoff_info : BackoffInfo :: default ( ) ,
861+ trusted_sender : config. trusted_senders . contains ( & sender_id) ,
844862 config,
845863 } ;
846864
@@ -1284,7 +1302,7 @@ pub mod tests {
12841302 Mock , MockServer , ResponseTemplate ,
12851303 } ;
12861304
1287- use super :: SenderAccountMessage ;
1305+ use super :: { RavInformation , SenderAccountMessage } ;
12881306 use crate :: {
12891307 agent:: {
12901308 sender_account:: ReceiptFees , sender_accounts_manager:: AllocationId ,
@@ -1294,7 +1312,7 @@ pub mod tests {
12941312 assert_not_triggered, assert_triggered,
12951313 test:: {
12961314 actors:: { create_mock_sender_allocation, MockSenderAllocation } ,
1297- create_rav, create_sender_account, store_rav_with_options, TRIGGER_VALUE ,
1315+ create_rav, create_sender_account, store_rav_with_options, ESCROW_VALUE , TRIGGER_VALUE ,
12981316 } ,
12991317 } ;
13001318
@@ -1343,7 +1361,6 @@ pub mod tests {
13431361 }
13441362
13451363 /// Prefix shared between tests so we don't have conflicts in the global registry
1346- const ESCROW_VALUE : u128 = 1000 ;
13471364 const BUFFER_DURATION : Duration = Duration :: from_millis ( 100 ) ;
13481365 const RETRY_DURATION : Duration = Duration :: from_millis ( 1000 ) ;
13491366
@@ -1986,6 +2003,76 @@ pub mod tests {
19862003 sender_account. stop_and_wait ( None , None ) . await . unwrap ( ) ;
19872004 }
19882005
2006+ #[ sqlx:: test( migrations = "../../migrations" ) ]
2007+ async fn test_trusted_sender ( pgpool : PgPool ) {
2008+ let max_amount_willing_to_lose_grt = ESCROW_VALUE / 10 ;
2009+ // initialize with no trigger value and no max receipt deny
2010+ let ( sender_account, notify, prefix, _) = create_sender_account ( )
2011+ . pgpool ( pgpool)
2012+ . trusted_sender ( true )
2013+ . rav_request_trigger_value ( u128:: MAX )
2014+ . max_amount_willing_to_lose_grt ( max_amount_willing_to_lose_grt)
2015+ . call ( )
2016+ . await ;
2017+
2018+ let ( mock_sender_allocation, _) =
2019+ MockSenderAllocation :: new_with_next_rav_value ( sender_account. clone ( ) ) ;
2020+
2021+ let name = format ! ( "{}:{}:{}" , prefix, SENDER . 1 , ALLOCATION_ID_0 ) ;
2022+ let ( allocation, _) = MockSenderAllocation :: spawn ( Some ( name) , mock_sender_allocation, ( ) )
2023+ . await
2024+ . unwrap ( ) ;
2025+
2026+ async fn get_deny_status ( sender_account : & ActorRef < SenderAccountMessage > ) -> bool {
2027+ call ! ( sender_account, SenderAccountMessage :: GetDeny ) . unwrap ( )
2028+ }
2029+
2030+ macro_rules! update_receipt_fees {
2031+ ( $value: expr) => {
2032+ sender_account
2033+ . cast( SenderAccountMessage :: UpdateRav ( RavInformation {
2034+ allocation_id: ALLOCATION_ID_0 ,
2035+ value_aggregate: $value,
2036+ } ) )
2037+ . unwrap( ) ;
2038+
2039+ flush_messages( & notify) . await ;
2040+ } ;
2041+ }
2042+
2043+ let deny = call ! ( sender_account, SenderAccountMessage :: GetDeny ) . unwrap ( ) ;
2044+ assert ! ( !deny) ;
2045+
2046+ update_receipt_fees ! ( ESCROW_VALUE - 1 ) ;
2047+ let deny = get_deny_status ( & sender_account) . await ;
2048+ assert ! ( !deny, "it shouldn't deny a sender below escrow balance" ) ;
2049+
2050+ update_receipt_fees ! ( ESCROW_VALUE ) ;
2051+ let deny = get_deny_status ( & sender_account) . await ;
2052+ assert ! (
2053+ !deny,
2054+ "it shouldn't deny a trusted sender below escrow balance + max willing to lose"
2055+ ) ;
2056+
2057+ update_receipt_fees ! ( ESCROW_VALUE + max_amount_willing_to_lose_grt - 1 ) ;
2058+ let deny = get_deny_status ( & sender_account) . await ;
2059+ assert ! (
2060+ !deny,
2061+ "it shouldn't deny a trusted sender below escrow balance + max willing to lose"
2062+ ) ;
2063+
2064+ update_receipt_fees ! ( ESCROW_VALUE + max_amount_willing_to_lose_grt) ;
2065+ let deny = get_deny_status ( & sender_account) . await ;
2066+ assert ! (
2067+ deny,
2068+ "it should deny a trusted sender over escrow balance + max willing to lose"
2069+ ) ;
2070+
2071+ allocation. stop_and_wait ( None , None ) . await . unwrap ( ) ;
2072+
2073+ sender_account. stop_and_wait ( None , None ) . await . unwrap ( ) ;
2074+ }
2075+
19892076 #[ sqlx:: test( migrations = "../../migrations" ) ]
19902077 async fn test_pending_rav_already_redeemed_and_redeem ( pgpool : PgPool ) {
19912078 // Start a mock graphql server using wiremock
0 commit comments