use {
crate::state::*,
anchor_lang::prelude::*,
hpl_utils::{reallocate, traits::Default},
sol_did::{
cpi::{
accounts::{AddVerificationMethod, Initialize, RemoveVerificationMethod},
add_verification_method, initialize, remove_verification_method,
},
program::SolDid,
state::{DidAccount, VerificationMethod},
},
};
#[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 did_data: AccountInfo<'info>,
#[account(mut)]
pub wallet: Signer<'info>,
pub rent_sysvar: Sysvar<'info, Rent>,
pub system_program: Program<'info, System>,
pub sol_did_program: Program<'info, SolDid>,
}
#[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();
let user_seeds = [b"user".as_ref(), user.username.as_bytes(), &[user.bump]];
let user_signer = &[&user_seeds[..]];
initialize(
CpiContext::new_with_signer(
ctx.accounts.sol_did_program.to_account_info(),
Initialize {
did_data: ctx.accounts.did_data.to_account_info(),
authority: user.to_account_info(),
payer: ctx.accounts.wallet.to_account_info(),
system_program: ctx.accounts.system_program.to_account_info(),
},
user_signer,
),
1024,
)
}
#[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>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, PartialEq)]
pub struct UpdateUserArgs {
pub username: Option<String>,
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.username = args.username.unwrap_or(user.username.clone());
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());
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 did_data: Account<'info, DidAccount>,
#[account(mut)]
pub new_wallet: Signer<'info>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
pub sol_did_program: Program<'info, SolDid>,
pub rent_sysvar: Sysvar<'info, Rent>,
}
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();
let user_seeds = [b"user".as_ref(), user.username.as_bytes(), &[user.bump]];
let user_signer = &[&user_seeds[..]];
add_verification_method(
CpiContext::new_with_signer(
ctx.accounts.sol_did_program.to_account_info(),
AddVerificationMethod {
did_data: ctx.accounts.did_data.to_account_info(),
authority: user.to_account_info(),
},
user_signer,
),
VerificationMethod {
fragment: ctx.accounts.new_wallet.key().to_string(),
flags: 0,
method_type: 0,
key_data: ctx.accounts.new_wallet.key().as_ref().to_vec(),
},
None,
)
}
#[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 = wallet, close = wallet )]
pub wallet_resolver: Account<'info, WalletResolver>,
#[account(mut)]
pub user: Account<'info, User>,
#[account(mut)]
pub did_data: Account<'info, DidAccount>,
#[account(mut)]
pub wallet: AccountInfo<'info>,
#[account(mut)]
pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
pub sol_did_program: Program<'info, SolDid>,
pub rent_sysvar: Sysvar<'info, Rent>,
}
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,
)?;
let user_seeds = [b"user".as_ref(), user.username.as_bytes(), &[user.bump]];
let user_signer = &[&user_seeds[..]];
remove_verification_method(
CpiContext::new_with_signer(
ctx.accounts.sol_did_program.to_account_info(),
RemoveVerificationMethod {
did_data: ctx.accounts.did_data.to_account_info(),
authority: user.to_account_info(),
},
user_signer,
),
ctx.accounts.wallet.key().to_string(),
None,
)
}