1use crate::errors::{GumError, PostError};
2use crate::events::{PostCommentNew, PostDeleted, PostNew, PostUpdated};
3use crate::state::{Post, Profile, User, MAX_LEN_URI};
4use gpl_session::{session_auth_or, Session};
5
6use anchor_lang::prelude::*;
7use std::convert::AsRef;
8
9use crate::constants::*;
10
11use gpl_session::{SessionError, SessionToken};
12
13#[derive(Accounts, Session)]
15#[instruction(metadata_uri: String, random_hash: [u8;32])]
16pub struct CreatePost<'info> {
17 #[account(
19 init,
20 seeds = [
21 POST_PREFIX_SEED.as_bytes(),
22 random_hash.as_ref(),
23 ],
24 bump,
25 payer = authority,
26 space = Post::LEN
27 )]
28 pub post: Account<'info, Post>,
29 #[account(
30 seeds = [
31 PROFILE_PREFIX_SEED.as_bytes(),
32 profile.namespace.as_ref().as_bytes(),
33 user.to_account_info().key.as_ref(),
34 ],
35 bump,
36 has_one = user,
37 )]
38 pub profile: Account<'info, Profile>,
39 #[account(
40 seeds = [
41 USER_PREFIX_SEED.as_bytes(),
42 user.random_hash.as_ref(),
43 ],
44 bump,
45 )]
46 pub user: Account<'info, User>,
47
48 #[session(
49 signer = authority,
50 authority = user.authority.key()
51 )]
52 pub session_token: Option<Account<'info, SessionToken>>,
53
54 #[account(mut)]
55 pub authority: Signer<'info>,
56 pub system_program: Program<'info, System>,
58}
59
60#[session_auth_or(
62 ctx.accounts.user.authority.key() == ctx.accounts.authority.key(),
63 GumError::UnauthorizedSigner
64)]
65pub fn create_post_handler(
66 ctx: Context<CreatePost>,
67 metadata_uri: String,
68 random_hash: [u8; 32],
69) -> Result<()> {
70 require!(metadata_uri.len() <= MAX_LEN_URI, PostError::URITooLong);
72
73 let post = &mut ctx.accounts.post;
74 post.metadata_uri = metadata_uri;
75 post.random_hash = random_hash;
76 post.profile = *ctx.accounts.profile.to_account_info().key;
77 emit!(PostNew {
79 post: *post.to_account_info().key,
80 profile: *ctx.accounts.profile.to_account_info().key,
81 user: *ctx.accounts.user.to_account_info().key,
82 random_hash: random_hash,
83 metadata_uri: post.metadata_uri.clone(),
84 timestamp: Clock::get()?.unix_timestamp,
85 });
86 Ok(())
87}
88
89#[derive(Accounts, Session)]
91#[instruction(metadata_uri: String)]
92pub struct UpdatePost<'info> {
93 #[account(
95 mut,
96 seeds = [
97 POST_PREFIX_SEED.as_bytes(),
98 post.random_hash.as_ref(),
99 ],
100 bump,
101 has_one = profile,
102 )]
103 pub post: Account<'info, Post>,
104 #[account(
105 seeds = [
106 PROFILE_PREFIX_SEED.as_bytes(),
107 profile.namespace.as_ref().as_bytes(),
108 user.to_account_info().key.as_ref(),
109 ],
110 bump,
111 has_one = user,
112 )]
113 pub profile: Account<'info, Profile>,
114 #[account(
115 seeds = [
116 USER_PREFIX_SEED.as_bytes(),
117 user.random_hash.as_ref(),
118 ],
119 bump,
120 )]
121 pub user: Account<'info, User>,
122 #[session(
123 signer = authority,
124 authority = user.authority.key()
125 )]
126 pub session_token: Option<Account<'info, SessionToken>>,
127 #[account(mut)]
128 pub authority: Signer<'info>,
129 pub system_program: Program<'info, System>,
130}
131
132#[session_auth_or(
134 ctx.accounts.user.authority.key() == ctx.accounts.authority.key(),
135 GumError::UnauthorizedSigner
136)]
137pub fn update_post_handler(ctx: Context<UpdatePost>, metadata_uri: String) -> Result<()> {
138 require!(metadata_uri.len() <= MAX_LEN_URI, PostError::URITooLong);
140 let post = &mut ctx.accounts.post;
141 post.metadata_uri = metadata_uri;
142 emit!(PostUpdated {
144 post: *post.to_account_info().key,
145 profile: *ctx.accounts.profile.to_account_info().key,
146 user: *ctx.accounts.user.to_account_info().key,
147 metadata_uri: post.metadata_uri.clone(),
148 timestamp: Clock::get()?.unix_timestamp,
149 });
150 Ok(())
151}
152
153#[derive(Accounts, Session)]
155#[instruction(metadata_uri: String, random_hash: [u8;32])]
156pub struct CreateComment<'info> {
157 #[account(
159 init,
160 seeds = [
161 POST_PREFIX_SEED.as_bytes(),
162 random_hash.as_ref(),
163 ],
164 bump,
165 payer = authority,
166 space = Post::LEN
167 )]
168 pub post: Account<'info, Post>,
169 #[account(
170 seeds = [
171 PROFILE_PREFIX_SEED.as_bytes(),
172 profile.namespace.as_ref().as_bytes(),
173 user.to_account_info().key.as_ref(),
174 ],
175 bump,
176 has_one = user,
177 )]
178 pub profile: Account<'info, Profile>,
179 #[account(
180 seeds = [
181 USER_PREFIX_SEED.as_bytes(),
182 user.random_hash.as_ref(),
183 ],
184 bump,
185 )]
186 pub user: Account<'info, User>,
187 #[account(
188 seeds = [
189 POST_PREFIX_SEED.as_bytes(),
190 reply_to.random_hash.as_ref(),
191 ],
192 bump,
193 )]
194 pub reply_to: Account<'info, Post>,
195 #[session(
196 signer = authority,
197 authority = user.authority.key()
198 )]
199 pub session_token: Option<Account<'info, SessionToken>>,
200 #[account(mut)]
201 pub authority: Signer<'info>,
202 pub system_program: Program<'info, System>,
204}
205
206#[session_auth_or(
208 ctx.accounts.user.authority.key() == ctx.accounts.authority.key(),
209 GumError::UnauthorizedSigner
210)]
211pub fn create_comment_handler(
212 ctx: Context<CreateComment>,
213 metadata_uri: String,
214 random_hash: [u8; 32],
215) -> Result<()> {
216 require!(metadata_uri.len() <= MAX_LEN_URI, PostError::URITooLong);
218
219 let post = &mut ctx.accounts.post;
220 post.metadata_uri = metadata_uri;
221 post.random_hash = random_hash;
222 post.profile = *ctx.accounts.profile.to_account_info().key;
223 post.reply_to = Some(*ctx.accounts.reply_to.to_account_info().key);
224 emit!(PostCommentNew {
226 post: *post.to_account_info().key,
227 profile: *ctx.accounts.profile.to_account_info().key,
228 user: *ctx.accounts.user.to_account_info().key,
229 random_hash: random_hash,
230 metadata_uri: post.metadata_uri.clone(),
231 reply_to: *ctx.accounts.reply_to.to_account_info().key,
232 timestamp: Clock::get()?.unix_timestamp,
233 });
234 Ok(())
235}
236
237#[derive(Accounts, Session)]
239pub struct DeletePost<'info> {
240 #[account(
242 mut,
243 seeds = [
244 POST_PREFIX_SEED.as_bytes(),
245 post.random_hash.as_ref(),
246 ],
247 bump,
248 has_one = profile,
249 close = refund_receiver,
250 )]
251 pub post: Account<'info, Post>,
252 #[account(
253 seeds = [
254 PROFILE_PREFIX_SEED.as_bytes(),
255 profile.namespace.as_ref().as_bytes(),
256 user.to_account_info().key.as_ref(),
257 ],
258 bump,
259 has_one = user,
260 )]
261 pub profile: Account<'info, Profile>,
262 #[account(
263 seeds = [
264 USER_PREFIX_SEED.as_bytes(),
265 user.random_hash.as_ref(),
266 ],
267 bump,
268 )]
269 pub user: Account<'info, User>,
270
271 #[session(
272 signer = authority,
273 authority = user.authority.key()
274 )]
275 pub session_token: Option<Account<'info, SessionToken>>,
276 #[account(mut)]
277 pub authority: Signer<'info>,
278 #[account(mut, constraint = refund_receiver.key() == user.authority)]
279 pub refund_receiver: SystemAccount<'info>,
280 pub system_program: Program<'info, System>,
281}
282
283#[session_auth_or(
285 ctx.accounts.user.authority.key() == ctx.accounts.authority.key(),
286 GumError::UnauthorizedSigner
287)]
288pub fn delete_post_handler(ctx: Context<DeletePost>) -> Result<()> {
289 emit!(PostDeleted {
291 post: *ctx.accounts.post.to_account_info().key,
292 profile: *ctx.accounts.profile.to_account_info().key,
293 user: *ctx.accounts.user.to_account_info().key,
294 timestamp: Clock::get()?.unix_timestamp,
295 });
296 Ok(())
297}