@@ -18,6 +18,7 @@ const INITIAL_SUPPLY: Symbol = Symbol::new(&"INITIAL_SUPPLY");
1818const ADMIN_BALANCE : Symbol = Symbol :: new ( & "ADMIN_BALANCE" ) ;
1919const ADMIN_ADDRESS : Symbol = Symbol :: new ( & "ADMIN_ADDRESS" ) ;
2020const PROPOSED_ADMIN : Symbol = Symbol :: new ( & "PROPOSED_ADMIN" ) ;
21+ const KEEPER_FEES : Symbol = Symbol :: new ( & "KEEPER_FEES" ) ;
2122
2223// Vault structure with lazy initialization
2324#[ contracttype]
@@ -28,6 +29,7 @@ pub struct Vault {
2829 pub released_amount : i128 ,
2930 pub start_time : u64 ,
3031 pub end_time : u64 ,
32+ pub keeper_fee : i128 , // Fee paid to anyone who triggers auto_claim
3133 pub is_initialized : bool , // Lazy initialization flag
3234 pub is_irrevocable : bool , // Security flag to prevent admin withdrawal
3335}
@@ -38,6 +40,7 @@ pub struct BatchCreateData {
3840 pub amounts : Vec < i128 > ,
3941 pub start_times : Vec < u64 > ,
4042 pub end_times : Vec < u64 > ,
43+ pub keeper_fees : Vec < i128 > ,
4144}
4245
4346#[ contracttype]
@@ -116,7 +119,7 @@ impl VestingContract {
116119 }
117120
118121 // Full initialization - writes all metadata immediately
119- pub fn create_vault_full ( env : Env , owner : Address , amount : i128 , start_time : u64 , end_time : u64 ) -> u64 {
122+ pub fn create_vault_full ( env : Env , owner : Address , amount : i128 , start_time : u64 , end_time : u64 , keeper_fee : i128 ) -> u64 {
120123 Self :: require_admin ( & env) ;
121124
122125 // Get next vault ID
@@ -137,6 +140,7 @@ impl VestingContract {
137140 released_amount : 0 ,
138141 start_time,
139142 end_time,
143+ keeper_fee,
140144 is_initialized : true , // Mark as fully initialized
141145 is_irrevocable : false , // Default to revocable
142146 } ;
@@ -170,7 +174,7 @@ impl VestingContract {
170174 }
171175
172176 // Lazy initialization - writes minimal data initially
173- pub fn create_vault_lazy ( env : Env , owner : Address , amount : i128 , start_time : u64 , end_time : u64 ) -> u64 {
177+ pub fn create_vault_lazy ( env : Env , owner : Address , amount : i128 , start_time : u64 , end_time : u64 , keeper_fee : i128 ) -> u64 {
174178 Self :: require_admin ( & env) ;
175179
176180 // Get next vault ID
@@ -191,6 +195,7 @@ impl VestingContract {
191195 released_amount : 0 ,
192196 start_time,
193197 end_time,
198+ keeper_fee,
194199 is_initialized : false , // Mark as lazy initialized
195200 is_irrevocable : false , // Default to revocable
196201 } ;
@@ -232,6 +237,7 @@ impl VestingContract {
232237 released_amount : 0 ,
233238 start_time : 0 ,
234239 end_time : 0 ,
240+ keeper_fee : 0 ,
235241 is_initialized : false ,
236242 is_irrevocable : false ,
237243 }
@@ -395,6 +401,7 @@ impl VestingContract {
395401 released_amount : 0 ,
396402 start_time : batch_data. start_times . get ( i) . unwrap ( ) ,
397403 end_time : batch_data. end_times . get ( i) . unwrap ( ) ,
404+ keeper_fee : batch_data. keeper_fees . get ( i) . unwrap ( ) ,
398405 is_initialized : false , // Lazy initialization
399406 is_irrevocable : false , // Default to revocable
400407 } ;
@@ -448,6 +455,7 @@ impl VestingContract {
448455 released_amount : 0 ,
449456 start_time : batch_data. start_times . get ( i) . unwrap ( ) ,
450457 end_time : batch_data. end_times . get ( i) . unwrap ( ) ,
458+ keeper_fee : batch_data. keeper_fees . get ( i) . unwrap ( ) ,
451459 is_initialized : true , // Full initialization
452460 is_irrevocable : false , // Default to revocable
453461 } ;
@@ -496,6 +504,7 @@ impl VestingContract {
496504 released_amount : 0 ,
497505 start_time : 0 ,
498506 end_time : 0 ,
507+ keeper_fee : 0 ,
499508 is_initialized : false ,
500509 is_irrevocable : false ,
501510 }
@@ -529,6 +538,7 @@ impl VestingContract {
529538 released_amount : 0 ,
530539 start_time : 0 ,
531540 end_time : 0 ,
541+ keeper_fee : 0 ,
532542 is_initialized : false ,
533543 is_irrevocable : false ,
534544 }
@@ -683,4 +693,79 @@ impl VestingContract {
683693 let sum = total_locked + total_claimed + admin_balance;
684694 sum == initial_supply
685695 }
696+
697+ // --- New Auto-Claim Logic ---
698+
699+ // Calculate currently claimable tokens based on linear vesting
700+ pub fn get_claimable_amount ( env : Env , vault_id : u64 ) -> i128 {
701+ let vault: Vault = env. storage ( ) . instance ( )
702+ . get ( & VAULT_DATA , & vault_id)
703+ . unwrap_or_else ( || panic ! ( "Vault not found" ) ) ;
704+
705+ let now = env. ledger ( ) . timestamp ( ) ;
706+
707+ if now <= vault. start_time {
708+ return 0 ;
709+ }
710+
711+ let elapsed = if now >= vault. end_time {
712+ vault. end_time - vault. start_time
713+ } else {
714+ now - vault. start_time
715+ } ;
716+
717+ let total_duration = vault. end_time - vault. start_time ;
718+
719+ let vested = if total_duration > 0 {
720+ // Use i128 for calculation to prevent overflow then back to i128
721+ ( vault. total_amount * elapsed as i128 ) / total_duration as i128
722+ } else {
723+ vault. total_amount
724+ } ;
725+
726+ if vested > vault. released_amount {
727+ vested - vault. released_amount
728+ } else {
729+ 0
730+ }
731+ }
732+
733+ // Auto-claim function that anyone can call.
734+ // Tokens go to beneficiary, but keeper can get a tip.
735+ pub fn auto_claim ( env : Env , vault_id : u64 , keeper : Address ) {
736+ let mut vault: Vault = env. storage ( ) . instance ( )
737+ . get ( & VAULT_DATA , & vault_id)
738+ . unwrap_or_else ( || panic ! ( "Vault not found" ) ) ;
739+
740+ require ! ( vault. is_initialized, "Vault not initialized" ) ;
741+
742+ let claimable = Self :: get_claimable_amount ( env. clone ( ) , vault_id) ;
743+
744+ // Ensure there's enough to cover the fee and something left for beneficiary
745+ require ! ( claimable > vault. keeper_fee, "Insufficient claimable tokens to cover fee" ) ;
746+
747+ let beneficiary_amount = claimable - vault. keeper_fee ;
748+
749+ // Update vault
750+ vault. released_amount += claimable;
751+ env. storage ( ) . instance ( ) . set ( & VAULT_DATA , & vault_id, & vault) ;
752+
753+ // Update keeper fees
754+ let mut fees: Map < Address , i128 > = env. storage ( ) . instance ( ) . get ( & KEEPER_FEES ) . unwrap_or ( Map :: new ( & env) ) ;
755+ let current_fees = fees. get ( keeper. clone ( ) ) . unwrap_or ( 0 ) ;
756+ fees. set ( keeper. clone ( ) , current_fees + vault. keeper_fee ) ;
757+ env. storage ( ) . instance ( ) . set ( & KEEPER_FEES , & fees) ;
758+
759+ // Emit KeeperClaim event
760+ env. events ( ) . publish (
761+ ( Symbol :: new ( & env, "KeeperClaim" ) , vault_id, keeper) ,
762+ ( beneficiary_amount, vault. keeper_fee )
763+ ) ;
764+ }
765+
766+ // Get accumulated fees for a keeper
767+ pub fn get_keeper_fee ( env : Env , keeper : Address ) -> i128 {
768+ let fees: Map < Address , i128 > = env. storage ( ) . instance ( ) . get ( & KEEPER_FEES ) . unwrap_or ( Map :: new ( & env) ) ;
769+ fees. get ( keeper) . unwrap_or ( 0 )
770+ }
686771}
0 commit comments