use core::panic;
use {
crate::state::*,
anchor_lang::prelude::*,
hpl_events::HplEvents,
hpl_utils::{reallocate, traits::Default},
};
#[derive(Accounts)]
#[instruction(args: InitializeUserArgs)]
pub struct InitializeUser<'info> {
#[account(
init, payer = wallet,
space = User::LEN,
seeds = [b"user".as_ref(), args.username.as_bytes()],
bump
)]
pub user: Account<'info, User>,
#[account(
init, payer = wallet,
space = WalletResolver::LEN,
seeds = [b"wallet_resolver".as_ref(), wallet.key().as_ref()],
bump
)]
pub wallet_resolver: Account<'info, WalletResolver>,
#[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>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, PartialEq)]
pub struct InitializeUserArgs {
pub username: String,
pub name: String,
pub bio: String,
pub pfp: String,
}
pub fn initialize_user(ctx: Context<InitializeUser>, args: InitializeUserArgs) -> Result<()> {
let user = &mut ctx.accounts.user;
user.set_defaults();
let user_info = user.to_account_info();
let mut diff: usize = 0;
let username_len = args.username.as_bytes().len();
if username_len > 24 {
diff += username_len - 24;
}
let name_len = args.name.as_bytes().len();
if name_len > 24 {
diff += name_len - 24;
}
let bio_len = args.bio.as_bytes().len();
if bio_len > 24 {
diff += bio_len - 24;
}
let pfp_len = args.pfp.as_bytes().len();
if pfp_len > 24 {
diff += pfp_len - 24;
}
if diff > 0 {
reallocate(
diff as isize,
user_info,
ctx.accounts.wallet.to_account_info(),
&ctx.accounts.rent_sysvar,
&ctx.accounts.system_program,
)?;
}
user.bump = ctx.bumps["user"];
user.primary_wallet = ctx.accounts.wallet.key();
user.username = args.username;
user.name = args.name;
user.bio = args.bio;
user.pfp = args.pfp;
let wallet_resolver = &mut ctx.accounts.wallet_resolver;
wallet_resolver.set_defaults();
wallet_resolver.bump = ctx.bumps["wallet_resolver"];
wallet_resolver.user = user.key();
wallet_resolver.wallet = ctx.accounts.wallet.key();
Event::new_user(user.key(), user.try_to_vec().unwrap(), &ctx.accounts.clock)
.emit(ctx.accounts.hpl_events.to_account_info())?;
Ok(())
}
#[derive(Accounts)]
pub struct CloseUser<'info> {
#[account(
mut,
close = wallet,
)]
pub user: Account<'info, User>,
#[account(mut, has_one = user, close = wallet)]
pub wallet_resolver: Account<'info, WalletResolver>,
#[account(mut)]
pub wallet: Signer<'info>,
pub system_program: Program<'info, System>,
}
pub fn close_user(_ctx: Context<CloseUser>) -> Result<()> {
Ok(())
}
#[derive(Accounts)]
#[instruction(_env: String)]
pub struct UpdateUser<'info> {
#[account(
seeds = [b"public_info".as_ref(), _env.as_bytes(), crate::id().as_ref()],
bump = public_info.bump,
)]
pub public_info: Account<'info, PublicInfo>,
#[account(mut)]
pub user: Account<'info, User>,
#[account(mut)]
pub authority: Signer<'info>,
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>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, PartialEq)]
pub struct UpdateUserArgs {
pub name: Option<String>,
pub bio: Option<String>,
pub pfp: Option<String>,
}
pub fn update_user(ctx: Context<UpdateUser>, _env: String, args: UpdateUserArgs) -> Result<()> {
let user = &mut ctx.accounts.user;
user.name = args.name.unwrap_or(user.name.clone());
user.bio = args.bio.unwrap_or(user.bio.clone());
user.pfp = args.pfp.unwrap_or(user.pfp.clone());
Event::update_user(user.key(), user.try_to_vec().unwrap(), &ctx.accounts.clock)
.emit(ctx.accounts.hpl_events.to_account_info())?;
Ok(())
}
#[derive(Accounts)]
#[instruction(_env: String)]
pub struct AddWallet<'info> {
#[account(
seeds = [b"public_info".as_ref(), _env.as_bytes(), crate::id().as_ref()],
bump = public_info.bump,
)]
pub public_info: Account<'info, PublicInfo>,
#[account(
init, payer = new_wallet,
space = WalletResolver::LEN,
seeds = [b"wallet_resolver".as_ref(), new_wallet.key().as_ref()],
bump
)]
pub wallet_resolver: Account<'info, WalletResolver>,
#[account(mut)]
pub user: Account<'info, User>,
#[account(mut)]
pub new_wallet: Signer<'info>,
#[account(mut)]
pub authority: 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>,
}
pub fn add_wallet(ctx: Context<AddWallet>, _env: String) -> Result<()> {
let user = &mut ctx.accounts.user;
reallocate(
32 as isize,
user.to_account_info(),
ctx.accounts.new_wallet.to_account_info(),
&ctx.accounts.rent_sysvar,
&ctx.accounts.system_program,
)?;
user.secondary_wallets.push(ctx.accounts.new_wallet.key());
let wallet_resolver = &mut ctx.accounts.wallet_resolver;
wallet_resolver.set_defaults();
wallet_resolver.bump = ctx.bumps["wallet_resolver"];
wallet_resolver.user = user.key();
wallet_resolver.wallet = ctx.accounts.new_wallet.key();
Event::update_user(user.key(), user.try_to_vec().unwrap(), &ctx.accounts.clock)
.emit(ctx.accounts.hpl_events.to_account_info())?;
Ok(())
}
#[derive(Accounts)]
#[instruction(_env: String)]
pub struct DeleteWallet<'info> {
#[account(
seeds = [b"public_info".as_ref(), _env.as_bytes(), crate::id().as_ref()],
bump = public_info.bump,
)]
pub public_info: Account<'info, PublicInfo>,
#[account( mut, has_one = user, has_one = wallet, close = wallet )]
pub wallet_resolver: Account<'info, WalletResolver>,
#[account(mut)]
pub user: Account<'info, User>,
#[account(mut)]
pub wallet: AccountInfo<'info>,
#[account(mut)]
pub authority: 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>,
}
pub fn delete_wallet(ctx: Context<DeleteWallet>, _env: String) -> Result<()> {
let user = &mut ctx.accounts.user;
let index = user
.secondary_wallets
.clone()
.into_iter()
.position(|x| x == ctx.accounts.wallet.key());
if let Some(i) = index {
user.secondary_wallets.swap_remove(i);
}
reallocate(
-32 as isize,
user.to_account_info(),
ctx.accounts.wallet.to_account_info(),
&ctx.accounts.rent_sysvar,
&ctx.accounts.system_program,
)?;
Event::update_user(user.key(), user.try_to_vec().unwrap(), &ctx.accounts.clock)
.emit(ctx.accounts.hpl_events.to_account_info())?;
Ok(())
}
#[derive(Accounts)]
pub struct CloseOrphanWalletResolver<'info> {
pub user: AccountInfo<'info>,
#[account(mut, has_one = user, has_one = wallet, close = wallet)]
pub wallet_resolver: Account<'info, WalletResolver>,
#[account(mut)]
pub wallet: AccountInfo<'info>,
pub system_program: Program<'info, System>,
}
pub fn close_orphan_wallet_resolver(ctx: Context<CloseOrphanWalletResolver>) -> Result<()> {
if ctx.accounts.user.lamports() > 0 {
panic!("User account still has lamports");
}
Ok(())
}