@@ -10,6 +10,7 @@ use crate::{
1010 DEFAULT_CACHE_ACCOUNT_TTL , DEFAULT_CACHE_DEFAULT_TTL ,
1111 DEFAULT_FEE_PAYER_BALANCE_METRICS_EXPIRY_SECONDS , DEFAULT_MAX_TIMESTAMP_AGE ,
1212 DEFAULT_METRICS_ENDPOINT , DEFAULT_METRICS_PORT , DEFAULT_METRICS_SCRAPE_INTERVAL ,
13+ DEFAULT_USAGE_LIMIT_FALLBACK_IF_UNAVAILABLE , DEFAULT_USAGE_LIMIT_MAX_TRANSACTIONS ,
1314 } ,
1415 error:: KoraError ,
1516 fee:: price:: { PriceConfig , PriceModel } ,
@@ -349,6 +350,8 @@ pub struct KoraConfig {
349350 pub payment_address : Option < String > ,
350351 #[ serde( default ) ]
351352 pub cache : CacheConfig ,
353+ #[ serde( default ) ]
354+ pub usage_limit : UsageLimitConfig ,
352355}
353356
354357impl Default for KoraConfig {
@@ -359,6 +362,30 @@ impl Default for KoraConfig {
359362 auth : AuthConfig :: default ( ) ,
360363 payment_address : None ,
361364 cache : CacheConfig :: default ( ) ,
365+ usage_limit : UsageLimitConfig :: default ( ) ,
366+ }
367+ }
368+ }
369+
370+ #[ derive( Debug , Clone , Serialize , Deserialize , ToSchema ) ]
371+ pub struct UsageLimitConfig {
372+ /// Enable per-wallet usage limiting
373+ pub enabled : bool ,
374+ /// Cache URL for shared usage limiting across multiple Kora instances
375+ pub cache_url : Option < String > ,
376+ /// Default maximum transactions per wallet (0 = unlimited)
377+ pub max_transactions : u64 ,
378+ /// Fallback behavior when cache is unavailable
379+ pub fallback_if_unavailable : bool ,
380+ }
381+
382+ impl Default for UsageLimitConfig {
383+ fn default ( ) -> Self {
384+ Self {
385+ enabled : false ,
386+ cache_url : None ,
387+ max_transactions : DEFAULT_USAGE_LIMIT_MAX_TRANSACTIONS ,
388+ fallback_if_unavailable : DEFAULT_USAGE_LIMIT_FALLBACK_IF_UNAVAILABLE ,
362389 }
363390 }
364391}
@@ -671,4 +698,41 @@ mod tests {
671698 assert_eq ! ( config. kora. cache. default_ttl, 300 ) ;
672699 assert_eq ! ( config. kora. cache. account_ttl, 60 ) ;
673700 }
701+
702+ #[ test]
703+ fn test_usage_limit_config_parsing ( ) {
704+ let config = ConfigBuilder :: new ( )
705+ . with_usage_limit_config ( true , Some ( "redis://localhost:6379" ) , 10 , false )
706+ . build_config ( )
707+ . unwrap ( ) ;
708+
709+ assert ! ( config. kora. usage_limit. enabled) ;
710+ assert_eq ! ( config. kora. usage_limit. cache_url, Some ( "redis://localhost:6379" . to_string( ) ) ) ;
711+ assert_eq ! ( config. kora. usage_limit. max_transactions, 10 ) ;
712+ assert ! ( !config. kora. usage_limit. fallback_if_unavailable) ;
713+ }
714+
715+ #[ test]
716+ fn test_usage_limit_config_default ( ) {
717+ let config = ConfigBuilder :: new ( ) . build_config ( ) . unwrap ( ) ;
718+
719+ assert ! ( !config. kora. usage_limit. enabled) ;
720+ assert_eq ! ( config. kora. usage_limit. cache_url, None ) ;
721+ assert_eq ! ( config. kora. usage_limit. max_transactions, DEFAULT_USAGE_LIMIT_MAX_TRANSACTIONS ) ;
722+ assert_eq ! (
723+ config. kora. usage_limit. fallback_if_unavailable,
724+ DEFAULT_USAGE_LIMIT_FALLBACK_IF_UNAVAILABLE
725+ ) ;
726+ }
727+
728+ #[ test]
729+ fn test_usage_limit_config_unlimited ( ) {
730+ let config = ConfigBuilder :: new ( )
731+ . with_usage_limit_config ( true , None , 0 , true )
732+ . build_config ( )
733+ . unwrap ( ) ;
734+
735+ assert ! ( config. kora. usage_limit. enabled) ;
736+ assert_eq ! ( config. kora. usage_limit. max_transactions, 0 ) ; // 0 = unlimited
737+ }
674738}
0 commit comments