gpl_core/instructions/
reaction.rs

1use crate::errors::GumError;
2use crate::state::{Post, Profile, Reaction, ReactionType, User};
3
4use anchor_lang::prelude::*;
5use std::convert::AsRef;
6use std::str::FromStr;
7
8use crate::constants::*;
9use crate::events::{ReactionDeleted, ReactionNew};
10use gpl_session::{session_auth_or, Session, SessionError, SessionToken};
11
12// Create a reaction to a post from a profile
13#[derive(Accounts, Session)]
14#[instruction(reaction_type: String)]
15pub struct CreateReaction<'info> {
16    // The account that will be initialized as a Reaction
17    #[account(
18        init,
19        seeds = [
20            REACTION_PREFIX_SEED.as_bytes(),
21            reaction_type.as_bytes(),
22            to_post.to_account_info().key.as_ref(),
23            from_profile.to_account_info().key.as_ref(),
24        ],
25        bump,
26        payer = authority,
27        space = Reaction::LEN
28    )]
29    pub reaction: Account<'info, Reaction>,
30    #[account(
31        seeds = [
32            POST_PREFIX_SEED.as_bytes(),
33            to_post.random_hash.as_ref(),
34        ],
35        bump,
36    )]
37    pub to_post: Account<'info, Post>,
38    #[account(
39        seeds = [
40            PROFILE_PREFIX_SEED.as_bytes(),
41            from_profile.namespace.as_ref().as_bytes(),
42            user.to_account_info().key.as_ref(),
43        ],
44        bump,
45        has_one = user,
46    )]
47    pub from_profile: Account<'info, Profile>,
48    #[account(
49        seeds = [
50            USER_PREFIX_SEED.as_bytes(),
51            user.random_hash.as_ref(),
52        ],
53        bump,
54    )]
55    pub user: Account<'info, User>,
56
57    #[session(
58        signer = authority,
59        authority = user.authority.key()
60    )]
61    pub session_token: Option<Account<'info, SessionToken>>,
62
63    #[account(mut)]
64    pub authority: Signer<'info>,
65
66    // The system program
67    pub system_program: Program<'info, System>,
68}
69
70// Handler to create a new Reaction account
71#[session_auth_or(
72    ctx.accounts.user.authority.key() == ctx.accounts.authority.key(),
73    GumError::UnauthorizedSigner
74)]
75pub fn create_reaction_handler(ctx: Context<CreateReaction>, reaction_type: String) -> Result<()> {
76    let reaction = &mut ctx.accounts.reaction;
77    reaction.reaction_type = ReactionType::from_str(&reaction_type).unwrap();
78    reaction.to_post = *ctx.accounts.to_post.to_account_info().key;
79    reaction.from_profile = *ctx.accounts.from_profile.to_account_info().key;
80
81    // emit a new reaction event
82    emit!(ReactionNew {
83        reaction: *reaction.to_account_info().key,
84        reaction_type: reaction.reaction_type,
85        user: *ctx.accounts.user.to_account_info().key,
86        to_post: *ctx.accounts.to_post.to_account_info().key,
87        from_profile: *ctx.accounts.from_profile.to_account_info().key,
88        timestamp: Clock::get()?.unix_timestamp,
89    });
90
91    Ok(())
92}
93
94// Delete a reaction account
95#[derive(Accounts, Session)]
96pub struct DeleteReaction<'info> {
97    #[account(
98        mut,
99        seeds = [
100            REACTION_PREFIX_SEED.as_bytes(),
101            reaction.reaction_type.as_ref().as_bytes(),
102            reaction.to_post.as_ref(),
103            reaction.from_profile.as_ref(),
104        ],
105        bump,
106        has_one = to_post,
107        has_one = from_profile,
108        close = refund_receiver,
109    )]
110    pub reaction: Account<'info, Reaction>,
111    #[account(
112        seeds = [
113            POST_PREFIX_SEED.as_bytes(),
114            to_post.random_hash.as_ref(),
115        ],
116        bump,
117    )]
118    pub to_post: Account<'info, Post>,
119    #[account(
120        seeds = [
121            PROFILE_PREFIX_SEED.as_bytes(),
122            from_profile.namespace.as_ref().as_bytes(),
123            user.to_account_info().key.as_ref(),
124        ],
125        bump,
126        has_one = user,
127    )]
128    pub from_profile: Account<'info, Profile>,
129    #[account(
130        seeds = [
131            USER_PREFIX_SEED.as_bytes(),
132            user.random_hash.as_ref(),
133        ],
134        bump,
135    )]
136    pub user: Account<'info, User>,
137
138    #[session(
139        signer = authority,
140        authority = user.authority.key()
141    )]
142    pub session_token: Option<Account<'info, SessionToken>>,
143    #[account(mut)]
144    pub authority: Signer<'info>,
145
146    #[account(mut, constraint = refund_receiver.key() == user.authority)]
147    pub refund_receiver: SystemAccount<'info>,
148
149    // The system program
150    pub system_program: Program<'info, System>,
151}
152
153// Handler to delete a Reaction account
154#[session_auth_or(
155    ctx.accounts.user.authority.key() == ctx.accounts.authority.key(),
156    GumError::UnauthorizedSigner
157)]
158pub fn delete_reaction_handler(ctx: Context<DeleteReaction>) -> Result<()> {
159    // emit a reaction deleted event
160    emit!(ReactionDeleted {
161        reaction: *ctx.accounts.reaction.to_account_info().key,
162        reaction_type: ctx.accounts.reaction.reaction_type,
163        user: *ctx.accounts.user.to_account_info().key,
164        to_post: *ctx.accounts.to_post.to_account_info().key,
165        from_profile: *ctx.accounts.from_profile.to_account_info().key,
166        timestamp: Clock::get()?.unix_timestamp,
167    });
168    Ok(())
169}