use {
crate::{assertions::assert_user, errors::ErrorCode, state::*},
anchor_lang::prelude::*,
hpl_events::HplEvents,
hpl_utils::{reallocate, traits::Default},
};
#[derive(Accounts)]
#[instruction(args: CreateProfileArgs)]
pub struct CreateProfile<'info> {
#[account()]
pub user: Box<Account<'info, User>>,
#[account()]
pub project: Box<Account<'info, Project>>,
#[account(
init, payer = wallet,
space = Profile::get_initial_len(&args),
seeds = [
b"profile".as_ref(),
project.key().as_ref(),
user.key().as_ref(),
&args.identity.to_bytes()[..]
],
bump
)]
pub profile: Account<'info, Profile>,
#[account(mut)]
pub wallet: Signer<'info>,
pub rent_sysvar: Sysvar<'info, Rent>,
pub system_program: Program<'info, System>,
pub hpl_events: Program<'info, HplEvents>,
pub clock: Sysvar<'info, Clock>,
#[account(address = anchor_lang::solana_program::sysvar::instructions::ID)]
pub instructions_sysvar: AccountInfo<'info>,
#[account(mut)]
pub vault: AccountInfo<'info>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, PartialEq)]
pub struct CreateProfileArgs {
pub identity: ProfileIdentity,
pub name: Option<String>,
pub bio: Option<String>,
pub pfp: Option<String>,
}
pub fn create_profile(ctx: Context<CreateProfile>, args: CreateProfileArgs) -> Result<()> {
assert_user(&ctx.accounts.user, ctx.accounts.wallet.key())?;
let profile = &mut ctx.accounts.profile;
profile.set_defaults();
profile.bump = ctx.bumps["profile"];
profile.project = ctx.accounts.project.key();
profile.user = ctx.accounts.user.key();
profile.identity = args.identity.clone();
profile.name = args.name.clone();
profile.bio = args.bio.clone();
profile.pfp = args.pfp.clone();
Event::new_profile(
profile.key(),
profile.try_to_vec().unwrap(),
&ctx.accounts.clock,
)
.emit(ctx.accounts.hpl_events.to_account_info())?;
Ok(())
}
#[derive(Accounts)]
pub struct DeleteProfile<'info> {
#[account()]
pub user: Box<Account<'info, User>>,
#[account()]
pub project: Box<Account<'info, Project>>,
#[account(mut, has_one = user, has_one = project, close = wallet)]
pub profile: Account<'info, Profile>,
#[account(mut)]
pub wallet: Signer<'info>,
pub system_program: Program<'info, System>,
pub hpl_events: Program<'info, HplEvents>,
pub clock: Sysvar<'info, Clock>,
pub rent_sysvar: Sysvar<'info, Rent>,
#[account(address = anchor_lang::solana_program::sysvar::instructions::ID)]
pub instructions_sysvar: AccountInfo<'info>,
#[account(mut)]
pub vault: AccountInfo<'info>,
}
pub fn delete_profile(ctx: Context<DeleteProfile>) -> Result<()> {
assert_user(&ctx.accounts.user, ctx.accounts.wallet.key())?;
Ok(())
}
#[derive(Accounts)]
pub struct ManageProfileData<'info> {
#[account()]
pub project: Box<Account<'info, Project>>,
#[account(mut, has_one = project)]
pub profile: Box<Account<'info, Profile>>,
#[account()]
pub delegate_authority: Option<Account<'info, DelegateAuthority>>,
pub authority: Signer<'info>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
pub hpl_events: Program<'info, HplEvents>,
pub clock: Sysvar<'info, Clock>,
pub rent_sysvar: Sysvar<'info, Rent>,
#[account(address = anchor_lang::solana_program::sysvar::instructions::ID)]
pub instructions_sysvar: AccountInfo<'info>,
#[account(mut)]
pub vault: AccountInfo<'info>,
}
#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct ManageProfileDataArgs {
pub label: String,
pub value: Option<ProfileData>,
pub is_app_context: bool,
}
pub fn manage_profile_data(
ctx: Context<ManageProfileData>,
args: ManageProfileDataArgs,
) -> Result<()> {
assert_eq!(
ctx.accounts
.project
.profile_data_config
.contains_key(&args.label),
true
);
let profile_info = ctx.accounts.profile.to_account_info();
let profile = &mut ctx.accounts.profile;
let to_update = if args.is_app_context {
&mut profile.app_context
} else {
&mut profile.data
};
let mut len = if to_update.contains_key(&args.label) {
if args.value.is_none() {
args.label.len() as isize * -1
} else {
0
}
} else {
args.label.len() as isize
};
if let Some(v) = to_update.get(&args.label) {
len -= v.size() as isize;
}
if let Some(value) = &args.value {
len += value.size() as isize;
}
if len > 0 {
reallocate(
len,
profile_info.clone(),
ctx.accounts.payer.to_account_info(),
&ctx.accounts.rent_sysvar,
&ctx.accounts.system_program,
)?;
}
if let Some(profile_data) = args.value {
if !ctx
.accounts
.project
.validate_profile_data(&args.label, &profile_data)
{
return Err(ErrorCode::InvalidValueType.into());
}
profile.data.insert(args.label, profile_data);
} else {
profile.data.remove(&args.label);
}
if len < 0 {
reallocate(
len,
profile_info,
ctx.accounts.payer.to_account_info(),
&ctx.accounts.rent_sysvar,
&ctx.accounts.system_program,
)?;
}
Event::update_profile(
profile.key(),
profile.try_to_vec().unwrap(),
&ctx.accounts.clock,
)
.emit(ctx.accounts.hpl_events.to_account_info())?;
Ok(())
}