gpl_core/instructions/
connection.rs

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