gpl_compression/instructions/
connection.rs1use crate::events::{CompressedConnectionDeleted, CompressedConnectionNew};
2use crate::state::TreeConfig;
3use crate::utils::{append_leaf, replace_leaf, try_find_asset_id, LeafSchema};
4use anchor_lang::Discriminator;
5use gpl_core::errors::ConnectionError;
6use spl_account_compression::wrap_application_data_v1;
7use spl_account_compression::Node;
8
9use gpl_core::state::{Connection, Profile, User};
10
11use anchor_lang::prelude::*;
12use std::convert::AsRef;
13
14use gpl_core::constants::*;
15
16use anchor_lang::solana_program::keccak::hashv;
17use gpl_core::program::GplCore;
18use spl_account_compression::program::SplAccountCompression;
19use spl_account_compression::Noop;
20
21#[derive(Accounts)]
23pub struct CreateCompressedConnection<'info> {
24 #[account(
25 seeds = [
26 PROFILE_PREFIX_SEED.as_bytes(),
27 from_profile.namespace.as_ref().as_bytes(),
28 user.to_account_info().key.as_ref(),
29 ],
30 seeds::program = gpl_core_program.key(),
31 bump,
32 has_one = user,
33 )]
34 pub from_profile: Account<'info, Profile>,
35 #[account(
36 seeds = [
37 PROFILE_PREFIX_SEED.as_bytes(),
38 to_profile.namespace.as_ref().as_bytes(),
39 to_profile.user.as_ref(),
40 ],
41 seeds::program = gpl_core_program.key(),
42 bump,
43 )]
44 pub to_profile: Account<'info, Profile>,
45 #[account(
46 seeds = [
47 USER_PREFIX_SEED.as_bytes(),
48 user.random_hash.as_ref(),
49 ],
50 seeds::program = gpl_core_program.key(),
51 bump,
52 has_one = authority,
53 )]
54 pub user: Account<'info, User>,
55
56 #[account(seeds = [merkle_tree.key.as_ref()], bump)]
57 pub tree_config: Account<'info, TreeConfig>,
58
59 #[account(mut)]
60 pub merkle_tree: UncheckedAccount<'info>,
62
63 #[account(mut)]
64 pub authority: Signer<'info>,
65
66 pub compression_program: Program<'info, SplAccountCompression>,
67 pub log_wrapper_program: Program<'info, Noop>,
68 pub gpl_core_program: Program<'info, GplCore>,
69 pub system_program: Program<'info, System>,
70}
71
72pub fn create_compressed_connection_handler(
74 ctx: Context<CreateCompressedConnection>,
75) -> Result<()> {
76 let from_profile = &ctx.accounts.from_profile;
77 let to_profile = &ctx.accounts.to_profile;
78
79 require_neq!(
81 from_profile.key(),
82 to_profile.key(),
83 ConnectionError::CannotConnectToSelf
84 );
85
86 let connection_seeds = [
87 CONNECTION_PREFIX_SEED.as_bytes(),
88 from_profile.to_account_info().key.as_ref(),
89 to_profile.to_account_info().key.as_ref(),
90 ];
91
92 let (connection_id, connection_bump) =
93 Pubkey::try_find_program_address(&connection_seeds, &GplCore::id()).unwrap();
94
95 let seed_hash = hashv(&connection_seeds).to_bytes();
96
97 let asset_id = try_find_asset_id(ctx.accounts.merkle_tree.key, seed_hash)?;
98
99 let connection = Connection {
100 from_profile: *from_profile.to_account_info().key,
101 to_profile: *to_profile.to_account_info().key,
102 };
103
104 let leaf = LeafSchema {
105 asset_id,
106 seed_hash,
107 data_hash: hashv(&[&Connection::DISCRIMINATOR, &connection.try_to_vec()?]).to_bytes(),
108 };
109
110 let leaf_node = leaf.to_node()?;
111
112 wrap_application_data_v1(leaf_node.to_vec(), &ctx.accounts.log_wrapper_program)?;
113
114 append_leaf(
115 ctx.accounts.merkle_tree.key,
116 ctx.bumps["tree_config"],
117 &ctx.accounts.authority.to_account_info(),
118 leaf_node,
119 &ctx.accounts.merkle_tree,
120 &ctx.accounts.compression_program,
121 &ctx.accounts.log_wrapper_program,
122 )?;
123
124 emit!(CompressedConnectionNew {
126 connection_id,
127 connection_bump,
128 from_profile: *from_profile.to_account_info().key,
129 to_profile: *to_profile.to_account_info().key,
130 asset_id,
131 user: *ctx.accounts.user.to_account_info().key,
132 timestamp: Clock::get()?.unix_timestamp,
133 index: 0 });
135
136 Ok(())
137}
138
139#[derive(Accounts)]
141#[instruction(root: [u8;32], index: u32)]
143pub struct DeleteCompressedConnection<'info> {
144 #[account(
145 seeds = [
146 PROFILE_PREFIX_SEED.as_bytes(),
147 from_profile.namespace.as_ref().as_bytes(),
148 user.to_account_info().key.as_ref(),
149 ],
150 seeds::program = gpl_core_program.key(),
151 bump,
152 has_one = user,
153 )]
154 pub from_profile: Account<'info, Profile>,
155 #[account(
156 seeds = [
157 PROFILE_PREFIX_SEED.as_bytes(),
158 to_profile.namespace.as_ref().as_bytes(),
159 to_profile.user.as_ref(),
160 ],
161 seeds::program = gpl_core_program.key(),
162 bump,
163 )]
164 pub to_profile: Account<'info, Profile>,
165 #[account(
166 seeds = [
167 USER_PREFIX_SEED.as_bytes(),
168 user.random_hash.as_ref(),
169 ],
170 seeds::program = gpl_core_program.key(),
171 bump,
172 has_one = authority,
173 )]
174 pub user: Account<'info, User>,
175
176 #[account(seeds = [merkle_tree.key.as_ref()], bump)]
177 pub tree_config: Account<'info, TreeConfig>,
178
179 #[account(mut)]
180 pub merkle_tree: UncheckedAccount<'info>,
182
183 #[account(mut)]
184 pub authority: Signer<'info>,
185 pub compression_program: Program<'info, SplAccountCompression>,
186 pub log_wrapper_program: Program<'info, Noop>,
187 pub gpl_core_program: Program<'info, GplCore>,
188 pub system_program: Program<'info, System>,
189}
190
191pub fn delete_compressed_connection_handler<'info>(
193 ctx: Context<'_, '_, '_, 'info, DeleteCompressedConnection<'info>>,
194 root: [u8; 32],
195 index: u32,
196) -> Result<()> {
197 let from_profile = &ctx.accounts.from_profile;
198 let to_profile = &ctx.accounts.to_profile;
199
200 require_neq!(
202 from_profile.key(),
203 to_profile.key(),
204 ConnectionError::CannotConnectToSelf
205 );
206
207 let connection_seeds = [
208 CONNECTION_PREFIX_SEED.as_bytes(),
209 from_profile.to_account_info().key.as_ref(),
210 to_profile.to_account_info().key.as_ref(),
211 ];
212
213 let (connection_id, connection_bump) =
214 Pubkey::try_find_program_address(&connection_seeds, &GplCore::id()).unwrap();
215
216 let seed_hash = hashv(&connection_seeds).to_bytes();
217
218 let asset_id = try_find_asset_id(ctx.accounts.merkle_tree.key, seed_hash)?;
219
220 let old_connection = Connection {
221 from_profile: *from_profile.to_account_info().key,
222 to_profile: *to_profile.to_account_info().key,
223 };
224
225 let old_leaf = LeafSchema {
226 asset_id,
227 seed_hash,
228 data_hash: hashv(&[&Connection::DISCRIMINATOR, &old_connection.try_to_vec()?]).to_bytes(),
229 };
230
231 let old_leaf_node = old_leaf.to_node()?;
232
233 let new_leaf_node = Node::default();
234
235 wrap_application_data_v1(new_leaf_node.to_vec(), &ctx.accounts.log_wrapper_program)?;
236
237 replace_leaf(
238 ctx.accounts.merkle_tree.key,
239 ctx.bumps["tree_config"],
240 &ctx.accounts.authority.to_account_info(),
241 &ctx.accounts.merkle_tree,
242 root,
243 old_leaf_node,
244 new_leaf_node,
245 index,
246 ctx.remaining_accounts,
247 &ctx.accounts.compression_program,
248 &ctx.accounts.log_wrapper_program,
249 )?;
250
251 emit!(CompressedConnectionDeleted {
253 connection_id,
254 connection_bump,
255 from_profile: *from_profile.to_account_info().key,
256 to_profile: *to_profile.to_account_info().key,
257 asset_id,
258 user: *ctx.accounts.user.to_account_info().key,
259 timestamp: Clock::get()?.unix_timestamp,
260 index
261 });
262
263 Ok(())
264}