spl_governance_chat/
processor.rs

1//! Program processor
2
3use {
4    crate::{
5        error::GovernanceChatError,
6        instruction::GovernanceChatInstruction,
7        state::{
8            assert_is_valid_chat_message, ChatMessage, GovernanceChatAccountType, MessageBody,
9        },
10    },
11    borsh::BorshDeserialize,
12    solana_program::{
13        account_info::{next_account_info, AccountInfo},
14        clock::Clock,
15        entrypoint::ProgramResult,
16        msg,
17        program_error::ProgramError,
18        pubkey::Pubkey,
19        sysvar::Sysvar,
20    },
21    spl_governance::state::{
22        governance::get_governance_data_for_realm, proposal::get_proposal_data_for_governance,
23        realm::get_realm_data, realm_config::get_realm_config_data_for_realm,
24        token_owner_record::get_token_owner_record_data_for_realm,
25    },
26    spl_governance_addin_api::voter_weight::VoterWeightAction,
27    spl_governance_tools::account::create_and_serialize_account,
28};
29
30/// Processes an instruction
31pub fn process_instruction(
32    program_id: &Pubkey,
33    accounts: &[AccountInfo],
34    input: &[u8],
35) -> ProgramResult {
36    msg!("VERSION:{:?}", env!("CARGO_PKG_VERSION"));
37
38    let instruction = GovernanceChatInstruction::try_from_slice(input)
39        .map_err(|_| ProgramError::InvalidInstructionData)?;
40
41    match instruction {
42        GovernanceChatInstruction::PostMessage { body, is_reply } => {
43            msg!("GOVERNANCE-CHAT-INSTRUCTION: PostMessage");
44            process_post_message(program_id, accounts, body, is_reply)
45        }
46    }
47}
48
49/// Processes PostMessage instruction
50pub fn process_post_message(
51    program_id: &Pubkey,
52    accounts: &[AccountInfo],
53    body: MessageBody,
54    is_reply: bool,
55) -> ProgramResult {
56    let account_info_iter = &mut accounts.iter();
57
58    let governance_program_info = next_account_info(account_info_iter)?; // 0
59    let realm_info = next_account_info(account_info_iter)?; // 1
60    let governance_info = next_account_info(account_info_iter)?; // 2
61    let proposal_info = next_account_info(account_info_iter)?; // 3
62    let token_owner_record_info = next_account_info(account_info_iter)?; // 4
63    let governance_authority_info = next_account_info(account_info_iter)?; // 5
64
65    let chat_message_info = next_account_info(account_info_iter)?; // 6
66
67    let payer_info = next_account_info(account_info_iter)?; // 7
68    let system_info = next_account_info(account_info_iter)?; // 8
69
70    let reply_to_address = if is_reply {
71        let reply_to_info = next_account_info(account_info_iter)?; // 9
72        assert_is_valid_chat_message(program_id, reply_to_info)?;
73        Some(*reply_to_info.key)
74    } else {
75        None
76    };
77
78    let governance_program_id = governance_program_info.key;
79    let realm_data = get_realm_data(governance_program_id, realm_info)?;
80
81    let governance_data =
82        get_governance_data_for_realm(governance_program_id, governance_info, realm_info.key)?;
83
84    let token_owner_record_data = get_token_owner_record_data_for_realm(
85        governance_program_id,
86        token_owner_record_info,
87        &governance_data.realm,
88    )?;
89
90    token_owner_record_data.assert_token_owner_or_delegate_is_signer(governance_authority_info)?;
91
92    // deserialize proposal to assert it belongs to the given governance and hence
93    // belongs to the same realm as the token owner
94    let _proposal_data = get_proposal_data_for_governance(
95        governance_program_id,
96        proposal_info,
97        governance_info.key,
98    )?;
99
100    let realm_config_info = next_account_info(account_info_iter)?; // 10
101
102    let realm_config_data =
103        get_realm_config_data_for_realm(governance_program_id, realm_config_info, realm_info.key)?;
104
105    let voter_weight = token_owner_record_data.resolve_voter_weight(
106        account_info_iter, // voter_weight_record *11
107        &realm_data,
108        &realm_config_data,
109        VoterWeightAction::CommentProposal,
110        proposal_info.key,
111    )?;
112
113    // The owner needs to have at least voter weight of 1 to comment on proposals
114    // Note: It can be either community or council token and is irrelevant to the
115    // proposal's governing token Note: 1 is currently hardcoded but if
116    // different level is required then it should be added to realm config
117    if voter_weight < 1 {
118        return Err(GovernanceChatError::NotEnoughTokensToCommentProposal.into());
119    }
120
121    let clock = Clock::get()?;
122
123    let chat_message_data = ChatMessage {
124        account_type: GovernanceChatAccountType::ChatMessage,
125        proposal: *proposal_info.key,
126        author: token_owner_record_data.governing_token_owner,
127        posted_at: clock.unix_timestamp,
128        reply_to: reply_to_address,
129        body,
130    };
131
132    create_and_serialize_account(
133        payer_info,
134        chat_message_info,
135        &chat_message_data,
136        program_id,
137        system_info,
138    )?;
139
140    Ok(())
141}