11use crate :: bcat;
22use crate :: consensus:: bic:: coin:: balance;
33use crate :: consensus:: consensus_apply:: ApplyEnv ;
4- use crate :: consensus:: consensus_kv:: { kv_delete, kv_get, kv_get_next, kv_increment, kv_put} ;
4+ use crate :: consensus:: consensus_kv:: { kv_delete, kv_exists , kv_get, kv_get_next, kv_increment, kv_put} ;
55use std:: collections:: { BTreeMap , HashSet } ;
66use std:: panic:: panic_any;
77use vecpak:: { decode, encode, Term } ;
@@ -269,7 +269,7 @@ pub fn pay_epoch_yield(
269269 let due = base. checked_mul ( vault. rate_bps as i128 ) . unwrap_or_else ( || panic_any ( "yield_overflow" ) ) / APY_EPOCH_DENOM ;
270270 let due = due. checked_mul ( reduction_pct. min ( 100 ) as i128 ) . unwrap_or_else ( || panic_any ( "yield_overflow" ) ) / 100 ;
271271 if due > 0 {
272- due_total += due;
272+ due_total = due_total . checked_add ( due) . unwrap_or_else ( || panic_any ( "yield_overflow" ) ) ;
273273 entries. push ( ( bcat ( & [ VAULT_KEY_PREFIX , & suffix] ) , vault, due) ) ;
274274 }
275275 }
@@ -294,7 +294,7 @@ pub fn pay_epoch_yield(
294294 let addr = vault. payout_address . as_ref ( ) . unwrap_or_else ( || panic_any ( "invalid_vault_data" ) ) . clone ( ) ;
295295 kv_increment ( env, & bcat ( & [ b"account:" , & addr, b":balance:AMA" ] ) , pay) ;
296296 }
297- paid_total += pay;
297+ paid_total = paid_total . checked_add ( pay) . unwrap_or_else ( || panic_any ( "yield_overflow" ) ) ;
298298 }
299299 paid_total
300300}
@@ -342,6 +342,13 @@ fn validate_pk(pk: &[u8], error: &'static str) {
342342 }
343343}
344344
345+ fn init_balance_if_missing ( env : & mut ApplyEnv , address : & [ u8 ] ) {
346+ let key = bcat ( & [ b"account:" , address, b":balance:AMA" ] ) ;
347+ if !kv_exists ( env, & key) {
348+ kv_increment ( env, & key, 0 ) ;
349+ }
350+ }
351+
345352//strict reader for a single vecpak map argument (tag 7). the codec already
346353//guarantees the map is canonical and duplicate-free (decode rejects anything
347354//else), so this layer only adds the policy the codec can't know: an allow-list
@@ -456,22 +463,31 @@ pub fn call_create(env: &mut ApplyEnv, args: Vec<Vec<u8>>) {
456463 None => tier_duration,
457464 } ;
458465
466+ let caller = env. caller_env . account_caller . clone ( ) ;
467+
468+ if amount < MIN_VAULT_AMOUNT {
469+ panic_any ( "vault_amount_below_minimum" )
470+ }
471+ if amount > balance ( env, & caller, b"AMA" ) {
472+ panic_any ( "insufficient_funds" )
473+ }
474+
459475 let validator = match map. opt_bin ( b"validator" , "invalid_validator_pk" ) {
460476 Some ( pk) => {
461477 validate_pk ( & pk, "invalid_validator_pk" ) ;
478+ init_balance_if_missing ( env, & pk) ;
462479 Some ( pk)
463480 }
464481 None => None ,
465482 } ;
466483 let payout_address = match map. opt_bin ( b"payout_address" , "invalid_payout_pk" ) {
467484 Some ( addr) => {
468485 validate_pk ( & addr, "invalid_payout_pk" ) ;
486+ init_balance_if_missing ( env, & addr) ;
469487 Some ( addr)
470488 }
471489 None => None ,
472490 } ;
473- //the caller always pays; the vault is keyed under the owner (caller by default)
474- let caller = env. caller_env . account_caller . clone ( ) ;
475491 let owner = match map. opt_bin ( b"owner" , "invalid_owner_pk" ) {
476492 Some ( pk) => {
477493 validate_pk ( & pk, "invalid_owner_pk" ) ;
@@ -480,13 +496,6 @@ pub fn call_create(env: &mut ApplyEnv, args: Vec<Vec<u8>>) {
480496 None => caller. clone ( ) ,
481497 } ;
482498
483- if amount < MIN_VAULT_AMOUNT {
484- panic_any ( "vault_amount_below_minimum" )
485- }
486- if amount > balance ( env, & caller, b"AMA" ) {
487- panic_any ( "insufficient_funds" )
488- }
489-
490499 let entry_epoch = env. caller_env . entry_epoch ;
491500 //the tier schedule is the floor; unlock_epoch can only extend maturity later
492501 let tier_mature = entry_epoch. saturating_add ( duration_epochs) ;
@@ -583,6 +592,7 @@ pub fn call_set_payout_address(env: &mut ApplyEnv, args: Vec<Vec<u8>>) {
583592 panic_any ( "vault_is_unlocking" )
584593 }
585594 validate_pk ( & args[ 1 ] , "invalid_payout_pk" ) ;
595+ init_balance_if_missing ( env, & args[ 1 ] ) ;
586596 vault. payout_address = Some ( args[ 1 ] . to_vec ( ) ) ;
587597 store_vault ( env, & key, & vault) ;
588598}
@@ -609,6 +619,7 @@ pub fn call_set_validator(env: &mut ApplyEnv, args: Vec<Vec<u8>>) {
609619 }
610620 let validator = args[ 1 ] . as_slice ( ) ;
611621 validate_pk ( validator, "invalid_validator_pk" ) ;
622+ init_balance_if_missing ( env, validator) ;
612623 //the validator the vault is already heading toward: the queued one if a change
613624 //is in flight, otherwise the active one. re-selecting it is a no-op so the
614625 //2-epoch clock is not reset
0 commit comments