gpl_compression/instructions/
comment.rs1use std::convert::AsRef;
2
3use gpl_core::state::{Post, Profile, User};
4
5use anchor_lang::prelude::*;
6use anchor_lang::solana_program::keccak::hashv;
7
8use gpl_core::constants::*;
9
10use gpl_core::errors::PostError;
11use gpl_core::program::GplCore;
12use gpl_core::state::MAX_LEN_URI;
13
14use spl_account_compression::program::SplAccountCompression;
15use spl_account_compression::wrap_application_data_v1;
16use spl_account_compression::Noop;
17
18use crate::events::CompressedCommentNew;
19use crate::state::TreeConfig;
20use crate::utils::LeafSchema;
21use crate::utils::{append_leaf, try_find_asset_id, verify_leaf};
22
23#[derive(Accounts)]
25#[instruction(reply_to: Pubkey, metadata_uri: String, random_hash: [u8; 32], post_root: [u8; 32], post_leaf: [u8; 32], post_index: u32)]
26pub struct CreateCompressedComment<'info> {
27 #[account(
28 seeds = [
29 PROFILE_PREFIX_SEED.as_bytes(),
30 from_profile.namespace.as_ref().as_bytes(),
31 user.to_account_info().key.as_ref(),
32 ],
33 seeds::program = gpl_core_program.key(),
34 bump,
35 has_one = user,
36 )]
37 pub from_profile: Account<'info, Profile>,
38
39 #[account(
40 seeds = [
41 USER_PREFIX_SEED.as_bytes(),
42 user.random_hash.as_ref(),
43 ],
44 seeds::program = gpl_core_program.key(),
45 bump,
46 has_one = authority,
47 )]
48 pub user: Account<'info, User>,
49
50 #[account(seeds = [merkle_tree.key.as_ref()], bump)]
51 pub tree_config: Account<'info, TreeConfig>,
52
53 #[account(mut)]
54 pub merkle_tree: UncheckedAccount<'info>,
56
57 #[account(seeds = [target_merkle_tree.key.as_ref()], bump)]
59 pub target_tree_config: Account<'info, TreeConfig>,
60
61 pub target_merkle_tree: UncheckedAccount<'info>,
63
64 #[account(mut)]
65 pub authority: Signer<'info>,
66
67 pub compression_program: Program<'info, SplAccountCompression>,
68 pub log_wrapper_program: Program<'info, Noop>,
69 pub gpl_core_program: Program<'info, GplCore>,
70 pub system_program: Program<'info, System>,
71}
72
73pub fn create_compressed_comment_handler<'info>(
75 ctx: Context<'_, '_, '_, 'info, CreateCompressedComment<'info>>,
76 reply_to: Pubkey,
77 metadata_uri: String,
78 random_hash: [u8; 32],
79 post_root: [u8; 32],
80 post_leaf: [u8; 32],
81 post_index: u32,
82) -> Result<()> {
83 require!(metadata_uri.len() <= MAX_LEN_URI, PostError::URITooLong);
84
85 verify_leaf(
90 ctx.accounts.target_merkle_tree.key,
91 ctx.bumps["target_tree_config"],
92 post_root,
93 post_leaf,
94 post_index,
95 ctx.remaining_accounts,
96 &ctx.accounts.target_merkle_tree,
97 &ctx.accounts.compression_program,
98 )?;
99
100 let post_seeds = [POST_PREFIX_SEED.as_bytes(), random_hash.as_ref()];
101
102 let (post_id, post_bump) =
103 Pubkey::try_find_program_address(&post_seeds, &GplCore::id()).unwrap();
104
105 let seed_hash = hashv(&post_seeds).to_bytes();
106
107 let asset_id = try_find_asset_id(ctx.accounts.merkle_tree.key, seed_hash)?;
108
109 let post = Post {
110 metadata_uri,
111 random_hash,
112 profile: *ctx.accounts.from_profile.to_account_info().key,
113 reply_to: Some(reply_to),
114 };
115
116 let leaf = LeafSchema {
117 asset_id,
118 seed_hash,
119 data_hash: hashv(&[&post.try_to_vec()?]).to_bytes(),
120 };
121
122 let leaf_node = leaf.to_node()?;
123
124 wrap_application_data_v1(leaf_node.to_vec(), &ctx.accounts.log_wrapper_program)?;
125
126 append_leaf(
127 ctx.accounts.merkle_tree.key,
128 ctx.bumps["tree_config"],
129 &ctx.accounts.authority.to_account_info(),
130 leaf_node,
131 &ctx.accounts.merkle_tree,
132 &ctx.accounts.compression_program,
133 &ctx.accounts.log_wrapper_program,
134 )?;
135
136 emit!(CompressedCommentNew {
137 asset_id,
138 post_id,
139 post_bump,
140 reply_to,
141 profile: *ctx.accounts.from_profile.to_account_info().key,
142 user: *ctx.accounts.user.to_account_info().key,
143 random_hash: random_hash,
144 metadata_uri: post.metadata_uri.clone(),
145 timestamp: Clock::get()?.unix_timestamp,
146 index: 0 });
148 Ok(())
149}