@@ -14,9 +14,8 @@ pub use crate::types::{
1414
1515use crate :: events:: {
1616 GovernanceInitializedEvent , GuardianAddedEvent , GuardianRemovedEvent , ProposalApprovedEvent ,
17- ProposalCancelledEvent , ProposalCreatedEvent , ProposalExecutedEvent , ProposalFailedEvent ,
18- ProposalQueuedEvent , RecoveryApprovedEvent , RecoveryExecutedEvent , RecoveryStartedEvent ,
19- VoteCastEvent ,
17+ ProposalCancelledEvent , ProposalQueuedEvent , RecoveryApprovedEvent , RecoveryExecutedEvent , RecoveryStartedEvent ,
18+ VoteCastEvent , emit_proposal_approved,
2019} ;
2120
2221// ========================================================================
@@ -425,14 +424,172 @@ pub fn execute_proposal(
425424 Ok ( ( ) )
426425}
427426
428- fn execute_proposal_type ( _env : & Env , proposal_type : & ProposalType ) -> Result < ( ) , GovernanceError > {
427+ fn execute_proposal_type ( env : & Env , proposal_type : & ProposalType ) -> Result < ( ) , GovernanceError > {
429428 match proposal_type {
430- ProposalType :: MinCollateralRatio ( _)
431- | ProposalType :: RiskParams ( _, _, _, _)
432- | ProposalType :: PauseSwitch ( _, _)
433- | ProposalType :: EmergencyPause ( _)
434- | ProposalType :: GenericAction ( _) => Ok ( ( ) ) ,
429+ ProposalType :: MinCollateralRatio ( ratio) => {
430+ risk_params:: set_risk_params ( env, Some ( * ratio) , None , None , None )
431+ . map_err ( |_| GovernanceError :: ExecutionFailed ) ?;
432+ }
433+ ProposalType :: RiskParams ( mcr, lt, cf, li) => {
434+ risk_params:: set_risk_params ( env, * mcr, * lt, * cf, * li)
435+ . map_err ( |_| GovernanceError :: ExecutionFailed ) ?;
436+ }
437+ ProposalType :: InterestRateConfig ( params) => {
438+ let admin = get_admin ( env) . ok_or ( GovernanceError :: NotInitialized ) ?;
439+ interest_rate:: update_interest_rate_config (
440+ env,
441+ admin,
442+ params. base_rate_bps ,
443+ params. kink_utilization_bps ,
444+ params. multiplier_bps ,
445+ params. jump_multiplier_bps ,
446+ params. rate_floor_bps ,
447+ params. rate_ceiling_bps ,
448+ params. spread_bps ,
449+ )
450+ . map_err ( |_| GovernanceError :: ExecutionFailed ) ?;
451+ }
452+ ProposalType :: PauseSwitch ( op, paused) => {
453+ let admin = get_admin ( env) . ok_or ( GovernanceError :: NotInitialized ) ?;
454+ risk_management:: set_pause_switch ( env, admin, op. clone ( ) , * paused)
455+ . map_err ( |_| GovernanceError :: ExecutionFailed ) ?;
456+ }
457+ ProposalType :: EmergencyPause ( paused) => {
458+ let admin = get_admin ( env) . ok_or ( GovernanceError :: NotInitialized ) ?;
459+ risk_management:: set_emergency_pause ( env, admin, * paused)
460+ . map_err ( |_| GovernanceError :: ExecutionFailed ) ?;
461+ }
462+ ProposalType :: GenericAction ( _) => {
463+ return Err ( GovernanceError :: InvalidProposalType ) ;
464+ }
435465 }
466+ Ok ( ( ) )
467+ }
468+
469+ pub fn create_admin_proposal (
470+ env : & Env ,
471+ admin : Address ,
472+ proposal_type : ProposalType ,
473+ description : String ,
474+ ) -> Result < u64 , GovernanceError > {
475+ admin. require_auth ( ) ;
476+
477+ let stored_admin: Address = env
478+ . storage ( )
479+ . instance ( )
480+ . get ( & GovernanceDataKey :: Admin )
481+ . ok_or ( GovernanceError :: NotInitialized ) ?;
482+
483+ if admin != stored_admin {
484+ return Err ( GovernanceError :: Unauthorized ) ;
485+ }
486+
487+ let config: GovernanceConfig = env
488+ . storage ( )
489+ . instance ( )
490+ . get ( & GovernanceDataKey :: Config )
491+ . ok_or ( GovernanceError :: NotInitialized ) ?;
492+
493+ let now = env. ledger ( ) . timestamp ( ) ;
494+ let proposal_id: u64 = env
495+ . storage ( )
496+ . instance ( )
497+ . get ( & GovernanceDataKey :: NextProposalId )
498+ . unwrap_or ( 0 ) ;
499+
500+ let execution_time = now + config. execution_delay . max ( MIN_TIMELOCK_DELAY ) ;
501+
502+ let proposal = Proposal {
503+ id : proposal_id,
504+ proposer : admin. clone ( ) ,
505+ proposal_type,
506+ description,
507+ status : ProposalStatus :: Queued ,
508+ start_time : now,
509+ end_time : now,
510+ execution_time : Some ( execution_time) ,
511+ voting_threshold : 0 ,
512+ for_votes : 0 ,
513+ against_votes : 0 ,
514+ abstain_votes : 0 ,
515+ total_voting_power : 0 ,
516+ created_at : now,
517+ } ;
518+
519+ env. storage ( )
520+ . persistent ( )
521+ . set ( & GovernanceDataKey :: Proposal ( proposal_id) , & proposal) ;
522+
523+ env. storage ( )
524+ . instance ( )
525+ . set ( & GovernanceDataKey :: NextProposalId , & ( proposal_id + 1 ) ) ;
526+
527+ emit_proposal_created_event ( env, & proposal_id, & admin) ;
528+
529+ let topics = ( Symbol :: new ( env, "proposal_queued" ) , proposal_id) ;
530+ env. events ( ) . publish ( topics, execution_time) ;
531+
532+ Ok ( proposal_id)
533+ }
534+
535+ pub fn create_emergency_proposal (
536+ env : & Env ,
537+ caller : Address ,
538+ proposal_type : ProposalType ,
539+ description : String ,
540+ ) -> Result < u64 , GovernanceError > {
541+ caller. require_auth ( ) ;
542+
543+ // Verification of multisig auth happens via approvals in multisig module,
544+ // but for "emergency bypass" we can allow direct execution if called by a valid multisig admin
545+ // assuming it's correctly authorized by the multisig threshold.
546+ // In this simplified version, we'll check against multisig admins.
547+
548+ let multisig_config: MultisigConfig = env
549+ . storage ( )
550+ . instance ( )
551+ . get ( & GovernanceDataKey :: MultisigConfig )
552+ . ok_or ( GovernanceError :: NotInitialized ) ?;
553+
554+ if !multisig_config. admins . contains ( & caller) {
555+ return Err ( GovernanceError :: Unauthorized ) ;
556+ }
557+
558+ let now = env. ledger ( ) . timestamp ( ) ;
559+ let proposal_id: u64 = env
560+ . storage ( )
561+ . instance ( )
562+ . get ( & GovernanceDataKey :: NextProposalId )
563+ . unwrap_or ( 0 ) ;
564+
565+ let proposal = Proposal {
566+ id : proposal_id,
567+ proposer : caller. clone ( ) ,
568+ proposal_type,
569+ description,
570+ status : ProposalStatus :: Queued ,
571+ start_time : now,
572+ end_time : now,
573+ execution_time : Some ( now) , // No delay for emergency
574+ voting_threshold : 0 ,
575+ for_votes : 0 ,
576+ against_votes : 0 ,
577+ abstain_votes : 0 ,
578+ total_voting_power : 0 ,
579+ created_at : now,
580+ } ;
581+
582+ env. storage ( )
583+ . persistent ( )
584+ . set ( & GovernanceDataKey :: Proposal ( proposal_id) , & proposal) ;
585+
586+ env. storage ( )
587+ . instance ( )
588+ . set ( & GovernanceDataKey :: NextProposalId , & ( proposal_id + 1 ) ) ;
589+
590+ emit_proposal_created_event ( env, & proposal_id, & caller) ;
591+
592+ Ok ( proposal_id)
436593}
437594
438595// ========================================================================
@@ -570,6 +727,91 @@ pub fn get_proposal_approvals(env: &Env, proposal_id: u64) -> Option<Vec<Address
570727 env. storage ( ) . persistent ( ) . get ( & approvals_key)
571728}
572729
730+ pub fn get_multisig_config ( env : & Env ) -> Option < MultisigConfig > {
731+ env. storage ( )
732+ . instance ( )
733+ . get ( & GovernanceDataKey :: MultisigConfig )
734+ }
735+
736+ pub fn get_multisig_admins ( env : & Env ) -> Option < Vec < Address > > {
737+ get_multisig_config ( env) . map ( |c| c. admins )
738+ }
739+
740+ pub fn get_multisig_threshold ( env : & Env ) -> u32 {
741+ get_multisig_config ( env) . map ( |c| c. threshold ) . unwrap_or ( 1 )
742+ }
743+
744+ pub fn set_multisig_admins (
745+ env : & Env ,
746+ caller : Address ,
747+ admins : Vec < Address > ,
748+ ) -> Result < ( ) , GovernanceError > {
749+ let config = get_multisig_config ( env) . ok_or ( GovernanceError :: NotInitialized ) ?;
750+ set_multisig_config ( env, caller, admins, config. threshold )
751+ }
752+
753+ pub fn set_multisig_threshold (
754+ env : & Env ,
755+ caller : Address ,
756+ threshold : u32 ,
757+ ) -> Result < ( ) , GovernanceError > {
758+ let config = get_multisig_config ( env) . ok_or ( GovernanceError :: NotInitialized ) ?;
759+ set_multisig_config ( env, caller, config. admins , threshold)
760+ }
761+
762+ pub fn propose_set_min_collateral_ratio (
763+ env : & Env ,
764+ proposer : Address ,
765+ new_ratio : i128 ,
766+ ) -> Result < u64 , GovernanceError > {
767+ create_proposal (
768+ env,
769+ proposer,
770+ ProposalType :: MinCollateralRatio ( new_ratio) ,
771+ String :: from_str ( env, "Update min collateral ratio" ) ,
772+ None ,
773+ )
774+ }
775+
776+ pub fn execute_multisig_proposal (
777+ env : & Env ,
778+ executor : Address ,
779+ proposal_id : u64 ,
780+ ) -> Result < ( ) , GovernanceError > {
781+ executor. require_auth ( ) ;
782+
783+ let multisig_config = get_multisig_config ( env) . ok_or ( GovernanceError :: NotInitialized ) ?;
784+ if !multisig_config. admins . contains ( & executor) {
785+ return Err ( GovernanceError :: Unauthorized ) ;
786+ }
787+
788+ let mut proposal: Proposal = env
789+ . storage ( )
790+ . persistent ( )
791+ . get ( & GovernanceDataKey :: Proposal ( proposal_id) )
792+ . ok_or ( GovernanceError :: ProposalNotFound ) ?;
793+
794+ if proposal. status != ProposalStatus :: Pending {
795+ return Err ( GovernanceError :: InvalidProposalStatus ) ;
796+ }
797+
798+ let approvals = get_proposal_approvals ( env, proposal_id) . unwrap_or_else ( || Vec :: new ( env) ) ;
799+ if approvals. len ( ) < multisig_config. threshold {
800+ return Err ( GovernanceError :: InsufficientApprovals ) ;
801+ }
802+
803+ execute_proposal_type ( env, & proposal. proposal_type ) ?;
804+
805+ proposal. status = ProposalStatus :: Executed ;
806+ env. storage ( )
807+ . persistent ( )
808+ . set ( & GovernanceDataKey :: Proposal ( proposal_id) , & proposal) ;
809+
810+ emit_proposal_executed_event ( env, & proposal_id, & executor) ;
811+
812+ Ok ( ( ) )
813+ }
814+
573815// ============================================================================
574816// Events
575817// ============================================================================
@@ -608,14 +850,7 @@ fn emit_proposal_failed_event(env: &Env, proposal_id: &u64) {
608850 env. events ( ) . publish ( topics, ( ) ) ;
609851}
610852
611- pub fn emit_approval_event ( env : & Env , proposal_id : & u64 , approver : & Address ) {
612- let topics = (
613- Symbol :: new ( env, "proposal_approved" ) ,
614- * proposal_id,
615- approver. clone ( ) ,
616- ) ;
617- env. events ( ) . publish ( topics, ( ) ) ;
618- }
853+
619854
620855pub fn add_guardian ( env : & Env , caller : Address , guardian : Address ) -> Result < ( ) , GovernanceError > {
621856 caller. require_auth ( ) ;
@@ -935,12 +1170,6 @@ pub fn get_admin(env: &Env) -> Option<Address> {
9351170 env. storage ( ) . instance ( ) . get ( & GovernanceDataKey :: Admin )
9361171}
9371172
938- pub fn get_multisig_config ( env : & Env ) -> Option < MultisigConfig > {
939- env. storage ( )
940- . instance ( )
941- . get ( & GovernanceDataKey :: MultisigConfig )
942- }
943-
9441173pub fn emit_guardian_added_event ( env : & Env , guardian : & Address ) {
9451174 let topics = ( Symbol :: new ( env, "guardian_added" ) , guardian. clone ( ) ) ;
9461175 env. events ( ) . publish ( topics, ( ) ) ;
0 commit comments