1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
use crate::state::{AssociatedProgram, SerializableActions};

pub mod delegate_instructions;
pub mod migrate;
pub mod profile_instructions;
pub mod project_instructions;
pub mod public_info_instructions;
pub mod user_instructions;

pub use {
    delegate_instructions::*, migrate::*, profile_instructions::*, project_instructions::*,
    public_info_instructions::*, user_instructions::*,
};

use {
    crate::{
        assertions::assert_authority,
        errors::ErrorCode,
        state::{ActionType, DelegateAuthority, Project, Service},
    },
    anchor_lang::{prelude::*, solana_program},
};
pub fn collect_payment<'info>(
    fee: u64,
    payer: AccountInfo<'info>,
    vault: AccountInfo<'info>,
    system_program: AccountInfo<'info>,
) -> Result<()> {
    if !crate::constants::is_sandbox() {
        if String::from("EE67X262V2CXa68oa65fn2KLzkn8hEEvwGc9aG6aCh2C") != vault.key().to_string() {
            panic!("Invalid Vault key")
        }

        solana_program::program::invoke(
            &solana_program::system_instruction::transfer(payer.key, vault.key, fee),
            &[payer, vault, system_program],
        )?;
    }
    Ok(())
}
pub fn platform_gate_fn<'info>(
    serializable_action: SerializableActions,
    service: Option<(u8, Pubkey)>,
    project: &Account<'info, Project>,
    signer_key: Pubkey,
    payer: AccountInfo<'info>,
    vault: AccountInfo<'info>,
    delegate_authority: &Option<Account<'info, DelegateAuthority>>,
    system_program: AccountInfo<'info>,
    instructions_sysvar: AccountInfo<'info>,
    program_id: Pubkey,
) -> Result<()> {
    let action = serializable_action.to_action();
    let fee = action.fee;

    // Get the innstructionn program id
    let ix_program_key =
        anchor_lang::solana_program::sysvar::instructions::get_instruction_relative(
            0,
            &instructions_sysvar,
        )
        .unwrap()
        .program_id;

    let is_cpi = program_id.eq(&ix_program_key);
    if is_cpi {
        // Get Allowed program list
        let associated_programs: Vec<AssociatedProgram> = vec![
            crate::constants::known_programs(),
            project.associated_programs.clone(),
        ]
        .concat();
        let program_ctx = associated_programs
            .iter()
            .find(|p| (p.address).eq(&ix_program_key));

        if let Some(program_ctx) = program_ctx {
            if let ActionType::Restricted { delegations: _ } = action.action_type {
                for action_ctx in program_ctx.trusted_actions.iter() {
                    if action_ctx.is_equal(&action) {
                        return collect_payment(fee, payer, vault, system_program);
                    }
                }
            }
        } else {
            return Err(ErrorCode::UnknownCPI.into());
        }
    }

    if service.is_some() && service.unwrap().1.eq(&Pubkey::default()) {
        // If non-service action then only driver is allowed
        if project.driver.ne(&signer_key) {
            return Err(ErrorCode::Unauthorized.into());
        }
    } else {
        // CHECKs the service to which action is being performed

        let mut index: Option<u8> = None;
        if let Some(service) = service {
            let service_id = match project.services[service.0 as usize] {
                Service::Assembler { assembler_id } => assembler_id,
                Service::AssetManager { asset_manager_id } => asset_manager_id,
                Service::Missions { pool_id } => pool_id,
                Service::Raffles { pool_id } => pool_id,
                Service::Staking { pool_id } => pool_id,
                Service::GuildKit { kit_id } => kit_id,
                _ => Pubkey::default(),
            };

            if service_id == service.1 {
                index = Some(service.0 as u8);
            }
        }

        assert_authority(
            action,
            index,
            signer_key,
            if delegate_authority.is_none() {
                Some(project.authority)
            } else {
                None
            },
            delegate_authority,
        )?;
    }
    collect_payment(fee, payer, vault, system_program)
}

#[derive(Accounts)]
pub struct PlatformGate<'info> {
    /// The project account.
    #[account()]
    pub project: Account<'info, Project>,

    /// The delegate authority PDA containing info about permissions
    #[account()]
    pub delegate_authority: Option<Account<'info, DelegateAuthority>>,

    /// The wallet that pays for the rent.
    #[account(mut)]
    pub payer: Signer<'info>,

    /// The signer assuming the authority for the action.
    pub signer: Signer<'info>,

    /// The vault that collects the fees.
    /// CHECK: This is not dangerous
    #[account(mut)]
    pub vault: AccountInfo<'info>,

    /// The system program.
    pub system_program: Program<'info, System>,

    /// NATIVE INSTRUCTIONS SYSVAR
    /// CHECK: This is not dangerous because we don't read or write from this account
    #[account(address = anchor_lang::solana_program::sysvar::instructions::ID)]
    pub instructions_sysvar: AccountInfo<'info>,
}

#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub struct PlatformGateArgs {
    pub action: SerializableActions,
    pub service: Option<(u8, Pubkey)>,
    pub program_id: Pubkey,
}

pub fn platform_gate(ctx: Context<PlatformGate>, args: PlatformGateArgs) -> Result<()> {
    platform_gate_fn(
        args.action,
        args.service,
        &ctx.accounts.project,
        ctx.accounts.signer.key(),
        ctx.accounts.payer.to_account_info(),
        ctx.accounts.vault.to_account_info(),
        &ctx.accounts.delegate_authority,
        ctx.accounts.system_program.to_account_info(),
        ctx.accounts.instructions_sysvar.to_account_info(),
        args.program_id,
    )
}