diff --git a/anchor/Anchor.toml b/anchor/Anchor.toml index 34c84235..9aba2376 100644 --- a/anchor/Anchor.toml +++ b/anchor/Anchor.toml @@ -25,6 +25,7 @@ wallet = "~/.config/solana/id.json" [scripts] test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_crud" +#test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_vault" #test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_share_class" #test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_investor" #test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_drift" @@ -33,7 +34,6 @@ test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --test #test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_jupiter" #test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_openfunds" #test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_wsol" -#test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_api_tx" #test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_sol_msol" #test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_policy_hook" diff --git a/anchor/programs/glam/src/error.rs b/anchor/programs/glam/src/error.rs index c673476b..e8e26b0d 100644 --- a/anchor/programs/glam/src/error.rs +++ b/anchor/programs/glam/src/error.rs @@ -1,11 +1,17 @@ use anchor_lang::prelude::*; +#[error_code] +pub enum AccessError { + #[msg("Signer is not authorized")] + NotAuthorized, + #[msg("Integration is disabled")] + IntegrationDisabled, +} + #[error_code] pub enum ManagerError { #[msg("Error closing account: not empty")] CloseNotEmptyError, - #[msg("Error: not authorized")] - NotAuthorizedError, #[msg("Invalid fund name: max 50 chars")] InvalidFundName, #[msg("Too many assets: max 50")] diff --git a/anchor/programs/glam/src/instructions/drift.rs b/anchor/programs/glam/src/instructions/drift.rs index 52673ae1..af0ebad5 100644 --- a/anchor/programs/glam/src/instructions/drift.rs +++ b/anchor/programs/glam/src/instructions/drift.rs @@ -4,6 +4,7 @@ use anchor_spl::token_interface::TokenAccount; use drift::{MarketType, PositionDirection}; use glam_macros::treasury_signer_seeds; +use crate::error::AccessError; use crate::state::*; use drift::cpi::accounts::{ @@ -37,24 +38,24 @@ pub struct DriftInitialize<'info> { pub treasury: SystemAccount<'info>, #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, pub drift_program: Program<'info, Drift>, pub rent: Sysvar<'info, Rent>, pub system_program: Program<'info, System>, } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::DriftInitialize))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::DriftInitialize))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Drift))] #[treasury_signer_seeds] -pub fn drift_initialize_handler(ctx: Context) -> Result<()> { +pub fn initialize_handler(ctx: Context) -> Result<()> { initialize_user_stats(CpiContext::new_with_signer( ctx.accounts.drift_program.to_account_info(), InitializeUserStats { user_stats: ctx.accounts.user_stats.to_account_info(), state: ctx.accounts.state.to_account_info(), authority: ctx.accounts.treasury.to_account_info(), - payer: ctx.accounts.manager.to_account_info(), + payer: ctx.accounts.signer.to_account_info(), rent: ctx.accounts.rent.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), }, @@ -72,7 +73,7 @@ pub fn drift_initialize_handler(ctx: Context) -> Result<()> { user_stats: ctx.accounts.user_stats.to_account_info(), state: ctx.accounts.state.to_account_info(), authority: ctx.accounts.treasury.to_account_info(), - payer: ctx.accounts.manager.to_account_info(), + payer: ctx.accounts.signer.to_account_info(), rent: ctx.accounts.rent.to_account_info(), system_program: ctx.accounts.system_program.to_account_info(), }, @@ -98,15 +99,15 @@ pub struct DriftUpdate<'info> { pub treasury: SystemAccount<'info>, #[account(mut)] - manager: Signer<'info>, + pub signer: Signer<'info>, pub drift_program: Program<'info, Drift>, } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::DriftUpdateUser))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::DriftUpdateUser))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Drift))] #[treasury_signer_seeds] -pub fn drift_update_user_custom_margin_ratio_handler( +pub fn update_user_custom_margin_ratio_handler( ctx: Context, sub_account_id: u16, margin_ratio: u32, @@ -127,10 +128,10 @@ pub fn drift_update_user_custom_margin_ratio_handler( Ok(()) } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::DriftUpdateUser))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::DriftUpdateUser))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Drift))] #[treasury_signer_seeds] -pub fn drift_update_user_margin_trading_enabled_handler( +pub fn update_user_margin_trading_enabled_handler( ctx: Context, sub_account_id: u16, margin_trading_enabled: bool, @@ -151,10 +152,10 @@ pub fn drift_update_user_margin_trading_enabled_handler( Ok(()) } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::DriftUpdateUser))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::DriftUpdateUser))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Drift))] #[treasury_signer_seeds] -pub fn drift_update_user_delegate_handler( +pub fn update_user_delegate_handler( ctx: Context, sub_account_id: u16, delegate: Pubkey, @@ -199,16 +200,16 @@ pub struct DriftDeposit<'info> { pub treasury_ata: Box>, #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, pub drift_program: Program<'info, Drift>, pub token_program: Program<'info, Token>, } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::DriftDeposit))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::DriftDeposit))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Drift))] #[treasury_signer_seeds] -pub fn drift_deposit_handler<'c: 'info, 'info>( +pub fn deposit_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftDeposit<'info>>, market_index: u16, amount: u64, @@ -262,16 +263,16 @@ pub struct DriftWithdraw<'info> { pub drift_ata: Box>, #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, pub drift_program: Program<'info, Drift>, pub token_program: Program<'info, Token>, } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::DriftWithdraw))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::DriftWithdraw))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Drift))] #[treasury_signer_seeds] -pub fn drift_withdraw_handler<'c: 'info, 'info>( +pub fn withdraw_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftWithdraw<'info>>, market_index: u16, amount: u64, @@ -319,16 +320,16 @@ pub struct DriftDeleteUser<'info> { pub treasury: SystemAccount<'info>, #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, pub drift_program: Program<'info, Drift>, pub system_program: Program<'info, System>, } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::DriftDeleteUser))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::DriftDeleteUser))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Drift))] #[treasury_signer_seeds] -pub fn drift_delete_user_handler(ctx: Context) -> Result<()> { +pub fn delete_user_handler(ctx: Context) -> Result<()> { delete_user(CpiContext::new_with_signer( ctx.accounts.drift_program.to_account_info(), DeleteUser { @@ -360,16 +361,16 @@ pub struct DriftPlaceOrders<'info> { pub treasury: SystemAccount<'info>, #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, pub drift_program: Program<'info, Drift>, pub token_program: Program<'info, Token>, } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::DriftPlaceOrders))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::DriftPlaceOrders))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Drift))] #[treasury_signer_seeds] -pub fn drift_place_orders_handler<'c: 'info, 'info>( +pub fn place_orders_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftPlaceOrders<'info>>, order_params: Vec, ) -> Result<()> { @@ -379,7 +380,7 @@ pub fn drift_place_orders_handler<'c: 'info, 'info>( MarketType::Spot => Permission::DriftSpotMarket, MarketType::Perp => Permission::DriftPerpMarket, }; - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, permission)?; + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, permission)?; match order.market_type { MarketType::Spot => { @@ -447,16 +448,16 @@ pub struct DriftCancelOrders<'info> { pub treasury: SystemAccount<'info>, #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, pub drift_program: Program<'info, Drift>, pub token_program: Program<'info, Token>, } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::DriftCancelOrders))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::DriftCancelOrders))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Drift))] #[treasury_signer_seeds] -pub fn drift_cancel_orders_handler<'c: 'info, 'info>( +pub fn cancel_orders_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftCancelOrders<'info>>, market_type: Option, market_index: Option, diff --git a/anchor/programs/glam/src/instructions/fund.rs b/anchor/programs/glam/src/instructions/fund.rs index 9a11c0f0..f477145b 100644 --- a/anchor/programs/glam/src/instructions/fund.rs +++ b/anchor/programs/glam/src/instructions/fund.rs @@ -1,28 +1,42 @@ use crate::{ constants::*, - error::{FundError, ManagerError}, + error::{AccessError, FundError, ManagerError}, state::*, }; use anchor_lang::{prelude::*, system_program}; -use anchor_spl::token_interface::{ - transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked, +use anchor_spl::{ + token::{close_account as close_token_account, CloseAccount as CloseTokenAccount, Token}, + token_2022::{ + close_account as close_token_2022_account, CloseAccount as CloseToken2022Account, Token2022, + }, + token_interface::{transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked}, }; use glam_macros::treasury_signer_seeds; #[derive(Accounts)] #[instruction(fund_model: FundModel)] pub struct InitializeFund<'info> { - #[account(init, seeds = [b"fund".as_ref(), manager.key().as_ref(), fund_model.created.as_ref().unwrap().key.as_ref()], bump, payer = manager, space = 8 + FundAccount::INIT_SIZE)] + #[account( + init, + seeds = [ + b"fund".as_ref(), + signer.key().as_ref(), + fund_model.created.as_ref().unwrap().key.as_ref() + ], + bump, + payer = signer, + space = 8 + FundAccount::INIT_SIZE + )] pub fund: Box>, - #[account(init, seeds = [b"openfunds".as_ref(), fund.key().as_ref()], bump, payer = manager, space = FundMetadataAccount::INIT_SIZE)] + #[account(init, seeds = [b"openfunds".as_ref(), fund.key().as_ref()], bump, payer = signer, space = FundMetadataAccount::INIT_SIZE)] pub openfunds: Box>, #[account(mut, seeds = [b"treasury".as_ref(), fund.key().as_ref()], bump)] pub treasury: SystemAccount<'info>, #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, pub system_program: Program<'info, System>, } @@ -58,7 +72,7 @@ pub fn initialize_fund_handler<'c: 'info, 'info>( fund.treasury = ctx.accounts.treasury.key(); fund.openfunds = ctx.accounts.openfunds.key(); - fund.manager = ctx.accounts.manager.key(); + fund.manager = ctx.accounts.signer.key(); // // Set engine params @@ -94,9 +108,10 @@ pub fn initialize_fund_handler<'c: 'info, 'info>( #[derive(Accounts)] pub struct UpdateFund<'info> { #[account(mut, constraint = fund.manager == signer.key() @ AccessError::NotAuthorized)] - fund: Account<'info, FundAccount>, + pub fund: Account<'info, FundAccount>, + #[account(mut)] - signer: Signer<'info>, + pub signer: Signer<'info>, } pub fn update_fund_handler<'c: 'info, 'info>( @@ -284,19 +299,19 @@ pub fn update_fund_handler<'c: 'info, 'info>( #[derive(Accounts)] pub struct CloseFund<'info> { - #[account(mut, close = manager, has_one = manager @ ManagerError::NotAuthorizedError)] - fund: Account<'info, FundAccount>, + #[account(mut, close = signer, constraint = fund.manager == signer.key() @ AccessError::NotAuthorized)] + pub fund: Account<'info, FundAccount>, - #[account(mut, close = manager)] - openfunds: Account<'info, FundMetadataAccount>, + #[account(mut, close = signer)] + pub openfunds: Account<'info, FundMetadataAccount>, #[account(mut, seeds = [b"treasury".as_ref(), fund.key().as_ref()], bump)] - treasury: SystemAccount<'info>, + pub treasury: SystemAccount<'info>, #[account(mut)] - manager: Signer<'info>, + pub signer: Signer<'info>, - system_program: Program<'info, System>, + pub system_program: Program<'info, System>, } #[treasury_signer_seeds] @@ -310,12 +325,12 @@ pub fn close_fund_handler(ctx: Context) -> Result<()> { solana_program::program::invoke_signed( &solana_program::system_instruction::transfer( ctx.accounts.treasury.key, - ctx.accounts.manager.key, + ctx.accounts.signer.key, ctx.accounts.treasury.lamports(), ), &[ ctx.accounts.treasury.to_account_info(), - ctx.accounts.manager.to_account_info(), + ctx.accounts.signer.to_account_info(), ], treasury_signer_seeds, )?; @@ -327,11 +342,11 @@ pub fn close_fund_handler(ctx: Context) -> Result<()> { #[derive(Accounts)] pub struct SetSubscribeRedeemEnabled<'info> { - #[account(mut, has_one = manager @ ManagerError::NotAuthorizedError)] - fund: Box>, + #[account(mut, constraint = fund.manager == signer.key() @ AccessError::NotAuthorized)] + pub fund: Box>, #[account(mut)] - manager: Signer<'info>, + pub signer: Signer<'info>, } pub fn set_subscribe_redeem_enabled_handler( @@ -362,11 +377,11 @@ pub fn set_subscribe_redeem_enabled_handler( #[derive(Accounts)] pub struct Withdraw<'info> { - #[account(mut, has_one = manager @ ManagerError::NotAuthorizedError)] - fund: Account<'info, FundAccount>, + #[account(mut, constraint = fund.manager == signer.key() @ AccessError::NotAuthorized)] + pub fund: Account<'info, FundAccount>, #[account(mut, seeds = [b"treasury".as_ref(), fund.key().as_ref()], bump)] - treasury: SystemAccount<'info>, + pub treasury: SystemAccount<'info>, pub asset: Box>, @@ -381,15 +396,15 @@ pub struct Withdraw<'info> { #[account( mut, associated_token::mint = asset, - associated_token::authority = manager, + associated_token::authority = signer, associated_token::token_program = token_program )] pub manager_ata: Box>, #[account(mut)] - manager: Signer<'info>, + pub signer: Signer<'info>, - token_program: Interface<'info, TokenInterface>, + pub token_program: Interface<'info, TokenInterface>, } #[treasury_signer_seeds] @@ -419,3 +434,48 @@ pub fn withdraw(ctx: Context, amount: u64) -> Result<()> { Ok(()) } + +#[derive(Accounts)] +pub struct CloseTokenAccounts<'info> { + #[account(mut, constraint = fund.manager == signer.key() @ AccessError::NotAuthorized)] + pub fund: Account<'info, FundAccount>, + + #[account(mut, seeds = [b"treasury".as_ref(), fund.key().as_ref()], bump)] + pub treasury: SystemAccount<'info>, + + #[account(mut)] + pub signer: Signer<'info>, + + pub token_program: Program<'info, Token>, + pub token_2022_program: Program<'info, Token2022>, +} + +#[treasury_signer_seeds] +pub fn close_token_accounts_handler<'info>( + ctx: Context<'_, '_, '_, 'info, CloseTokenAccounts<'info>>, +) -> Result<()> { + ctx.remaining_accounts.iter().try_for_each(|account| { + if account.owner.eq(&ctx.accounts.token_program.key()) { + close_token_account(CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + CloseTokenAccount { + account: account.to_account_info(), + destination: ctx.accounts.treasury.to_account_info(), + authority: ctx.accounts.treasury.to_account_info(), + }, + treasury_signer_seeds, + )) + } else { + close_token_2022_account(CpiContext::new_with_signer( + ctx.accounts.token_2022_program.to_account_info(), + CloseToken2022Account { + account: account.to_account_info(), + destination: ctx.accounts.treasury.to_account_info(), + authority: ctx.accounts.treasury.to_account_info(), + }, + treasury_signer_seeds, + )) + } + })?; + Ok(()) +} diff --git a/anchor/programs/glam/src/instructions/investor.rs b/anchor/programs/glam/src/instructions/investor.rs index 7fe16a5a..44469b94 100644 --- a/anchor/programs/glam/src/instructions/investor.rs +++ b/anchor/programs/glam/src/instructions/investor.rs @@ -14,14 +14,16 @@ use solana_program::stake::state::warmup_cooldown_rate; use crate::constants::{self, WSOL}; use crate::error::{FundError, InvestorError, PolicyError}; +use crate::instructions::policy_hook::PolicyAccount; use crate::state::pyth_price::PriceExt; -use crate::{state::*, PolicyAccount}; +use crate::state::*; fn log_decimal(amount: u64, minus_decimals: i32) -> f64 { amount as f64 * 10f64.powf(minus_decimals as f64) } #[derive(Accounts)] +#[instruction(_share_class_id: u8)] pub struct Subscribe<'info> { #[account()] pub fund: Box>, @@ -32,7 +34,7 @@ pub struct Subscribe<'info> { // the shares to mint #[account( mut, - seeds = [b"share".as_ref(), &[0u8], fund.key().as_ref()], //TODO: add share_class_idx to instruction + seeds = [b"share".as_ref(), &[_share_class_id], fund.key().as_ref()], bump, mint::authority = share_class, mint::token_program = token_2022_program @@ -82,6 +84,7 @@ pub struct Subscribe<'info> { pub fn subscribe_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, Subscribe<'info>>, + _share_class_id: u8, amount: u64, skip_state: bool, ) -> Result<()> { diff --git a/anchor/programs/glam/src/instructions/jupiter.rs b/anchor/programs/glam/src/instructions/jupiter.rs index 18616fcb..d518c1c5 100644 --- a/anchor/programs/glam/src/instructions/jupiter.rs +++ b/anchor/programs/glam/src/instructions/jupiter.rs @@ -157,8 +157,11 @@ fn parse_shared_accounts_route(ctx: &Context) -> (bool, usize) { vec![Permission::JupiterSwapFundAssets, Permission::JupiterSwapAnyAsset] ) )] +#[access_control( + acl::check_integration(&ctx.accounts.fund, IntegrationName::JupiterSwap) +)] #[treasury_signer_seeds] -pub fn jupiter_swap<'c: 'info, 'info>( +pub fn swap_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, JupiterSwap<'info>>, amount: u64, data: Vec, @@ -290,7 +293,7 @@ pub struct InitLockedVoterEscrow<'info> { acl::check_integration(&ctx.accounts.fund, IntegrationName::JupiterVote) )] #[treasury_signer_seeds] -pub fn init_locked_voter_escrow<'info>(ctx: Context) -> Result<()> { +pub fn init_locked_voter_escrow_handler<'info>(ctx: Context) -> Result<()> { new_escrow(CpiContext::new_with_signer( ctx.accounts.locked_voter_program.to_account_info(), NewEscrow { @@ -337,7 +340,7 @@ pub struct ToogleMaxLock<'info> { acl::check_integration(&ctx.accounts.fund, IntegrationName::JupiterVote) )] #[treasury_signer_seeds] -pub fn toggle_max_lock<'info>(ctx: Context, value: bool) -> Result<()> { +pub fn toggle_max_lock_handler<'info>(ctx: Context, value: bool) -> Result<()> { jup_toggle_max_lock( CpiContext::new_with_signer( ctx.accounts.locked_voter_program.to_account_info(), @@ -388,7 +391,7 @@ pub struct IncreaseLockedAmount<'info> { acl::check_integration(&ctx.accounts.fund, IntegrationName::JupiterVote) )] #[treasury_signer_seeds] -pub fn increase_locked_amount<'info>( +pub fn increase_locked_amount_handler<'info>( ctx: Context, amount: u64, ) -> Result<()> { @@ -443,7 +446,7 @@ pub struct PartialUnstaking<'info> { acl::check_integration(&ctx.accounts.fund, IntegrationName::JupiterVote) )] #[treasury_signer_seeds] -pub fn open_partial_unstaking<'info>( +pub fn open_partial_unstaking_handler<'info>( ctx: Context, amount: u64, memo: String, @@ -473,7 +476,7 @@ pub fn open_partial_unstaking<'info>( acl::check_integration(&ctx.accounts.fund, IntegrationName::JupiterVote) )] #[treasury_signer_seeds] -pub fn merge_partial_unstaking<'info>(ctx: Context) -> Result<()> { +pub fn merge_partial_unstaking_handler<'info>(ctx: Context) -> Result<()> { jup_merge_partial_unstaking(CpiContext::new_with_signer( ctx.accounts.locked_voter_program.to_account_info(), MergePartialUnstaking { @@ -521,7 +524,7 @@ pub struct WithdrawAllStakedJup<'info> { acl::check_integration(&ctx.accounts.fund, IntegrationName::JupiterVote) )] #[treasury_signer_seeds] -pub fn withdraw_all_staked_jup<'info>(ctx: Context) -> Result<()> { +pub fn withdraw_all_staked_jup_handler<'info>(ctx: Context) -> Result<()> { withdraw(CpiContext::new_with_signer( ctx.accounts.locked_voter_program.to_account_info(), Withdraw { @@ -576,7 +579,9 @@ pub struct WithdrawPartialUnstaking<'info> { acl::check_integration(&ctx.accounts.fund, IntegrationName::JupiterVote) )] #[treasury_signer_seeds] -pub fn withdraw_partial_unstaking<'info>(ctx: Context) -> Result<()> { +pub fn withdraw_partial_unstaking_handler<'info>( + ctx: Context, +) -> Result<()> { jup_partial_withdraw(CpiContext::new_with_signer( ctx.accounts.locked_voter_program.to_account_info(), JupWithdrawPartialUnstaking { @@ -624,7 +629,7 @@ pub struct NewVote<'info> { acl::check_integration(&ctx.accounts.fund, IntegrationName::JupiterVote) )] #[treasury_signer_seeds] -pub fn new_vote<'info>(ctx: Context) -> Result<()> { +pub fn new_vote_handler<'info>(ctx: Context) -> Result<()> { jup_new_vote( CpiContext::new_with_signer( ctx.accounts.governance_program.to_account_info(), @@ -679,7 +684,7 @@ pub struct CastVote<'info> { acl::check_integration(&ctx.accounts.fund, IntegrationName::JupiterVote) )] #[treasury_signer_seeds] -pub fn cast_vote<'info>(ctx: Context, side: u8) -> Result<()> { +pub fn cast_vote_handler<'info>(ctx: Context, side: u8) -> Result<()> { jup_cast_vote( CpiContext::new_with_signer( ctx.accounts.locked_voter_program.to_account_info(), diff --git a/anchor/programs/glam/src/instructions/marinade.rs b/anchor/programs/glam/src/instructions/marinade.rs index 04a5db89..7912b566 100644 --- a/anchor/programs/glam/src/instructions/marinade.rs +++ b/anchor/programs/glam/src/instructions/marinade.rs @@ -12,10 +12,11 @@ use marinade::program::MarinadeFinance; use marinade::state::delayed_unstake_ticket::TicketAccountData; #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Stake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Stake) )] +#[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Marinade))] #[treasury_signer_seeds] -pub fn marinade_deposit_sol<'c: 'info, 'info>( +pub fn marinade_deposit_sol_handler<'c: 'info, 'info>( ctx: Context, lamports: u64, ) -> Result<()> { @@ -42,10 +43,11 @@ pub fn marinade_deposit_sol<'c: 'info, 'info>( } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Stake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Stake) )] +#[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Marinade))] #[treasury_signer_seeds] -pub fn marinade_deposit_stake<'c: 'info, 'info>( +pub fn marinade_deposit_stake_handler<'c: 'info, 'info>( ctx: Context, validator_idx: u32, ) -> Result<()> { @@ -74,10 +76,11 @@ pub fn marinade_deposit_stake<'c: 'info, 'info>( } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Unstake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Unstake) )] +#[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Marinade))] #[treasury_signer_seeds] -pub fn marinade_delayed_unstake<'c: 'info, 'info>( +pub fn delayed_unstake_handler<'c: 'info, 'info>( ctx: Context, msol_amount: u64, ticket_id: String, @@ -100,7 +103,7 @@ pub fn marinade_delayed_unstake<'c: 'info, 'info>( CpiContext::new_with_signer( ctx.accounts.system_program.to_account_info(), system_program::CreateAccount { - from: ctx.accounts.manager.to_account_info(), // treasury PDA + from: ctx.accounts.signer.to_account_info(), // treasury PDA to: ctx.accounts.ticket.to_account_info().clone(), }, signer_seeds, @@ -137,10 +140,11 @@ pub fn marinade_delayed_unstake<'c: 'info, 'info>( } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Unstake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Unstake) )] +#[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Marinade))] #[treasury_signer_seeds] -pub fn marinade_claim_tickets<'info>( +pub fn claim_tickets_handler<'info>( ctx: Context<'_, '_, '_, 'info, MarinadeClaimTickets<'info>>, ) -> Result<()> { let fund = &mut ctx.accounts.fund; @@ -174,10 +178,11 @@ pub fn marinade_claim_tickets<'info>( } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::LiquidUnstake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::LiquidUnstake) )] +#[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Marinade))] #[treasury_signer_seeds] -pub fn marinade_liquid_unstake<'c: 'info, 'info>( +pub fn liquid_unstake_handler<'c: 'info, 'info>( ctx: Context, msol_amount: u64, ) -> Result<()> { @@ -203,7 +208,7 @@ pub fn marinade_liquid_unstake<'c: 'info, 'info>( #[derive(Accounts)] pub struct MarinadeDepositSol<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(has_one = treasury)] pub fund: Box>, @@ -240,7 +245,7 @@ pub struct MarinadeDepositSol<'info> { #[account( init_if_needed, - payer = manager, + payer = signer, associated_token::mint = msol_mint, associated_token::authority = treasury, )] @@ -255,7 +260,7 @@ pub struct MarinadeDepositSol<'info> { #[derive(Accounts)] pub struct MarinadeDepositStake<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(has_one = treasury)] pub fund: Box>, @@ -290,7 +295,7 @@ pub struct MarinadeDepositStake<'info> { #[account( init_if_needed, - payer = manager, + payer = signer, associated_token::mint = msol_mint, associated_token::authority = treasury, )] @@ -310,7 +315,7 @@ pub struct MarinadeDepositStake<'info> { // #[instruction(ticket_id: String)] pub struct MarinadeDelayedUnstake<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(mut, has_one = treasury)] pub fund: Box>, @@ -350,7 +355,7 @@ pub struct MarinadeDelayedUnstake<'info> { #[derive(Accounts)] pub struct MarinadeClaimTickets<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(mut, has_one = treasury)] pub fund: Box>, @@ -375,7 +380,7 @@ pub struct MarinadeClaimTickets<'info> { #[derive(Accounts)] pub struct MarinadeLiquidUnstake<'info> { - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(has_one = treasury)] pub fund: Box>, diff --git a/anchor/programs/glam/src/instructions/share_class.rs b/anchor/programs/glam/src/instructions/share_class.rs index bed88a36..bdbbb7bf 100644 --- a/anchor/programs/glam/src/instructions/share_class.rs +++ b/anchor/programs/glam/src/instructions/share_class.rs @@ -1,12 +1,11 @@ use crate::{ - error::{FundError, ManagerError, ShareClassError}, + error::{AccessError, FundError, ShareClassError}, policy_hook::TRANSFER_HOOK_EXTRA_ACCOUNTS, state::*, ID, }; use anchor_lang::{prelude::*, system_program}; use anchor_spl::{ - token::{close_account as close_token_account, CloseAccount as CloseTokenAccount, Token}, token_2022::{ self, close_account as close_token_2022_account, spl_token_2022::{ @@ -22,7 +21,7 @@ use anchor_spl::{ TransferChecked, }, }; -use glam_macros::{share_class_signer_seeds, treasury_signer_seeds}; +use glam_macros::share_class_signer_seeds; use { spl_tlv_account_resolution::{ account::ExtraAccountMeta, seeds::Seed, state::ExtraAccountMetaList, @@ -50,18 +49,18 @@ pub struct AddShareClass<'info> { space = ExtraAccountMetaList::size_of(TRANSFER_HOOK_EXTRA_ACCOUNTS).unwrap(), seeds = [b"extra-account-metas", share_class_mint.key().as_ref()], bump, - payer = manager, + payer = signer, )] pub extra_account_meta_list: UncheckedAccount<'info>, - #[account(mut, has_one = manager @ ManagerError::NotAuthorizedError)] + #[account(mut, constraint = fund.manager == signer.key() @ AccessError::NotAuthorized)] pub fund: Box>, #[account(mut)] pub openfunds: Box>, #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, pub system_program: Program<'info, System>, pub token_2022_program: Program<'info, Token2022>, @@ -151,12 +150,6 @@ pub fn add_share_class_handler<'c: 'info, 'info>( other => other, }; - msg!( - "share_class_metadata.permanent_delegate: {:?}", - share_class_metadata.permanent_delegate, - ); - msg!("share permanent delegate: {:?}", share_permanent_delegate); - let default_account_state_frozen = share_class_metadata.default_account_state_frozen; let seeds = &[ @@ -192,7 +185,7 @@ pub fn add_share_class_handler<'c: 'info, 'info>( CpiContext::new_with_signer( ctx.accounts.token_2022_program.to_account_info(), system_program::CreateAccount { - from: ctx.accounts.manager.to_account_info(), + from: ctx.accounts.signer.to_account_info(), to: share_mint.clone(), }, signer_seeds, @@ -354,18 +347,18 @@ pub fn add_share_class_handler<'c: 'info, 'info>( #[instruction(share_class_id: u8)] pub struct SetTokenAccountsStates<'info> { #[account(mut, seeds = [b"share".as_ref(), &[share_class_id], fund.key().as_ref()], bump)] - share_class_mint: InterfaceAccount<'info, Mint>, + pub share_class_mint: InterfaceAccount<'info, Mint>, - #[account(mut, has_one = manager @ ManagerError::NotAuthorizedError)] - fund: Box>, + #[account(mut)] + pub fund: Box>, #[account(mut)] - manager: Signer<'info>, + pub signer: Signer<'info>, - token_2022_program: Program<'info, Token2022>, + pub token_2022_program: Program<'info, Token2022>, } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::SetTokenAccountsStates))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::SetTokenAccountsStates))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Mint))] #[share_class_signer_seeds] pub fn set_token_accounts_states_handler<'info>( @@ -424,51 +417,6 @@ pub fn set_token_accounts_states_handler<'info>( Ok(()) } -#[derive(Accounts)] -pub struct CloseTokenAccounts<'info> { - #[account(mut, has_one = manager @ ManagerError::NotAuthorizedError)] - fund: Account<'info, FundAccount>, - - #[account(mut, seeds = [b"treasury".as_ref(), fund.key().as_ref()], bump)] - treasury: SystemAccount<'info>, - - #[account(mut)] - manager: Signer<'info>, - - token_program: Program<'info, Token>, - token_2022_program: Program<'info, Token2022>, -} - -#[treasury_signer_seeds] -pub fn close_token_accounts_handler<'info>( - ctx: Context<'_, '_, '_, 'info, CloseTokenAccounts<'info>>, -) -> Result<()> { - ctx.remaining_accounts.iter().try_for_each(|account| { - if account.owner.eq(&ctx.accounts.token_program.key()) { - close_token_account(CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - CloseTokenAccount { - account: account.to_account_info(), - destination: ctx.accounts.treasury.to_account_info(), - authority: ctx.accounts.treasury.to_account_info(), - }, - treasury_signer_seeds, - )) - } else { - close_token_2022_account(CpiContext::new_with_signer( - ctx.accounts.token_2022_program.to_account_info(), - CloseToken2022Account { - account: account.to_account_info(), - destination: ctx.accounts.treasury.to_account_info(), - authority: ctx.accounts.treasury.to_account_info(), - }, - treasury_signer_seeds, - )) - } - })?; - Ok(()) -} - #[derive(Accounts)] #[instruction(share_class_id: u8)] pub struct ForceTransferShare<'info> { @@ -500,18 +448,18 @@ pub struct ForceTransferShare<'info> { mint::authority = share_class_mint, mint::token_program = token_2022_program )] - share_class_mint: InterfaceAccount<'info, Mint>, + pub share_class_mint: InterfaceAccount<'info, Mint>, - #[account(mut, has_one = manager @ ManagerError::NotAuthorizedError)] - fund: Box>, + #[account(mut)] + pub fund: Box>, #[account(mut)] - manager: Signer<'info>, + pub signer: Signer<'info>, - token_2022_program: Program<'info, Token2022>, + pub token_2022_program: Program<'info, Token2022>, } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::ForceTransferShare))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::ForceTransferShare))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Mint))] #[share_class_signer_seeds] pub fn force_transfer_share_handler( @@ -569,18 +517,18 @@ pub struct BurnShare<'info> { mint::authority = share_class_mint, mint::token_program = token_2022_program )] - share_class_mint: InterfaceAccount<'info, Mint>, + pub share_class_mint: InterfaceAccount<'info, Mint>, - #[account(mut, has_one = manager @ ManagerError::NotAuthorizedError)] - fund: Box>, + #[account(mut)] + pub fund: Box>, #[account(mut)] - manager: Signer<'info>, + pub signer: Signer<'info>, - token_2022_program: Program<'info, Token2022>, + pub token_2022_program: Program<'info, Token2022>, } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::BurnShare))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::BurnShare))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Mint))] #[share_class_signer_seeds] pub fn burn_share_handler(ctx: Context, share_class_id: u8, amount: u64) -> Result<()> { @@ -630,16 +578,16 @@ pub struct MintShare<'info> { )] share_class_mint: InterfaceAccount<'info, Mint>, - #[account(mut, has_one = manager @ ManagerError::NotAuthorizedError)] + #[account(mut)] pub fund: Box>, #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, pub token_2022_program: Program<'info, Token2022>, } -#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::MintShare))] +#[access_control(acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::MintShare))] #[access_control(acl::check_integration(&ctx.accounts.fund, IntegrationName::Mint))] #[share_class_signer_seeds] pub fn mint_share_handler<'info>( @@ -675,11 +623,11 @@ pub struct UpdateShareClass<'info> { )] share_class_mint: InterfaceAccount<'info, Mint>, - #[account(mut, has_one = manager @ ManagerError::NotAuthorizedError)] + #[account(mut, constraint = fund.manager == signer.key() @ AccessError::NotAuthorized)] pub fund: Box>, #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, pub token_2022_program: Program<'info, Token2022>, } @@ -710,11 +658,11 @@ pub fn update_share_class_handler( #[derive(Accounts)] #[instruction(share_class_id: u8)] pub struct CloseShareClass<'info> { - #[account(mut, has_one = manager @ ManagerError::NotAuthorizedError)] - fund: Account<'info, FundAccount>, + #[account(mut, constraint = fund.manager == signer.key() @ AccessError::NotAuthorized)] + pub fund: Account<'info, FundAccount>, #[account(mut, seeds = [b"treasury".as_ref(), fund.key().as_ref()], bump)] - treasury: SystemAccount<'info>, + pub treasury: SystemAccount<'info>, #[account( mut, @@ -723,9 +671,11 @@ pub struct CloseShareClass<'info> { &[share_class_id], fund.key().as_ref() ], - bump, mint::authority = share_class_mint, mint::token_program = token_2022_program + bump, + mint::authority = share_class_mint, + mint::token_program = token_2022_program )] - share_class_mint: InterfaceAccount<'info, Mint>, + pub share_class_mint: InterfaceAccount<'info, Mint>, /// CHECK: Token2022 Transfer Hook, we manually close it #[account( @@ -739,9 +689,9 @@ pub struct CloseShareClass<'info> { pub openfunds: Box>, #[account(mut)] - manager: Signer<'info>, + pub signer: Signer<'info>, - token_2022_program: Program<'info, Token2022>, + pub token_2022_program: Program<'info, Token2022>, } #[share_class_signer_seeds] diff --git a/anchor/programs/glam/src/instructions/stake.rs b/anchor/programs/glam/src/instructions/stake.rs index bcf5a318..6afefefb 100644 --- a/anchor/programs/glam/src/instructions/stake.rs +++ b/anchor/programs/glam/src/instructions/stake.rs @@ -8,7 +8,7 @@ use glam_macros::treasury_signer_seeds; #[derive(Accounts)] pub struct InitializeAndDelegateStake<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(mut, has_one = treasury)] pub fund: Box>, @@ -35,10 +35,13 @@ pub struct InitializeAndDelegateStake<'info> { } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Stake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Stake) +)] +#[access_control( + acl::check_integration(&ctx.accounts.fund, IntegrationName::NativeStaking) )] #[treasury_signer_seeds] -pub fn initialize_and_delegate_stake<'c: 'info, 'info>( +pub fn initialize_and_delegate_stake_handler<'c: 'info, 'info>( ctx: Context, lamports: u64, stake_account_id: String, @@ -126,7 +129,7 @@ pub fn initialize_and_delegate_stake<'c: 'info, 'info>( #[derive(Accounts)] pub struct DeactivateStakeAccounts<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(has_one = treasury)] pub fund: Box>, @@ -139,10 +142,13 @@ pub struct DeactivateStakeAccounts<'info> { } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Unstake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Unstake) +)] +#[access_control( + acl::check_integration(&ctx.accounts.fund, IntegrationName::NativeStaking) )] #[treasury_signer_seeds] -pub fn deactivate_stake_accounts<'info>( +pub fn deactivate_stake_accounts_handler<'info>( ctx: Context<'_, '_, '_, 'info, DeactivateStakeAccounts<'info>>, ) -> Result<()> { ctx.remaining_accounts.iter().for_each(|stake_account| { @@ -163,7 +169,7 @@ pub fn deactivate_stake_accounts<'info>( #[derive(Accounts)] pub struct WithdrawFromStakeAccounts<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(mut, has_one = treasury)] pub fund: Box>, @@ -177,10 +183,13 @@ pub struct WithdrawFromStakeAccounts<'info> { } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Unstake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Unstake) +)] +#[access_control( + acl::check_integration(&ctx.accounts.fund, IntegrationName::NativeStaking) )] #[treasury_signer_seeds] -pub fn withdraw_from_stake_accounts<'info>( +pub fn withdraw_from_stake_accounts_handler<'info>( ctx: Context<'_, '_, '_, 'info, WithdrawFromStakeAccounts<'info>>, ) -> Result<()> { let fund = &mut ctx.accounts.fund; @@ -212,7 +221,7 @@ pub fn withdraw_from_stake_accounts<'info>( #[derive(Accounts)] pub struct MergeStakeAccounts<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(mut, has_one = treasury)] pub fund: Box>, @@ -234,10 +243,15 @@ pub struct MergeStakeAccounts<'info> { } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Stake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Stake) +)] +#[access_control( + acl::check_integration(&ctx.accounts.fund, IntegrationName::NativeStaking) )] #[treasury_signer_seeds] -pub fn merge_stake_accounts<'c: 'info, 'info>(ctx: Context) -> Result<()> { +pub fn merge_stake_accounts_handler<'c: 'info, 'info>( + ctx: Context, +) -> Result<()> { let ix = solana_program::stake::instruction::merge( &ctx.accounts.to_stake.key(), &ctx.accounts.from_stake.key(), @@ -265,7 +279,7 @@ pub fn merge_stake_accounts<'c: 'info, 'info>(ctx: Context) #[derive(Accounts)] pub struct SplitStakeAccount<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(mut, has_one = treasury)] pub fund: Box>, @@ -287,10 +301,13 @@ pub struct SplitStakeAccount<'info> { } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Stake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Unstake) +)] +#[access_control( + acl::check_integration(&ctx.accounts.fund, IntegrationName::NativeStaking) )] #[treasury_signer_seeds] -pub fn split_stake_account<'c: 'info, 'info>( +pub fn split_stake_account_handler<'c: 'info, 'info>( ctx: Context, lamports: u64, new_stake_account_id: String, @@ -355,7 +372,7 @@ pub fn split_stake_account<'c: 'info, 'info>( #[derive(Accounts)] pub struct RedelegateStake<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(mut, has_one = treasury)] pub fund: Box>, @@ -383,10 +400,13 @@ pub struct RedelegateStake<'info> { } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Stake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Stake) +)] +#[access_control( + acl::check_integration(&ctx.accounts.fund, IntegrationName::NativeStaking) )] #[treasury_signer_seeds] -pub fn redelegate_stake<'c: 'info, 'info>( +pub fn redelegate_stake_handler<'c: 'info, 'info>( ctx: Context, new_stake_account_id: String, new_stake_account_bump: u8, diff --git a/anchor/programs/glam/src/instructions/stake_pool.rs b/anchor/programs/glam/src/instructions/stake_pool.rs index 4c3bce23..2d0168b8 100644 --- a/anchor/programs/glam/src/instructions/stake_pool.rs +++ b/anchor/programs/glam/src/instructions/stake_pool.rs @@ -28,7 +28,7 @@ impl anchor_lang::Ids for StakePoolInterface { #[derive(Accounts)] pub struct StakePoolDepositSol<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(has_one = treasury)] pub fund: Box>, @@ -36,8 +36,6 @@ pub struct StakePoolDepositSol<'info> { #[account(mut, seeds = [b"treasury".as_ref(), fund.key().as_ref()], bump)] pub treasury: SystemAccount<'info>, - pub stake_pool_program: Interface<'info, StakePoolInterface>, - /// CHECK: checked by stake pool program #[account(mut)] pub stake_pool: AccountInfo<'info>, @@ -59,22 +57,24 @@ pub struct StakePoolDepositSol<'info> { #[account( init_if_needed, - payer = manager, + payer = signer, associated_token::mint = pool_mint, associated_token::authority = treasury, )] pub mint_to: Account<'info, TokenAccount>, + pub stake_pool_program: Interface<'info, StakePoolInterface>, pub associated_token_program: Program<'info, AssociatedToken>, pub system_program: Program<'info, System>, pub token_program: Interface<'info, TokenInterface>, } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Stake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Stake) )] +#[access_control(acl::check_stake_pool_integration(&ctx.accounts.fund, &ctx.accounts.stake_pool_program.key))] #[treasury_signer_seeds] -pub fn stake_pool_deposit_sol<'c: 'info, 'info>( +pub fn deposit_sol_handler<'c: 'info, 'info>( ctx: Context, lamports: u64, ) -> Result<()> { @@ -114,7 +114,7 @@ pub fn stake_pool_deposit_sol<'c: 'info, 'info>( #[derive(Accounts)] pub struct StakePoolDepositStake<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(mut, has_one = treasury)] pub fund: Box>, @@ -127,7 +127,7 @@ pub struct StakePoolDepositStake<'info> { #[account( init_if_needed, - payer = manager, + payer = signer, associated_token::mint = pool_mint, associated_token::authority = treasury, )] @@ -162,11 +162,10 @@ pub struct StakePoolDepositStake<'info> { #[account(mut)] pub reserve_stake_account: Box>, - pub stake_pool_program: Interface<'info, StakePoolInterface>, - pub clock: Sysvar<'info, Clock>, pub stake_history: Sysvar<'info, StakeHistory>, + pub stake_pool_program: Interface<'info, StakePoolInterface>, pub associated_token_program: Program<'info, AssociatedToken>, pub system_program: Program<'info, System>, pub token_program: Interface<'info, TokenInterface>, @@ -174,12 +173,11 @@ pub struct StakePoolDepositStake<'info> { } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Stake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Stake) )] +#[access_control(acl::check_stake_pool_integration(&ctx.accounts.fund, &ctx.accounts.stake_pool_program.key))] #[treasury_signer_seeds] -pub fn stake_pool_deposit_stake<'c: 'info, 'info>( - ctx: Context, -) -> Result<()> { +pub fn deposit_stake_handler<'c: 'info, 'info>(ctx: Context) -> Result<()> { let vec_ix = deposit_stake( ctx.accounts.stake_pool_program.key, ctx.accounts.stake_pool.key, @@ -231,7 +229,7 @@ pub fn stake_pool_deposit_stake<'c: 'info, 'info>( #[derive(Accounts)] pub struct StakePoolWithdrawSol<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(has_one = treasury)] pub fund: Box>, @@ -239,8 +237,6 @@ pub struct StakePoolWithdrawSol<'info> { #[account(mut, seeds = [b"treasury".as_ref(), fund.key().as_ref()], bump)] pub treasury: SystemAccount<'info>, - pub stake_pool_program: Interface<'info, StakePoolInterface>, - /// CHECK: checked by stake pool program #[account(mut)] pub stake_pool: AccountInfo<'info>, @@ -266,16 +262,18 @@ pub struct StakePoolWithdrawSol<'info> { pub clock: Sysvar<'info, Clock>, pub stake_history: Sysvar<'info, StakeHistory>, + pub stake_pool_program: Interface<'info, StakePoolInterface>, pub system_program: Program<'info, System>, pub token_program: Interface<'info, TokenInterface>, pub stake_program: Program<'info, Stake>, } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::LiquidUnstake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::LiquidUnstake) )] +#[access_control(acl::check_stake_pool_integration(&ctx.accounts.fund, &ctx.accounts.stake_pool_program.key))] #[treasury_signer_seeds] -pub fn stake_pool_withdraw_sol<'c: 'info, 'info>( +pub fn withdraw_sol_handler<'c: 'info, 'info>( ctx: Context, pool_token_amount: u64, ) -> Result<()> { @@ -318,7 +316,7 @@ pub fn stake_pool_withdraw_sol<'c: 'info, 'info>( #[derive(Accounts)] pub struct StakePoolWithdrawStake<'info> { #[account(mut)] - pub manager: Signer<'info>, + pub signer: Signer<'info>, #[account(mut, has_one = treasury)] pub fund: Box>, @@ -355,20 +353,22 @@ pub struct StakePoolWithdrawStake<'info> { #[account(mut, constraint = pool_token_ata.mint == pool_mint.key())] pub pool_token_ata: Account<'info, TokenAccount>, - pub stake_pool_program: Interface<'info, StakePoolInterface>, - pub clock: Sysvar<'info, Clock>, + pub stake_pool_program: Interface<'info, StakePoolInterface>, pub system_program: Program<'info, System>, pub token_program: Interface<'info, TokenInterface>, pub stake_program: Program<'info, Stake>, } #[access_control( - acl::check_access(&ctx.accounts.fund, &ctx.accounts.manager.key, Permission::Unstake) + acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::Unstake) +)] +#[access_control( + acl::check_stake_pool_integration(&ctx.accounts.fund, &ctx.accounts.stake_pool_program.key) )] #[treasury_signer_seeds] -pub fn stake_pool_withdraw_stake<'c: 'info, 'info>( +pub fn withdraw_stake_handler<'c: 'info, 'info>( ctx: Context, pool_token_amount: u64, stake_account_bump: u8, diff --git a/anchor/programs/glam/src/instructions/wsol.rs b/anchor/programs/glam/src/instructions/wsol.rs index cc45629a..9f18101d 100644 --- a/anchor/programs/glam/src/instructions/wsol.rs +++ b/anchor/programs/glam/src/instructions/wsol.rs @@ -41,7 +41,7 @@ pub struct WSolWrap<'info> { acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::WSolWrap) )] #[treasury_signer_seeds] -pub fn wsol_wrap(ctx: Context, lamports: u64) -> Result<()> { +pub fn wrap_handler(ctx: Context, lamports: u64) -> Result<()> { // Transfer SOL to token account transfer( CpiContext::new_with_signer( @@ -95,7 +95,7 @@ pub struct WSolUnwrap<'info> { acl::check_access(&ctx.accounts.fund, &ctx.accounts.signer.key, Permission::WSolUnwrap) )] #[treasury_signer_seeds] -pub fn wsol_unwrap(ctx: Context) -> Result<()> { +pub fn unwrap_handler(ctx: Context) -> Result<()> { close_account(CpiContext::new_with_signer( ctx.accounts.token_program.to_account_info(), CloseAccount { diff --git a/anchor/programs/glam/src/lib.rs b/anchor/programs/glam/src/lib.rs index e0eff118..04335790 100644 --- a/anchor/programs/glam/src/lib.rs +++ b/anchor/programs/glam/src/lib.rs @@ -22,10 +22,18 @@ pub mod glam { use super::*; - // - // Fund - // - + ////////////////////////////////////////////////////////////////////// + /// Fund + ////////////////////////////////////////////////////////////////////// + + /// Initializes a fund from the provided FundModel instance. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `fund`: An instance of `FundModel` containing the details of the fund to be initialized. + /// + /// # Permission required + /// - Manager only, delegates not allowed pub fn initialize_fund<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, InitializeFund<'info>>, fund: FundModel, @@ -33,6 +41,14 @@ pub mod glam { fund::initialize_fund_handler(ctx, fund) } + /// Updates an existing fund with new parameters. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `fund`: An instance of `FundModel` containing the updated details of the fund. + /// + /// # Permission required + /// - Manager only, delegates not allowed pub fn update_fund<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, UpdateFund<'info>>, fund: FundModel, @@ -40,10 +56,27 @@ pub mod glam { fund::update_fund_handler(ctx, fund) } + /// Closes a fund and releases its resources. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Manager only, delegates not allowed pub fn close_fund(ctx: Context) -> Result<()> { fund::close_fund_handler(ctx) } + /// Enables or disables the subscribe and redeem functionality for the fund. + /// + /// This allows the manager to pause/unpause subscription and redemption of a fund. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `enabled`: A boolean indicating whether to enable or disable the subscribe and redeem functionality. + /// + /// # Permission required + /// - Manager only, delegates not allowed pub fn set_subscribe_redeem_enabled( ctx: Context, enabled: bool, @@ -51,14 +84,43 @@ pub mod glam { fund::set_subscribe_redeem_enabled_handler(ctx, enabled) } + /// Closes token accounts owned by the treasury. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Manager only, delegates not allowed + pub fn close_token_accounts<'info>( + ctx: Context<'_, '_, '_, 'info, CloseTokenAccounts<'info>>, + ) -> Result<()> { + fund::close_token_accounts_handler(ctx) + } + + /// Withdraw an asset from fund treasury into manager's wallet. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `amount`: The amount to withdraw. + /// + /// # Permission required + /// - Manager only, delegates not allowed pub fn withdraw(ctx: Context, amount: u64) -> Result<()> { fund::withdraw(ctx, amount) } - // - // Share class - // + ////////////////////////////////////////////////////////////////////// + /// Share class + ////////////////////////////////////////////////////////////////////// + /// Adds a new share class to a fund. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `share_class_metadata`: An instance of `ShareClassModel` containing the metadata for the new share class. + /// + /// # Permission required + /// - Manager only, delegates not allowed pub fn add_share_class<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, AddShareClass<'info>>, share_class_metadata: ShareClassModel, @@ -66,6 +128,15 @@ pub mod glam { share_class::add_share_class_handler(ctx, share_class_metadata) } + /// Updates an existing share class with new metadata. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `share_class_id`: The id of the share class to be updated. + /// - `share_class_metadata`: An instance of `ShareClassModel` containing the updated metadata for the new share class. + /// + /// # Permission required + /// - Manager only, delegates not allowed pub fn update_share_class( ctx: Context, share_class_id: u8, @@ -74,10 +145,30 @@ pub mod glam { share_class::update_share_class_handler(ctx, share_class_id, share_class_metadata) } + /// Closes a share class and releases its resources. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `share_class_id`: The id of the share class to be closed. + /// + /// # Permission required + /// - Manager only, delegates not allowed pub fn close_share_class(ctx: Context, share_class_id: u8) -> Result<()> { share_class::close_share_class_handler(ctx, share_class_id) } + /// Mints a specified amount of shares for the given share class. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `share_class_id`: The id of the share class to mint shares for. + /// - `amount`: The amount of shares to mint. + /// + /// # Permission required + /// - Permission::MintShare + /// + /// # Integration required + /// - IntegrationName::Mint pub fn mint_share<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, MintShare<'info>>, share_class_id: u8, @@ -86,6 +177,18 @@ pub mod glam { share_class::mint_share_handler(ctx, share_class_id, amount) } + /// Forcefully transfers a specified amount of shares from one account to another. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `share_class_id`: The id of the share class to transfer shares for. + /// - `amount`: The amount of shares to transfer. + /// + /// # Permission required + /// - Permission::ForceTransferShare + /// + /// # Integration required + /// - IntegrationName::Mint pub fn force_transfer_share( ctx: Context, share_class_id: u8, @@ -94,10 +197,34 @@ pub mod glam { share_class::force_transfer_share_handler(ctx, share_class_id, amount) } + /// Burns a specified amount of shares for the given share class. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `share_class_id`: The id of the share class to burn shares for. + /// - `amount`: The amount of shares to burn. + /// + /// # Permission required + /// - Permission::BurnShare + /// + /// # Integration required + /// - IntegrationName::Mint pub fn burn_share(ctx: Context, share_class_id: u8, amount: u64) -> Result<()> { share_class::burn_share_handler(ctx, share_class_id, amount) } + /// Sets the frozen state of the token accounts for the specified share class. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `share_class_id`: The id of the share class to set the frozen state for. + /// - `frozen`: The new frozen state. + /// + /// # Permission required + /// - Permission::SetTokenAccountsStates + /// + /// # Integration required + /// - IntegrationName::Mint pub fn set_token_accounts_states<'info>( ctx: Context<'_, '_, 'info, 'info, SetTokenAccountsStates<'info>>, share_class_id: u8, @@ -106,23 +233,32 @@ pub mod glam { share_class::set_token_accounts_states_handler(ctx, share_class_id, frozen) } - pub fn close_token_accounts<'info>( - ctx: Context<'_, '_, '_, 'info, CloseTokenAccounts<'info>>, - ) -> Result<()> { - share_class::close_token_accounts_handler(ctx) - } - - // - // Investor - // + ////////////////////////////////////////////////////////////////////// + /// Investor + ////////////////////////////////////////////////////////////////////// + /// Subscribes to a specified amount of shares. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `amount`: The amount of shares to subscribe. + /// - `skip_state`: Should always be true (state check to be implemented). pub fn subscribe<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, Subscribe<'info>>, + share_class_id: u8, amount: u64, skip_state: bool, ) -> Result<()> { - investor::subscribe_handler(ctx, amount, skip_state) + investor::subscribe_handler(ctx, share_class_id, amount, skip_state) } + + /// Redeems a specified amount of shares. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `amount`: The amount of shares to redeem. + /// - `in_kind`: Whether to redeem in kind. + /// - `skip_state`: Should always be true (state check to be implemented). pub fn redeem<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, Redeem<'info>>, amount: u64, @@ -132,142 +268,347 @@ pub mod glam { investor::redeem_handler(ctx, amount, in_kind, skip_state) } - // - // Drift - // - + ////////////////////////////////////////////////////////////////////// + /// Drift + ////////////////////////////////////////////////////////////////////// + + /// Initializes a drift account owned by fund treasury and creates a subaccount. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::DriftInitialize + /// + /// # Integration required + /// - IntegrationName::Drift pub fn drift_initialize(ctx: Context) -> Result<()> { - drift::drift_initialize_handler(ctx) - } - + drift::initialize_handler(ctx) + } + + /// Updates custom margin ratio. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `sub_account_id`: Sub account. + /// - `margin_ratio`: Margin ratio. + /// + /// # Permission required + /// - Permission::DriftUpdateUser + /// + /// # Integration required + /// - IntegrationName::Drift pub fn drift_update_user_custom_margin_ratio( ctx: Context, sub_account_id: u16, margin_ratio: u32, ) -> Result<()> { - drift::drift_update_user_custom_margin_ratio_handler(ctx, sub_account_id, margin_ratio) - } - + drift::update_user_custom_margin_ratio_handler(ctx, sub_account_id, margin_ratio) + } + + /// Enables/Disables margin trading. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `sub_account_id`: Sub account. + /// - `margin_trading_enabled`: Whether to enable or disable margin trading. + /// + /// # Permission required + /// - Permission::DriftUpdateUser + /// + /// # Integration required + /// - IntegrationName::Drift pub fn drift_update_user_margin_trading_enabled( ctx: Context, sub_account_id: u16, margin_trading_enabled: bool, ) -> Result<()> { - drift::drift_update_user_margin_trading_enabled_handler( + drift::update_user_margin_trading_enabled_handler( ctx, sub_account_id, margin_trading_enabled, ) } + /// Sets a delegate on the specified sub account. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `sub_account_id`: Sub account. + /// - `delegate`: Delegate's wallet address. + /// + /// # Permission required + /// - Permission::DriftUpdateUser + /// + /// # Integration required + /// - IntegrationName::Drift pub fn drift_update_user_delegate( ctx: Context, sub_account_id: u16, delegate: Pubkey, ) -> Result<()> { - drift::drift_update_user_delegate_handler(ctx, sub_account_id, delegate) - } - + drift::update_user_delegate_handler(ctx, sub_account_id, delegate) + } + + /// Deposits to drift. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `market_index`: Index of the drift spot market. + /// - `amount`: Amount of asset to deposit. + /// + /// # Permission required + /// - Permission::DriftDeposit + /// + /// # Integration required + /// - IntegrationName::Drift pub fn drift_deposit<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftDeposit<'info>>, market_index: u16, amount: u64, ) -> Result<()> { - drift::drift_deposit_handler(ctx, market_index, amount) - } - + drift::deposit_handler(ctx, market_index, amount) + } + + /// Withdraws from drift. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `market_index`: Index of the drift spot market. + /// - `amount`: Amount to withdraw. + /// + /// # Permission required + /// - Permission::DriftWithdraw + /// + /// # Integration required + /// - IntegrationName::Drift pub fn drift_withdraw<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftWithdraw<'info>>, market_index: u16, amount: u64, ) -> Result<()> { - drift::drift_withdraw_handler(ctx, market_index, amount) - } - - pub fn drift_delete_user(ctx: Context, _sub_account_id: u16) -> Result<()> { - drift::drift_delete_user_handler(ctx) - } - + drift::withdraw_handler(ctx, market_index, amount) + } + + /// Deletes a drift user (sub account). + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::DriftDeleteUser + /// + /// # Integration required + /// - IntegrationName::Drift + pub fn drift_delete_user(ctx: Context) -> Result<()> { + drift::delete_user_handler(ctx) + } + + /// Places orders on drift. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `order_params`: A list of orders. + /// + /// # Permissions required + /// - Permission::DriftPlaceOrders + /// - Additional permission Permission::DriftSpotMarket or Permission::DriftPerpMarket is required depending on market type. + /// + /// # Integration required + /// - IntegrationName::Drift pub fn drift_place_orders<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftPlaceOrders<'info>>, order_params: Vec, ) -> Result<()> { - drift::drift_place_orders_handler(ctx, order_params) - } - + drift::place_orders_handler(ctx, order_params) + } + + /// Cancels drift orders. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `market_type`: + /// - `market_index`: + /// - `direction`: + /// + /// # Permission required + /// - Permission::DriftCancelOrders + /// + /// # Integration required + /// - IntegrationName::Drift pub fn drift_cancel_orders<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftCancelOrders<'info>>, market_type: Option, market_index: Option, direction: Option, ) -> Result<()> { - drift::drift_cancel_orders_handler(ctx, market_type, market_index, direction) + drift::cancel_orders_handler(ctx, market_type, market_index, direction) } - // - // Marinade - // + ////////////////////////////////////////////////////////////////////// + /// Marinade + ////////////////////////////////////////////////////////////////////// + /// Deposits SOL to get mSOL. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `lamports`: The amount of SOL to deposit. + /// + /// # Permission required + /// - Permission::Stake + /// + /// # Integration required + /// - IntegrationName::Marinade pub fn marinade_deposit_sol(ctx: Context, lamports: u64) -> Result<()> { - marinade::marinade_deposit_sol(ctx, lamports) - } - + marinade::marinade_deposit_sol_handler(ctx, lamports) + } + + /// Deposits a stake account to get mSOL. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `validator_idx`: Validator index. + /// + /// # Permission required + /// - Permission::Stake + /// + /// # Integration required + /// - IntegrationName::Marinade pub fn marinade_deposit_stake( ctx: Context, validator_idx: u32, ) -> Result<()> { - marinade::marinade_deposit_stake(ctx, validator_idx) + marinade::marinade_deposit_stake_handler(ctx, validator_idx) } + /// Unstakes mSOL to get SOL immediately. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `msol_amount`: Amount of mSOL to unstake. + /// + /// # Permission required + /// - Permission::LiquidUnstake + /// + /// # Integration required + /// - IntegrationName::Marinade pub fn marinade_liquid_unstake( ctx: Context, msol_amount: u64, ) -> Result<()> { - marinade::marinade_liquid_unstake(ctx, msol_amount) - } - + marinade::liquid_unstake_handler(ctx, msol_amount) + } + + /// Unstakes mSOL to get a ticket that can be claimed at the next epoch. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `msol_amount`: Amount of mSOL to unstake. + /// - `ticket_id`: Ticket ID. + /// - `bump`: Bump seed. + /// + /// # Permission required + /// - Permission::Unstake + /// + /// # Integration required + /// - IntegrationName::Marinade pub fn marinade_delayed_unstake( ctx: Context, msol_amount: u64, ticket_id: String, bump: u8, ) -> Result<()> { - marinade::marinade_delayed_unstake(ctx, msol_amount, ticket_id, bump) + marinade::delayed_unstake_handler(ctx, msol_amount, ticket_id, bump) } + /// Claims tickets that were unstaked in the previous epoch to get SOL. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::Unstake + /// + /// # Integration required + /// - IntegrationName::Marinade pub fn marinade_claim_tickets<'info>( ctx: Context<'_, '_, '_, 'info, MarinadeClaimTickets<'info>>, ) -> Result<()> { - marinade::marinade_claim_tickets(ctx) + marinade::claim_tickets_handler(ctx) } - // + ////////////////////////////////////////////////////////////////////// // Stake pool - // - + ////////////////////////////////////////////////////////////////////// + + /// Deposits SOL to a stake pool to get pool token. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `lamports`: The amount of SOL to deposit. + /// + /// # Permission required + /// - Permission::Stake + /// + /// # Integration required + /// - IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used. pub fn stake_pool_deposit_sol(ctx: Context, lamports: u64) -> Result<()> { - stake_pool::stake_pool_deposit_sol(ctx, lamports) - } - - #[doc = "Deposit a stake account into the stake pool and receive pool token"] + stake_pool::deposit_sol_handler(ctx, lamports) + } + + /// Deposits a stake account to a stake pool to get pool token. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::Stake + /// + /// # Integration required + /// - IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used. pub fn stake_pool_deposit_stake(ctx: Context) -> Result<()> { - stake_pool::stake_pool_deposit_stake(ctx) - } - + stake_pool::deposit_stake_handler(ctx) + } + + /// Unstakes from pool token to get SOL immediately. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `pool_token_amount`: Amount of pool token to unstake. + /// + /// # Permission required + /// - Permission::LiquidUnstake + /// + /// # Integration required + /// - IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used. pub fn stake_pool_withdraw_sol( ctx: Context, pool_token_amount: u64, ) -> Result<()> { - stake_pool::stake_pool_withdraw_sol(ctx, pool_token_amount) - } - + stake_pool::withdraw_sol_handler(ctx, pool_token_amount) + } + + /// Unstakes from pool token into a stake account. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `pool_token_amount`: Amount of pool token to unstake. + /// - `stake_account_id`: Stake account ID. + /// - `stake_account_bump`: Stake account bump seed. + /// + /// # Permission required + /// - Permission::Unstake + /// + /// # Integration required + /// - IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used. pub fn stake_pool_withdraw_stake( ctx: Context, pool_token_amount: u64, stake_account_id: String, stake_account_bump: u8, ) -> Result<()> { - stake_pool::stake_pool_withdraw_stake( + stake_pool::withdraw_stake_handler( ctx, pool_token_amount, stake_account_bump, @@ -275,119 +616,323 @@ pub mod glam { ) } - // + ////////////////////////////////////////////////////////////////////// // Native staking - // - + ////////////////////////////////////////////////////////////////////// + + /// Initializes a stake account and delegates it to a validator. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `lamports`: The amount of SOL to initialize the stake account with. + /// - `stake_account_id`: The ID of the stake account to initialize. + /// - `stake_account_bump`: The bump seed for the stake account. + /// + /// # Permission required + /// - Permission::Stake + /// + /// # Integration required + /// - IntegrationName::NativeStaking pub fn initialize_and_delegate_stake<'info>( ctx: Context<'_, '_, '_, 'info, InitializeAndDelegateStake<'info>>, lamports: u64, stake_account_id: String, stake_account_bump: u8, ) -> Result<()> { - stake::initialize_and_delegate_stake(ctx, lamports, stake_account_id, stake_account_bump) + stake::initialize_and_delegate_stake_handler( + ctx, + lamports, + stake_account_id, + stake_account_bump, + ) } + /// Deactivates stake accounts. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::Unstake + /// + /// # Integration required + /// - IntegrationName::NativeStaking pub fn deactivate_stake_accounts<'info>( ctx: Context<'_, '_, '_, 'info, DeactivateStakeAccounts<'info>>, ) -> Result<()> { - stake::deactivate_stake_accounts(ctx) + stake::deactivate_stake_accounts_handler(ctx) } + /// Withdraws SOL from stake accounts. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::Unstake + /// + /// # Integration required + /// - IntegrationName::NativeStaking pub fn withdraw_from_stake_accounts<'info>( ctx: Context<'_, '_, '_, 'info, WithdrawFromStakeAccounts<'info>>, ) -> Result<()> { - stake::withdraw_from_stake_accounts(ctx) + stake::withdraw_from_stake_accounts_handler(ctx) } + /// Merges two stake accounts. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::Stake + /// + /// # Integration required + /// - IntegrationName::NativeStaking pub fn merge_stake_accounts<'info>( ctx: Context<'_, '_, '_, 'info, MergeStakeAccounts<'info>>, ) -> Result<()> { - stake::merge_stake_accounts(ctx) - } - + stake::merge_stake_accounts_handler(ctx) + } + + /// Splits from an existing stake account to get a new stake account. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `lamports`: The amount of SOL to split. + /// - `new_stake_account_id`: The ID of the new stake account. + /// - `new_stake_account_bump`: The bump seed for the new stake account. + /// + /// # Permission required + /// - Permission::Unstake + /// + /// # Integration required + /// - IntegrationName::NativeStaking pub fn split_stake_account<'info>( ctx: Context<'_, '_, '_, 'info, SplitStakeAccount<'info>>, lamports: u64, new_stake_account_id: String, new_stake_account_bump: u8, ) -> Result<()> { - stake::split_stake_account(ctx, lamports, new_stake_account_id, new_stake_account_bump) + stake::split_stake_account_handler( + ctx, + lamports, + new_stake_account_id, + new_stake_account_bump, + ) } + /// Redelegates an existing stake account to a new validator (a new stake account will be created). + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `new_stake_account_id`: The ID of the new stake account. + /// - `new_stake_account_bump`: The bump seed for the new stake account. + /// + /// # Permission required + /// - Permission::Unstake + /// + /// # Integration required + /// - IntegrationName::NativeStaking pub fn redelegate_stake<'info>( ctx: Context<'_, '_, '_, 'info, RedelegateStake<'info>>, new_stake_account_id: String, new_stake_account_bump: u8, ) -> Result<()> { - stake::redelegate_stake(ctx, new_stake_account_id, new_stake_account_bump) - } - - // - // Jupiter - // - + stake::redelegate_stake_handler(ctx, new_stake_account_id, new_stake_account_bump) + } + + ////////////////////////////////////////////////////////////////////// + // Jupiter swap + ////////////////////////////////////////////////////////////////////// + + /// Swaps assets using Jupiter. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `amount`: The amount of asset to swap. + /// - `data`: The data for the swap. + /// + /// # Permission required + /// - Permission::JupiterSwapFundAssets or Permission::JupiterSwapAnyAsset + /// + /// # Integration required + /// - IntegrationName::JupiterSwap pub fn jupiter_swap<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, JupiterSwap<'info>>, amount: u64, data: Vec, ) -> Result<()> { - jupiter::jupiter_swap(ctx, amount, data) + jupiter::swap_handler(ctx, amount, data) } - pub fn init_locked_voter_escrow<'info>(ctx: Context) -> Result<()> { - jupiter::init_locked_voter_escrow(ctx) - } + ////////////////////////////////////////////////////////////////////// + // Jupiter vote + ////////////////////////////////////////////////////////////////////// + /// Initializes a locked voter escrow. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::StakeJup + /// + /// # Integration required + /// - IntegrationName::JupiterVote + pub fn init_locked_voter_escrow<'info>(ctx: Context) -> Result<()> { + jupiter::init_locked_voter_escrow_handler(ctx) + } + + /// Toggles max lock. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `value`: The value to toggle. + /// + /// # Permission required + /// - Permission::UnstakeJup + /// + /// # Integration required + /// - IntegrationName::JupiterVote pub fn toggle_max_lock<'info>(ctx: Context, value: bool) -> Result<()> { - jupiter::toggle_max_lock(ctx, value) - } - + jupiter::toggle_max_lock_handler(ctx, value) + } + + /// Increases the locked amount (aka stakes JUP). + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `amount`: The amount of JUP to stake. + /// + /// # Permission required + /// - Permission::StakeJup + /// + /// # Integration required + /// - IntegrationName::JupiterVote pub fn increase_locked_amount<'info>( ctx: Context, amount: u64, ) -> Result<()> { - jupiter::increase_locked_amount(ctx, amount) - } - + jupiter::increase_locked_amount_handler(ctx, amount) + } + + /// Partially unstakes JUP. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `amount`: The amount of JUP to partially unstake. + /// - `memo`: The memo for the partial unstaking. + /// + /// # Permission required + /// - Permission::UnstakeJup + /// + /// # Integration required + /// - IntegrationName::JupiterVote pub fn open_partial_unstaking<'info>( ctx: Context, amount: u64, memo: String, ) -> Result<()> { - jupiter::open_partial_unstaking(ctx, amount, memo) + jupiter::open_partial_unstaking_handler(ctx, amount, memo) } + /// Merges partial unstaking. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::UnstakeJup + /// + /// # Integration required + /// - IntegrationName::JupiterVote pub fn merge_partial_unstaking<'info>(ctx: Context) -> Result<()> { - jupiter::merge_partial_unstaking(ctx) - } - + jupiter::merge_partial_unstaking_handler(ctx) + } + + /// Withdraws JUP from partial unstaking. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::UnstakeJup + /// + /// # Integration required + /// - IntegrationName::JupiterVote pub fn withdraw_partial_unstaking<'info>(ctx: Context) -> Result<()> { - jupiter::withdraw_partial_unstaking(ctx) - } - + jupiter::withdraw_partial_unstaking_handler(ctx) + } + + /// Withdraws all unstaked JUP. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::UnstakeJup + /// + /// # Integration required + /// - IntegrationName::JupiterVote pub fn withdraw_all_staked_jup<'info>(ctx: Context) -> Result<()> { - jupiter::withdraw_all_staked_jup(ctx) - } - + jupiter::withdraw_all_staked_jup_handler(ctx) + } + + /// Creates a new vote. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::VoteOnProposal + /// + /// # Integration required + /// - IntegrationName::JupiterVote pub fn new_vote<'info>(ctx: Context) -> Result<()> { - jupiter::new_vote(ctx) - } - + jupiter::new_vote_handler(ctx) + } + + /// Casts a vote. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `side`: The side to vote for. + /// + /// # Permission required + /// - Permission::VoteOnProposal + /// + /// # Integration required + /// - IntegrationName::JupiterVote pub fn cast_vote<'info>(ctx: Context, side: u8) -> Result<()> { - jupiter::cast_vote(ctx, side) + jupiter::cast_vote_handler(ctx, side) } - // + ////////////////////////////////////////////////////////////////////// // wSOL - // - + ////////////////////////////////////////////////////////////////////// + + /// Wraps SOL to get wSOL. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// - `lamports`: The amount of SOL to wrap. + /// + /// # Permission required + /// - Permission::WSolWrap pub fn wsol_wrap(ctx: Context, lamports: u64) -> Result<()> { - wsol::wsol_wrap(ctx, lamports) + wsol::wrap_handler(ctx, lamports) } + /// Unwraps all wSOL to get SOL. + /// + /// # Parameters + /// - `ctx`: The context for the transaction. + /// + /// # Permission required + /// - Permission::WSolUnwrap pub fn wsol_unwrap(ctx: Context) -> Result<()> { - wsol::wsol_unwrap(ctx) + wsol::unwrap_handler(ctx) } // diff --git a/anchor/programs/glam/src/state/acl.rs b/anchor/programs/glam/src/state/acl.rs index 13d4bd1b..e829c659 100644 --- a/anchor/programs/glam/src/state/acl.rs +++ b/anchor/programs/glam/src/state/acl.rs @@ -1,14 +1,8 @@ use anchor_lang::prelude::*; use super::FundAccount; - -#[error_code] -pub enum AccessError { - #[msg("Signer is not authorized")] - NotAuthorized, - #[msg("Integration is disabled")] - IntegrationDisabled, -} +use crate::error::AccessError; +use spl_stake_pool::ID as SPL_STAKE_POOL_PROGRAM_ID; /** * Delegate ACL @@ -56,7 +50,7 @@ pub enum IntegrationName { SanctumStakePool, NativeStaking, Marinade, - Jupiter, // Jupiter Swap + JupiterSwap, // Jupiter Swap Mint, // GLAM Mint JupiterVote, // Jupiter Vote } @@ -138,3 +132,12 @@ pub fn check_integration(fund: &FundAccount, integration: IntegrationName) -> Re return Err(AccessError::IntegrationDisabled.into()); } + +pub fn check_stake_pool_integration(fund: &FundAccount, stake_pool_program: &Pubkey) -> Result<()> { + let integration = if stake_pool_program == &SPL_STAKE_POOL_PROGRAM_ID { + IntegrationName::SplStakePool + } else { + IntegrationName::SanctumStakePool + }; + check_integration(fund, integration) +} diff --git a/anchor/src/client/base.ts b/anchor/src/client/base.ts index 985dc857..5af55053 100644 --- a/anchor/src/client/base.ts +++ b/anchor/src/client/base.ts @@ -168,12 +168,8 @@ export class BaseClient { jitoTipLamports?: number; latestBlockhash?: BlockhashWithExpiryBlockHeight; }): Promise { - if (lookupTables === undefined) { - lookupTables = []; - } - if (signer === undefined) { - signer = this.getManager(); - } + lookupTables = lookupTables || []; + signer = signer || this.getSigner(); const instructions = tx.instructions; @@ -335,23 +331,14 @@ export class BaseClient { return publicKey; } - //@deprecated - getManager(): PublicKey { - const managerPublicKey = this.provider?.publicKey; - if (!managerPublicKey) { - throw new Error("Manager public key cannot be retrieved from provider"); - } - return managerPublicKey; - } - - getManagerAta( + getAta( mint: PublicKey, - manager?: PublicKey, + owner?: PublicKey, tokenProgram = TOKEN_PROGRAM_ID, ): PublicKey { return getAssociatedTokenAddressSync( mint, - manager || this.getManager(), + owner || this.getSigner(), true, tokenProgram, ); @@ -364,7 +351,7 @@ export class BaseClient { ).subarray(0, 8), ]; - const manager = this.getManager(); + const manager = this.getSigner(); const [pda, _bump] = PublicKey.findProgramAddressSync( [ anchor.utils.bytes.utf8.encode("fund"), @@ -389,12 +376,7 @@ export class BaseClient { mint: PublicKey, programId?: PublicKey, ): PublicKey { - return getAssociatedTokenAddressSync( - mint, - this.getTreasuryPDA(fundPDA), - true, - programId, - ); + return this.getAta(mint, this.getTreasuryPDA(fundPDA), programId); } /** @@ -482,12 +464,7 @@ export class BaseClient { } getShareClassAta(user: PublicKey, shareClassPDA: PublicKey): PublicKey { - return getAssociatedTokenAddressSync( - shareClassPDA, - user, - true, - TOKEN_2022_PROGRAM_ID, - ); + return this.getAta(shareClassPDA, user, TOKEN_2022_PROGRAM_ID); } getFundName(fundModel: Partial) { diff --git a/anchor/src/client/drift.ts b/anchor/src/client/drift.ts index 64a8243c..50468b9a 100644 --- a/anchor/src/client/drift.ts +++ b/anchor/src/client/drift.ts @@ -30,11 +30,10 @@ export interface PerpMarketConfig { categories: string[]; symbol: string; baseAsset: string; - decimals: number; marketIndex: number; launchTs: string; oracle: string; - oraceSource: string; + oracleSource: string; pythPullOraclePDA: string; pythFeedId: string; marketPDA: string; @@ -74,7 +73,7 @@ export interface GlamDriftUser { } const DRIFT_VAULT = new PublicKey( - "JCNCMFXo5M5qwUPg2Utu1u6YWp3MbygxqBsBeXXJfrw" + "JCNCMFXo5M5qwUPg2Utu1u6YWp3MbygxqBsBeXXJfrw", ); const DRIFT_MARGIN_PRECISION = 10_000; @@ -93,12 +92,12 @@ export class DriftClient { public async updateUserCustomMarginRatio( fund: PublicKey, maxLeverage: number, // 1=1x, 2=2x ... 50=50x leverage - subAccountId: number = 0 + subAccountId: number = 0, ): Promise { const tx = await this.updateUserCustomMarginRatioTx( fund, maxLeverage, - subAccountId + subAccountId, ); return await this.base.sendAndConfirm(tx); } @@ -106,12 +105,12 @@ export class DriftClient { public async updateUserMarginTradingEnabled( fund: PublicKey, marginTradingEnabled: boolean, - subAccountId: number = 0 + subAccountId: number = 0, ): Promise { const tx = await this.updateUserMarginTradingEnabledTx( fund, marginTradingEnabled, - subAccountId + subAccountId, ); return await this.base.sendAndConfirm(tx); } @@ -119,7 +118,7 @@ export class DriftClient { public async updateUserDelegate( fund: PublicKey, delegate: PublicKey, - subAccountId: number = 0 + subAccountId: number = 0, ): Promise { const tx = await this.updateUserDelegateTx(fund, delegate, subAccountId); return await this.base.sendAndConfirm(tx); @@ -131,7 +130,7 @@ export class DriftClient { marketIndex: number = 1, subAccountId: number = 0, marketConfigs: DriftMarketConfigs, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { const tx = await this.depositTx( fund, @@ -139,7 +138,7 @@ export class DriftClient { marketIndex, subAccountId, marketConfigs, - txOptions + txOptions, ); return await this.base.sendAndConfirm(tx); } @@ -150,7 +149,7 @@ export class DriftClient { marketIndex: number = 1, subAccountId: number = 0, marketConfigs: DriftMarketConfigs, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { const tx = await this.withdrawTx( fund, @@ -158,7 +157,7 @@ export class DriftClient { marketIndex, subAccountId, marketConfigs, - txOptions + txOptions, ); return await this.base.sendAndConfirm(tx); } @@ -168,14 +167,14 @@ export class DriftClient { orderParams: OrderParams, subAccountId: number = 0, marketConfigs: DriftMarketConfigs, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { const tx = await this.placeOrderTx( fund, orderParams, subAccountId, marketConfigs, - txOptions + txOptions, ); return await this.base.sendAndConfirm(tx); } @@ -187,7 +186,7 @@ export class DriftClient { direction: PositionDirection, subAccountId: number = 0, marketConfigs: DriftMarketConfigs, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { const tx = await this.cancelOrdersTx( fund, @@ -196,7 +195,7 @@ export class DriftClient { direction, subAccountId, marketConfigs, - txOptions + txOptions, ); return await this.base.sendAndConfirm(tx); } @@ -218,7 +217,7 @@ export class DriftClient { async getPositions(fund: PublicKey, subAccountId: number = 0) { const treasury = this.base.getTreasuryPDA(fund); const response = await fetch( - `https://api.glam.systems/v0/drift/user?authority=${treasury.toBase58()}&accountId=0` + `https://api.glam.systems/v0/drift/user?authority=${treasury.toBase58()}&accountId=${subAccountId}`, ); const data = await response.json(); const { spotPositions, perpPositions } = data as GlamDriftUser; @@ -233,7 +232,7 @@ export class DriftClient { const connection = this.base.provider.connection; const info = await connection.getAccountInfo( driftUserAddress, - connection.commitment + connection.commitment, ); if (info) { driftUserAccount = decodeUser(info.data); @@ -264,11 +263,11 @@ export class DriftClient { subAccountId: number, marketType: MarketType, marketIndex: number, - marketConfigs: DriftMarketConfigs + marketConfigs: DriftMarketConfigs, ): Promise { const { spotPositions, perpPositions } = await this.getPositions( fund, - subAccountId + subAccountId, ); const spotMarketIndexes = spotPositions.map((p) => p.marketIndex); const perpMarketIndexes = perpPositions.map((p) => p.marketIndex); @@ -286,6 +285,9 @@ export class DriftClient { break; } + console.log("spotMarketIndexes:", spotMarketIndexes); + console.log("perpMarketIndexes:", perpMarketIndexes); + const oracles = spotMarketIndexes .map((i) => marketConfigs.spot[i].oracle) .concat(perpMarketIndexes.map((i) => marketConfigs.perp[i].oracle)); @@ -293,6 +295,8 @@ export class DriftClient { .map((i) => marketConfigs.spot[i].marketPDA) .concat(perpMarketIndexes.map((i) => marketConfigs.perp[i].marketPDA)); + console.log("oracles:", oracles); + return oracles .map((o) => ({ pubkey: new PublicKey(o), @@ -304,7 +308,7 @@ export class DriftClient { pubkey: new PublicKey(m), isWritable: true, isSigner: false, - })) + })), ); } @@ -314,9 +318,9 @@ export class DriftClient { public async initializeTx( fund: PublicKey, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const [user, userStats] = this.getUser(fund); const state = await getDriftStateAccountPublicKey(this.DRIFT_PROGRAM); @@ -329,7 +333,7 @@ export class DriftClient { user, userStats, state, - manager, + signer, }) .transaction(); @@ -343,9 +347,9 @@ export class DriftClient { fund: PublicKey, maxLeverage: number, // 1=1x, 2=2x ... 50=50x leverage subAccountId: number = 0, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const [user] = this.getUser(fund, subAccountId); // https://github.com/drift-labs/protocol-v2/blob/babed162b08b1fe34e49a81c5aa3e4ec0a88ecdf/programs/drift/src/math/constants.rs#L183-L184 @@ -353,10 +357,10 @@ export class DriftClient { const tx = await this.base.program.methods .driftUpdateUserCustomMarginRatio(subAccountId, marginRatio) - .accounts({ + .accountsPartial({ fund, user, - manager, + signer, }) .transaction(); @@ -370,17 +374,17 @@ export class DriftClient { fund: PublicKey, marginTradingEnabled: boolean, subAccountId: number = 0, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const [user] = this.getUser(fund, subAccountId); const tx = await this.base.program.methods .driftUpdateUserMarginTradingEnabled(subAccountId, marginTradingEnabled) - .accounts({ + .accountsPartial({ fund, user, - manager, + signer, }) .transaction(); @@ -394,17 +398,17 @@ export class DriftClient { fund: PublicKey, delegate: PublicKey, subAccountId: number = 0, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const [user] = this.getUser(fund, subAccountId); const tx = await this.base.program.methods .driftUpdateUserDelegate(subAccountId, delegate) - .accounts({ + .accountsPartial({ fund, user, - manager, + signer, }) .transaction(); @@ -420,9 +424,9 @@ export class DriftClient { marketIndex: number = 1, subAccountId: number = 0, marketConfigs: DriftMarketConfigs, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const [user, userStats] = this.getUser(fund, subAccountId); const state = await getDriftStateAccountPublicKey(this.DRIFT_PROGRAM); @@ -431,14 +435,14 @@ export class DriftClient { const tx = await this.base.program.methods .driftDeposit(marketIndex, amount) - .accounts({ + .accountsPartial({ fund, treasuryAta: this.base.getTreasuryAta(fund, new PublicKey(mint)), driftAta: new PublicKey(vaultPDA), user, userStats, state, - manager, + signer, }) .remainingAccounts([ { pubkey: new PublicKey(oracle), isSigner: false, isWritable: false }, @@ -458,9 +462,9 @@ export class DriftClient { marketIndex: number = 1, subAccountId: number = 0, marketConfigs: DriftMarketConfigs, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const [user, userStats] = this.getUser(fund, subAccountId); const state = await getDriftStateAccountPublicKey(this.DRIFT_PROGRAM); @@ -470,19 +474,19 @@ export class DriftClient { subAccountId, MarketType.SPOT, marketIndex, - marketConfigs + marketConfigs, ); const tx = await this.base.program.methods .driftWithdraw(marketIndex, amount) - .accounts({ + .accountsPartial({ fund, treasuryAta: this.base.getTreasuryAta(fund, new PublicKey(mint)), driftAta: new PublicKey(vaultPDA), user, userStats, state, - manager, + signer, driftSigner: DRIFT_VAULT, }) .remainingAccounts(remainingAccounts) @@ -499,7 +503,7 @@ export class DriftClient { orderParams: OrderParams, subAccountId: number = 0, marketConfigs: DriftMarketConfigs, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { const { marketIndex, marketType } = orderParams; const remainingAccounts = await this.composeRemainingAccounts( @@ -507,22 +511,20 @@ export class DriftClient { subAccountId, marketType, marketIndex, - marketConfigs + marketConfigs, ); - console.log("remainingAccounts", remainingAccounts); - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const [user] = this.getUser(fund, subAccountId); const state = await getDriftStateAccountPublicKey(this.DRIFT_PROGRAM); const tx = await this.base.program.methods - //@ts-ignore .driftPlaceOrders([orderParams]) - .accounts({ + .accountsPartial({ fund, user, state, - manager, + signer, }) .remainingAccounts(remainingAccounts) .transaction(); @@ -540,9 +542,9 @@ export class DriftClient { direction: PositionDirection, subAccountId: number = 0, marketConfigs: DriftMarketConfigs, - txOptions: TxOptions = {} + txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const [user] = this.getUser(fund, subAccountId); const state = await getDriftStateAccountPublicKey(this.DRIFT_PROGRAM); @@ -551,17 +553,16 @@ export class DriftClient { subAccountId, marketType, marketIndex, - marketConfigs + marketConfigs, ); const tx = await this.base.program.methods - //@ts-ignore .driftCancelOrders(marketType, marketIndex, direction) - .accounts({ + .accountsPartial({ fund, user, state, - manager, + signer, }) .remainingAccounts(remainingAccounts) .transaction(); diff --git a/anchor/src/client/fund.ts b/anchor/src/client/fund.ts index 92532e09..e6eb2c92 100644 --- a/anchor/src/client/fund.ts +++ b/anchor/src/client/fund.ts @@ -34,12 +34,9 @@ export class FundClient { ): Promise<[TransactionSignature, PublicKey]> { let fundModel = this.enrichFundModel(partialFundModel); - console.log("Enriched fund model", fundModel); - const fundPDA = this.base.getFundPDA(fundModel); const treasury = this.base.getTreasuryPDA(fundPDA); const openfunds = this.base.getOpenfundsPDA(fundPDA); - const manager = this.base.getManager(); const shareClasses = fundModel.shareClasses; fundModel.shareClasses = []; @@ -57,7 +54,6 @@ export class FundClient { fund: fundPDA, treasury, openfunds, - manager, }) .rpc(); return [txSig, fundPDA]; @@ -71,7 +67,6 @@ export class FundClient { fund: fundPDA, treasury, openfunds, - manager, }) .instruction(); @@ -95,7 +90,6 @@ export class FundClient { fund: fundPDA, treasury, openfunds, - manager, }) .rpc(); @@ -123,12 +117,12 @@ export class FundClient { public async updateFund( fundPDA: PublicKey, updated: Partial, + txOptions: TxOptions = {}, ): Promise { return await this.base.program.methods .updateFund(new FundModel(updated)) .accounts({ fund: fundPDA, - signer: this.base.getManager(), }) .rpc(); } @@ -152,7 +146,7 @@ export class FundClient { */ enrichFundModel(partialFundModel: Partial): FundModel { const fundModel = { ...partialFundModel }; - const manager = this.base.getManager(); + const manager = this.base.getSigner(); const defaultDate = new Date().toISOString().split("T")[0]; // createdKey = hash fund name and get first 8 bytes @@ -248,31 +242,21 @@ export class FundClient { public async deleteDelegateAcls( fundPDA: PublicKey, delegates: PublicKey[], + txOptions: TxOptions = {}, ): Promise { - let updatedFund = new FundModel({ + const updatedFund = new FundModel({ delegateAcls: delegates.map((pubkey) => ({ pubkey, permissions: [] })), }); - return await this.base.program.methods - .updateFund(updatedFund) - .accounts({ - fund: fundPDA, - signer: this.base.getManager(), - }) - .rpc(); + return await this.updateFund(fundPDA, updatedFund, txOptions); } public async upsertDelegateAcls( fundPDA: PublicKey, delegateAcls: DelegateAcl[], + txOptions: TxOptions = {}, ): Promise { - let updatedFund = new FundModel({ delegateAcls }); - return await this.base.program.methods - .updateFund(updatedFund) - .accounts({ - fund: fundPDA, - signer: this.base.getManager(), - }) - .rpc(); + const updatedFund = new FundModel({ delegateAcls }); + return await this.updateFund(fundPDA, updatedFund, txOptions); } public async setSubscribeRedeemEnabled( @@ -364,28 +348,27 @@ export class FundClient { amount: number | BN, txOptions: TxOptions, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const { mint, tokenProgram } = await this.fetchMintWithOwner(asset); - const managerAta = this.base.getManagerAta(asset, manager, tokenProgram); + const signerAta = this.base.getAta(asset, signer, tokenProgram); const treasuryAta = this.base.getTreasuryAta(fund, asset, tokenProgram); - // @ts-ignore const tx = new Transaction().add( createAssociatedTokenAccountIdempotentInstruction( - manager, + signer, treasuryAta, treasury, asset, tokenProgram, ), createTransferCheckedInstruction( - managerAta, + signerAta, asset, treasuryAta, - manager, + signer, new BN(amount).toNumber(), mint.decimals, [], @@ -402,9 +385,9 @@ export class FundClient { amount: number | BN, txOptions: TxOptions, ): Promise { - const manager = txOptions.signer || this.base.getManager(); - const { mint, tokenProgram } = await this.fetchMintWithOwner(asset); - const managerAta = this.base.getManagerAta(asset, manager, tokenProgram); + const signer = txOptions.signer || this.base.getSigner(); + const { tokenProgram } = await this.fetchMintWithOwner(asset); + const signerAta = this.base.getAta(asset, signer, tokenProgram); // @ts-ignore const tx = await this.base.program.methods @@ -412,15 +395,13 @@ export class FundClient { .accounts({ fund, asset, - //@ts-ignore - manager, tokenProgram, }) .preInstructions([ createAssociatedTokenAccountIdempotentInstruction( - manager, - managerAta, - manager, + signer, + signerAta, + signer, asset, tokenProgram, ), diff --git a/anchor/src/client/investor.ts b/anchor/src/client/investor.ts index 7bd4a74c..3a725ff5 100644 --- a/anchor/src/client/investor.ts +++ b/anchor/src/client/investor.ts @@ -173,7 +173,6 @@ export class InvestorClient { } catch (err) { // ignore } - // const solBalance = new BN(String(await connection.getBalance(signer))); const delta = amount.sub(wsolBalance); if (delta.gt(new BN(0))) { preInstructions = preInstructions.concat([ @@ -189,7 +188,7 @@ export class InvestorClient { // @ts-ignore const tx = await this.base.program.methods - .subscribe(amount, skipState) + .subscribe(0, amount, skipState) .accounts({ fund, shareClass, diff --git a/anchor/src/client/jupiter.ts b/anchor/src/client/jupiter.ts index b6dc2a58..dc5fe6f5 100644 --- a/anchor/src/client/jupiter.ts +++ b/anchor/src/client/jupiter.ts @@ -140,7 +140,7 @@ export class JupiterClient { } preInstructions.push( createAssociatedTokenAccountIdempotentInstruction( - this.base.getManager(), + this.base.getSigner(), escrowJupAta, escrow, JUP, @@ -230,7 +230,7 @@ export class JupiterClient { swapInstructions?: SwapInstructions, txOptions: TxOptions = {}, ): Promise { - const signer = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); let swapInstruction: InstructionFromJupiter; let addressLookupTableAddresses: string[]; @@ -298,12 +298,8 @@ export class JupiterClient { outputMint, outputTokenProgram, ), - inputSignerAta: this.base.getManagerAta( - inputMint, - signer, - inputTokenProgram, - ), - outputSignerAta: this.base.getManagerAta( + inputSignerAta: this.base.getAta(inputMint, signer, inputTokenProgram), + outputSignerAta: this.base.getAta( outputMint, signer, outputTokenProgram, @@ -344,14 +340,14 @@ export class JupiterClient { const ataParams = [ { payer: manager, - ata: this.base.getManagerAta(inputMint, manager, inputTokenProgram), + ata: this.base.getAta(inputMint, manager, inputTokenProgram), owner: manager, mint: inputMint, tokenProgram: inputTokenProgram, }, { payer: manager, - ata: this.base.getManagerAta(outputMint, manager, outputTokenProgram), + ata: this.base.getAta(outputMint, manager, outputTokenProgram), owner: manager, mint: outputMint, tokenProgram: outputTokenProgram, diff --git a/anchor/src/client/marinade.ts b/anchor/src/client/marinade.ts index 58faa7f1..b79eba74 100644 --- a/anchor/src/client/marinade.ts +++ b/anchor/src/client/marinade.ts @@ -26,7 +26,7 @@ export class MarinadeClient { public async depositSol( fund: PublicKey, - amount: BN + amount: BN, ): Promise { const tx = await this.depositSolTx(fund, amount, {}); return await this.base.sendAndConfirm(tx); @@ -34,7 +34,7 @@ export class MarinadeClient { public async depositStake( fund: PublicKey, - stakeAccount: PublicKey + stakeAccount: PublicKey, ): Promise { const tx = await this.depositStakeTx(fund, stakeAccount, {}); return await this.base.sendAndConfirm(tx); @@ -42,7 +42,7 @@ export class MarinadeClient { public async liquidUnstake( fund: PublicKey, - amount: BN + amount: BN, ): Promise { const tx = await this.liquidUnstakeTx(fund, amount, {}); return await this.base.sendAndConfirm(tx); @@ -50,7 +50,7 @@ export class MarinadeClient { public async delayedUnstake( fund: PublicKey, - amount: BN + amount: BN, ): Promise { const tx = await this.delayedUnstakeTx(fund, amount, {}); return await this.base.sendAndConfirm(tx); @@ -58,7 +58,7 @@ export class MarinadeClient { public async claimTickets( fund: PublicKey, - tickets: PublicKey[] + tickets: PublicKey[], ): Promise { const tx = await this.claimTicketsTx(fund, tickets, {}); return await this.base.sendAndConfirm(tx); @@ -70,11 +70,11 @@ export class MarinadeClient { getMarinadeTicketPDA( fundPDA: PublicKey, - ticketId: string + ticketId: string, ): [PublicKey, number] { return PublicKey.findProgramAddressSync( [Buffer.from("ticket"), Buffer.from(ticketId), fundPDA.toBuffer()], - this.base.programId + this.base.programId, ); } @@ -94,7 +94,7 @@ export class MarinadeClient { }, }, ], - } + }, ); return accounts.map((a) => a.pubkey); } @@ -128,13 +128,13 @@ export class MarinadeClient { }, }, ], - } + }, ); const currentEpoch = await this.base.provider.connection.getEpochInfo(); return accounts.map((a) => { const lamports = Number((a.account.data as Buffer).readBigInt64LE(72)); const createdEpoch = Number( - (a.account.data as Buffer).readBigInt64LE(80) + (a.account.data as Buffer).readBigInt64LE(80), ); return { address: a.pubkey, @@ -151,21 +151,21 @@ export class MarinadeClient { // TODO: use marinade.getMarinadeState(); ? return { marinadeStateAddress: new PublicKey( - "8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC" + "8szGkuLTAux9XMgZ2vtY39jVSowEcpBfFfD8hXSEqdGC", ), msolMintAddress: MSOL, treasuryMsolAccount: new PublicKey( - "B1aLzaNMeFVAyQ6f3XbbUyKcH2YPHu2fqiEagmiF23VR" + "B1aLzaNMeFVAyQ6f3XbbUyKcH2YPHu2fqiEagmiF23VR", ), reserveAddress: new PublicKey( - "Du3Ysj1wKbxPKkuPPnvzQLQh8oMSVifs3jGZjJWXFmHN" + "Du3Ysj1wKbxPKkuPPnvzQLQh8oMSVifs3jGZjJWXFmHN", ), mSolMintAuthority: new PublicKey( - "3JLPCS1qM2zRw3Dp6V4hZnYHd4toMNPkNesXdX9tg6KM" + "3JLPCS1qM2zRw3Dp6V4hZnYHd4toMNPkNesXdX9tg6KM", ), msolLeg: new PublicKey("7GgPYjS5Dza89wV6FpZ23kUJRG5vbQ1GM25ezspYFSoE"), msolLegAuthority: new PublicKey( - "EyaSjUtSgo9aRD1f8LWXwdvkpDTmXAW54yoSHZRF14WL" + "EyaSjUtSgo9aRD1f8LWXwdvkpDTmXAW54yoSHZRF14WL", ), solLeg: new PublicKey("UefNb6z6yvArqe4cJHTXCqStRsKmWhGxnZzuHbikP5Q"), }; @@ -176,7 +176,7 @@ export class MarinadeClient { await this.base.provider.connection.getParsedAccountInfo(stakeAccount); if (!stakeAccountInfo) { throw new Error( - `Failed to find the stake account ${stakeAccount.toBase58()}` + `Failed to find the stake account ${stakeAccount.toBase58()}`, ); } @@ -184,7 +184,7 @@ export class MarinadeClient { throw new Error( `${stakeAccount.toBase58()} is not a stake account because owner is ${ stakeAccountInfo.owner - }` + }`, ); } @@ -197,7 +197,7 @@ export class MarinadeClient { throw new Error( `${stakeAccount.toBase58()} is not a stake account because space is ${ parsedData.space - } != 200` + } != 200`, ); } @@ -215,25 +215,24 @@ export class MarinadeClient { public async depositSolTx( fund: PublicKey, amount: BN, - txOptions: TxOptions + txOptions: TxOptions, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const marinadeState = this.getMarinadeState(); const treasuryMsolAta = getAssociatedTokenAddressSync( marinadeState.msolMintAddress, treasury, - true + true, ); // @ts-ignore const tx = await this.base.program.methods .marinadeDepositSol(amount) - .accounts({ + .accountsPartial({ fund, - //@ts-ignore IDL ts type is unhappy treasury, - manager, + signer, reservePda: marinadeState.reserveAddress, marinadeState: marinadeState.marinadeStateAddress, msolMint: marinadeState.msolMintAddress, @@ -255,9 +254,9 @@ export class MarinadeClient { public async depositStakeTx( fund: PublicKey, stakeAccount: PublicKey, - txOptions: TxOptions + txOptions: TxOptions, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const stakeAccountInfo = await this.getParsedStakeAccountInfo(stakeAccount); @@ -266,7 +265,7 @@ export class MarinadeClient { const marinadeState = await new Marinade().getMarinadeState(); const { validatorRecords } = await marinadeState.getValidatorRecords(); const validatorLookupIndex = validatorRecords.findIndex( - ({ validatorAccount }) => validatorAccount.equals(stakeAccountInfo.voter) + ({ validatorAccount }) => validatorAccount.equals(stakeAccountInfo.voter), ); const validatorIndex = validatorLookupIndex === -1 @@ -274,16 +273,15 @@ export class MarinadeClient { : validatorLookupIndex; const duplicationFlag = await marinadeState.validatorDuplicationFlag( - stakeAccountInfo.voter + stakeAccountInfo.voter, ); const tx = await this.base.program.methods .marinadeDepositStake(validatorIndex) - .accounts({ + .accountsPartial({ fund, - //@ts-ignore IDL ts type is unhappy treasury, - manager, + signer, marinadeState: marinadeState.marinadeStateAddress, validatorList: marinadeState.state.validatorSystem.validatorList.account, @@ -310,27 +308,26 @@ export class MarinadeClient { public async delayedUnstakeTx( fund: PublicKey, amount: BN, - txOptions: TxOptions + txOptions: TxOptions, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const ticketId = Date.now().toString(); const [ticket, bump] = this.getMarinadeTicketPDA(fund, ticketId); const treasury = this.base.getTreasuryPDA(fund); const marinadeState = this.getMarinadeState(); const treasuryMsolAta = this.base.getTreasuryAta( fund, - marinadeState.msolMintAddress + marinadeState.msolMintAddress, ); console.log(`Ticket ${ticketId}`, ticket.toBase58()); const tx = await this.base.program.methods .marinadeDelayedUnstake(amount, ticketId, bump) - .accounts({ + .accountsPartial({ fund, - //@ts-ignore IDL ts type is unhappy treasury, - manager, + signer, ticket, msolMint: marinadeState.msolMintAddress, burnMsolFrom: treasuryMsolAta, @@ -349,25 +346,24 @@ export class MarinadeClient { public async claimTicketsTx( fund: PublicKey, tickets: PublicKey[], - txOptions: TxOptions + txOptions: TxOptions, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const marinadeState = this.getMarinadeState(); const tx = await this.base.program.methods .marinadeClaimTickets() - .accounts({ + .accountsPartial({ fund, - //@ts-ignore IDL ts type is unhappy treasury, - manager, + signer, marinadeState: marinadeState.marinadeStateAddress, reservePda: marinadeState.reserveAddress, marinadeProgram: MARINADE_PROGRAM_ID, }) .remainingAccounts( - tickets.map((t) => ({ pubkey: t, isSigner: false, isWritable: true })) + tickets.map((t) => ({ pubkey: t, isSigner: false, isWritable: true })), ) .transaction(); @@ -380,24 +376,23 @@ export class MarinadeClient { public async liquidUnstakeTx( fund: PublicKey, amount: BN, - txOptions: TxOptions + txOptions: TxOptions, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const marinadeState = this.getMarinadeState(); const treasuryMsolAta = getAssociatedTokenAddressSync( marinadeState.msolMintAddress, treasury, - true + true, ); const tx = await this.base.program.methods .marinadeLiquidUnstake(amount) - .accounts({ + .accountsPartial({ fund, - //@ts-ignore IDL ts type is unhappy treasury, - manager, + signer, marinadeState: marinadeState.marinadeStateAddress, msolMint: marinadeState.msolMintAddress, liqPoolSolLegPda: marinadeState.solLeg, diff --git a/anchor/src/client/shareclass.ts b/anchor/src/client/shareclass.ts index e387d30c..d74d1e28 100644 --- a/anchor/src/client/shareclass.ts +++ b/anchor/src/client/shareclass.ts @@ -90,7 +90,7 @@ export class ShareClassClient { const shareClassMint = this.base.getShareClassPDA(fundPDA, shareClassId); const ata = this.base.getShareClassAta(owner, shareClassMint); const ixCreateAta = createAssociatedTokenAccountIdempotentInstruction( - this.base.getManager(), + this.base.getSigner(), ata, owner, shareClassMint, @@ -164,7 +164,7 @@ export class ShareClassClient { const preInstructions = []; preInstructions.push( createAssociatedTokenAccountIdempotentInstruction( - this.base.getManager(), + this.base.getSigner(), mintTo, recipient, shareClassMint, @@ -251,7 +251,7 @@ export class ShareClassClient { const preInstructions = []; preInstructions.push( createAssociatedTokenAccountIdempotentInstruction( - this.base.getManager(), + this.base.getSigner(), toAta, to, shareClassMint, diff --git a/anchor/src/client/staking.ts b/anchor/src/client/staking.ts index 9c30c837..e2577f0b 100644 --- a/anchor/src/client/staking.ts +++ b/anchor/src/client/staking.ts @@ -334,7 +334,7 @@ export class StakingClient { amount: BN, txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const { programId: stakePoolProgram, @@ -350,10 +350,9 @@ export class StakingClient { // @ts-ignore const tx = await this.base.program.methods .stakePoolDepositSol(amount) - .accounts({ - manager, + .accountsPartial({ + signer, fund, - //@ts-ignore IDL ts type is unhappy treasury, mintTo: this.base.getTreasuryAta(fund, poolMint), stakePoolProgram, @@ -378,7 +377,7 @@ export class StakingClient { stakeAccount: PublicKey, txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const { programId: stakePoolProgram, @@ -396,10 +395,9 @@ export class StakingClient { const tx = await this.base.program.methods .stakePoolDepositStake() - .accounts({ - manager, + .accountsPartial({ + signer, fund, - //@ts-ignore IDL ts type is unhappy treasury, treasuryStakeAccount: stakeAccount, mintTo: this.base.getTreasuryAta(fund, poolMint), @@ -431,7 +429,7 @@ export class StakingClient { deactivate: boolean = false, txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const { programId: stakePoolProgram, @@ -456,7 +454,7 @@ export class StakingClient { await this.base.program.methods .deactivateStakeAccounts() .accountsPartial({ - manager, + signer, fund, treasury, clock: SYSVAR_CLOCK_PUBKEY, @@ -476,7 +474,7 @@ export class StakingClient { const tx = await this.base.program.methods .stakePoolWithdrawStake(amount, stakeAccountId, bump) .accountsPartial({ - manager, + signer, fund, treasury, treasuryStakeAccount: stakeAccountPda, @@ -506,7 +504,7 @@ export class StakingClient { amount: BN, txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const stakeAccountId = Date.now().toString(); const [stakeAccountPda, bump] = this.getStakeAccountPDA( @@ -515,10 +513,9 @@ export class StakingClient { ); const tx = await this.base.program.methods .initializeAndDelegateStake(amount, stakeAccountId, bump) - .accounts({ - manager, + .accountsPartial({ + signer, fund, - //@ts-ignore IDL ts type is unhappy treasury, treasuryStakeAccount: stakeAccountPda, vote, @@ -539,14 +536,13 @@ export class StakingClient { stakeAccounts: PublicKey[], txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const tx = await this.base.program.methods .deactivateStakeAccounts() - .accounts({ - manager, + .accountsPartial({ + signer, fund, - //@ts-ignore IDL ts type is unhappy treasury, clock: SYSVAR_CLOCK_PUBKEY, stakeProgram: StakeProgram.programId, @@ -570,14 +566,13 @@ export class StakingClient { stakeAccounts: PublicKey[], txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const tx = await this.base.program.methods .withdrawFromStakeAccounts() - .accounts({ - manager, + .accountsPartial({ + signer, fund, - //@ts-ignore IDL ts type is unhappy treasury, clock: SYSVAR_CLOCK_PUBKEY, stakeHistory: SYSVAR_STAKE_HISTORY_PUBKEY, @@ -603,14 +598,13 @@ export class StakingClient { fromStake: PublicKey, txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const tx = await this.base.program.methods .mergeStakeAccounts() - .accounts({ - manager, + .accountsPartial({ + signer, fund, - //@ts-ignore IDL ts type is unhappy treasury, toStake, fromStake, @@ -633,15 +627,14 @@ export class StakingClient { newStakeAccountBump: number, txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const tx = await this.base.program.methods .splitStakeAccount(lamports, newStakeAccountId, newStakeAccountBump) - .accounts({ - manager, + .accountsPartial({ + signer, fund, - //@ts-ignore IDL ts type is unhappy treasury, existingStake, newStake, @@ -663,15 +656,14 @@ export class StakingClient { newStakeAccountBump: number, txOptions: TxOptions = {}, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const tx = await this.base.program.methods .redelegateStake(newStakeAccountId, newStakeAccountBump) - .accounts({ - manager, + .accountsPartial({ + signer, fund, - //@ts-ignore IDL ts type is unhappy treasury, vote, existingStake, diff --git a/anchor/src/client/wsol.ts b/anchor/src/client/wsol.ts index 48c65892..4dc9e9aa 100644 --- a/anchor/src/client/wsol.ts +++ b/anchor/src/client/wsol.ts @@ -18,7 +18,7 @@ export class WSolClient { public async wrap( fund: PublicKey, amount: BN, - txOptions: TxOptions = {} as TxOptions + txOptions: TxOptions = {} as TxOptions, ): Promise { const tx = await this.wrapTx(fund, amount, txOptions); return await this.base.sendAndConfirm(tx); @@ -26,7 +26,7 @@ export class WSolClient { public async unwrap( fund: PublicKey, - txOptions: TxOptions = {} as TxOptions + txOptions: TxOptions = {} as TxOptions, ): Promise { const tx = await this.unwrapTx(fund, txOptions); return await this.base.sendAndConfirm(tx); @@ -39,22 +39,21 @@ export class WSolClient { public async wrapTx( fund: PublicKey, amount: BN, - txOptions: TxOptions + txOptions: TxOptions, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const treasuryWsolAta = this.base.getTreasuryAta(fund, WSOL); // @ts-ignore const tx = await this.base.program.methods .wsolWrap(amount) - .accounts({ + .accountsPartial({ fund, - //@ts-ignore IDL ts type is unhappy treasury, treasuryWsolAta, wsolMint: WSOL, - signer: manager, + signer, }) .transaction(); @@ -66,21 +65,20 @@ export class WSolClient { public async unwrapTx( fund: PublicKey, - txOptions: TxOptions + txOptions: TxOptions, ): Promise { - const manager = txOptions.signer || this.base.getManager(); + const signer = txOptions.signer || this.base.getSigner(); const treasury = this.base.getTreasuryPDA(fund); const treasuryWsolAta = this.base.getTreasuryAta(fund, WSOL); const tx = await this.base.program.methods .wsolUnwrap() - .accounts({ + .accountsPartial({ fund, - //@ts-ignore IDL ts type is unhappy treasury, treasuryWsolAta, wsolMint: WSOL, - signer: manager, + signer, }) .transaction(); diff --git a/anchor/src/models.ts b/anchor/src/models.ts index 96817414..9beb4c6a 100644 --- a/anchor/src/models.ts +++ b/anchor/src/models.ts @@ -116,6 +116,8 @@ export class FundModel extends FundIdlModel { shareClassModel["allowlist"] = value as PublicKey[]; } else if (name === "shareClassBlocklist") { shareClassModel["blocklist"] = value as PublicKey[]; + } else if (name == "lockUp") { + shareClassModel["lockUpPeriodInSeconds"] = Number(value); } else { shareClassModel[name] = value; } @@ -135,14 +137,25 @@ export class FundModel extends FundIdlModel { } if (shareClassMint) { - const data = getExtensionData( + const extMetadata = getExtensionData( ExtensionType.TokenMetadata, shareClassMint.tlvData, ); - const metadata = data ? unpack(data) : ({} as TokenMetadata); - shareClassModel["symbol"] = metadata?.symbol; - shareClassModel["name"] = metadata?.name; - shareClassModel["uri"] = metadata?.uri; + const tokenMetadata = extMetadata + ? unpack(extMetadata) + : ({} as TokenMetadata); + shareClassModel["symbol"] = tokenMetadata?.symbol; + shareClassModel["name"] = tokenMetadata?.name; + shareClassModel["uri"] = tokenMetadata?.uri; + + const extPermDelegate = getExtensionData( + ExtensionType.PermanentDelegate, + shareClassMint.tlvData, + ); + if (extPermDelegate) { + const permanentDelegate = new PublicKey(extPermDelegate); + shareClassModel["permanentDelegate"] = permanentDelegate; + } } fundModel.shareClasses.push(new ShareClassModel(shareClassModel)); diff --git a/anchor/src/utils/blockhash.ts b/anchor/src/utils/blockhash.ts index 1f0dc8d9..a8358f3b 100644 --- a/anchor/src/utils/blockhash.ts +++ b/anchor/src/utils/blockhash.ts @@ -18,7 +18,7 @@ export class BlockhashWithCache { constructor( provider: anchor.Provider, isBrowser: boolean, - ttl: number = 15_000 + ttl: number = 15_000, ) { this.provider = provider; this.isBrowser = isBrowser; @@ -37,18 +37,18 @@ export class BlockhashWithCache { if (data) { const { blockhash, expiresAt } = data; - console.log(`blockhash expires at`, expiresAt); + // console.log(`blockhash expires at`, expiresAt); if (expiresAt > Date.now()) { - console.log("blockhash cache hit"); + // console.log("blockhash cache hit"); return blockhash; } } const latestBlockhash = await this.provider.connection.getLatestBlockhash(); - console.log( - "blockhash cache miss, fetched latestBlockhash:", - latestBlockhash - ); + // console.log( + // "blockhash cache miss, fetched latestBlockhash:", + // latestBlockhash + // ); await this.set({ blockhash: latestBlockhash, expiresAt: Date.now() + this.ttl, @@ -91,7 +91,7 @@ export class BlockhashWithCache { this.cacheKey, new Response(JSON.stringify(data), { headers: { "Content-Type": "application/json" }, - }) + }), ); } diff --git a/anchor/target/idl/glam.json b/anchor/target/idl/glam.json index 130bf754..5f97e817 100644 --- a/anchor/target/idl/glam.json +++ b/anchor/target/idl/glam.json @@ -9,6 +9,17 @@ "instructions": [ { "name": "add_share_class", + "docs": [ + "Share class", + "Adds a new share class to a fund.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_metadata`: An instance of `ShareClassModel` containing the metadata for the new share class.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 34, 49, @@ -69,12 +80,9 @@ "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "system_program", @@ -98,6 +106,20 @@ }, { "name": "burn_share", + "docs": [ + "Burns a specified amount of shares for the given share class.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to burn shares for.", + "- `amount`: The amount of shares to burn.", + "", + "# Permission required", + "- Permission::BurnShare", + "", + "# Integration required", + "- IntegrationName::Mint" + ], "discriminator": [ 111, 41, @@ -178,12 +200,9 @@ "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token_2022_program", @@ -203,6 +222,19 @@ }, { "name": "cast_vote", + "docs": [ + "Casts a vote.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `side`: The side to vote for.", + "", + "# Permission required", + "- Permission::VoteOnProposal", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 20, 212, @@ -283,6 +315,15 @@ }, { "name": "close_fund", + "docs": [ + "Closes a fund and releases its resources.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 230, 183, @@ -328,12 +369,9 @@ } }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "system_program", @@ -344,6 +382,16 @@ }, { "name": "close_share_class", + "docs": [ + "Closes a share class and releases its resources.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to be closed.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 35, 248, @@ -429,12 +477,9 @@ "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token_2022_program", @@ -450,6 +495,15 @@ }, { "name": "close_token_accounts", + "docs": [ + "Closes token accounts owned by the treasury.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 199, 170, @@ -491,12 +545,9 @@ } }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token_program", @@ -511,6 +562,18 @@ }, { "name": "deactivate_stake_accounts", + "docs": [ + "Deactivates stake accounts.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 58, 18, @@ -523,7 +586,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -571,6 +634,21 @@ }, { "name": "drift_cancel_orders", + "docs": [ + "Cancels drift orders.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `market_type`:", + "- `market_index`:", + "- `direction`:", + "", + "# Permission required", + "- Permission::DriftCancelOrders", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 98, 107, @@ -618,7 +696,7 @@ } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -662,6 +740,18 @@ }, { "name": "drift_delete_user", + "docs": [ + "Deletes a drift user (sub account).", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::DriftDeleteUser", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 179, 118, @@ -713,7 +803,7 @@ } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -726,15 +816,24 @@ "address": "11111111111111111111111111111111" } ], - "args": [ - { - "name": "_sub_account_id", - "type": "u16" - } - ] + "args": [] }, { "name": "drift_deposit", + "docs": [ + "Deposits to drift.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `market_index`: Index of the drift spot market.", + "- `amount`: Amount of asset to deposit.", + "", + "# Permission required", + "- Permission::DriftDeposit", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 252, 63, @@ -794,7 +893,7 @@ "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -820,6 +919,19 @@ }, { "name": "drift_initialize", + "docs": [ + "Drift", + "Initializes a drift account owned by fund treasury and creates a subaccount.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::DriftInitialize", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 21, 21, @@ -871,7 +983,7 @@ } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -892,6 +1004,20 @@ }, { "name": "drift_place_orders", + "docs": [ + "Places orders on drift.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `order_params`: A list of orders.", + "", + "# Permissions required", + "- Permission::DriftPlaceOrders", + "- Additional permission Permission::DriftSpotMarket or Permission::DriftPerpMarket is required depending on market type.", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 117, 18, @@ -939,7 +1065,7 @@ } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -967,6 +1093,20 @@ }, { "name": "drift_update_user_custom_margin_ratio", + "docs": [ + "Updates custom margin ratio.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `sub_account_id`: Sub account.", + "- `margin_ratio`: Margin ratio.", + "", + "# Permission required", + "- Permission::DriftUpdateUser", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 4, 47, @@ -1010,7 +1150,7 @@ } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1032,6 +1172,20 @@ }, { "name": "drift_update_user_delegate", + "docs": [ + "Sets a delegate on the specified sub account.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `sub_account_id`: Sub account.", + "- `delegate`: Delegate's wallet address.", + "", + "# Permission required", + "- Permission::DriftUpdateUser", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 36, 181, @@ -1075,7 +1229,7 @@ } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1097,6 +1251,20 @@ }, { "name": "drift_update_user_margin_trading_enabled", + "docs": [ + "Enables/Disables margin trading.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `sub_account_id`: Sub account.", + "- `margin_trading_enabled`: Whether to enable or disable margin trading.", + "", + "# Permission required", + "- Permission::DriftUpdateUser", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 157, 175, @@ -1140,7 +1308,7 @@ } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1162,6 +1330,20 @@ }, { "name": "drift_withdraw", + "docs": [ + "Withdraws from drift.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `market_index`: Index of the drift spot market.", + "- `amount`: Amount to withdraw.", + "", + "# Permission required", + "- Permission::DriftWithdraw", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 86, 59, @@ -1224,7 +1406,7 @@ "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1250,6 +1432,20 @@ }, { "name": "force_transfer_share", + "docs": [ + "Forcefully transfers a specified amount of shares from one account to another.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to transfer shares for.", + "- `amount`: The amount of shares to transfer.", + "", + "# Permission required", + "- Permission::ForceTransferShare", + "", + "# Integration required", + "- IntegrationName::Mint" + ], "discriminator": [ 71, 90, @@ -1390,12 +1586,9 @@ "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token_2022_program", @@ -1415,6 +1608,19 @@ }, { "name": "increase_locked_amount", + "docs": [ + "Increases the locked amount (aka stakes JUP).", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount of JUP to stake.", + "", + "# Permission required", + "- Permission::StakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 5, 168, @@ -1493,6 +1699,18 @@ }, { "name": "init_locked_voter_escrow", + "docs": [ + "Initializes a locked voter escrow.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::StakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 148, 74, @@ -1558,6 +1776,21 @@ }, { "name": "initialize_and_delegate_stake", + "docs": [ + "Initializes a stake account and delegates it to a validator.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `lamports`: The amount of SOL to initialize the stake account with.", + "- `stake_account_id`: The ID of the stake account to initialize.", + "- `stake_account_bump`: The bump seed for the stake account.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 71, 101, @@ -1570,7 +1803,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1654,6 +1887,17 @@ }, { "name": "initialize_fund", + "docs": [ + "Fund", + "Initializes a fund from the provided FundModel instance.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `fund`: An instance of `FundModel` containing the details of the fund to be initialized.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 212, 42, @@ -1681,7 +1925,7 @@ }, { "kind": "account", - "path": "manager" + "path": "signer" }, { "kind": "arg", @@ -1742,7 +1986,7 @@ } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1764,6 +2008,20 @@ }, { "name": "jupiter_swap", + "docs": [ + "Swaps assets using Jupiter.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount of asset to swap.", + "- `data`: The data for the swap.", + "", + "# Permission required", + "- Permission::JupiterSwapFundAssets or Permission::JupiterSwapAnyAsset", + "", + "# Integration required", + "- IntegrationName::JupiterSwap" + ], "discriminator": [ 116, 207, @@ -1972,6 +2230,18 @@ }, { "name": "marinade_claim_tickets", + "docs": [ + "Claims tickets that were unstaked in the previous epoch to get SOL.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::Marinade" + ], "discriminator": [ 14, 146, @@ -1984,7 +2254,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -2053,6 +2323,21 @@ }, { "name": "marinade_delayed_unstake", + "docs": [ + "Unstakes mSOL to get a ticket that can be claimed at the next epoch.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `msol_amount`: Amount of mSOL to unstake.", + "- `ticket_id`: Ticket ID.", + "- `bump`: Bump seed.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::Marinade" + ], "discriminator": [ 117, 66, @@ -2065,7 +2350,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -2159,6 +2444,20 @@ }, { "name": "marinade_deposit_sol", + "docs": [ + "Marinade", + "Deposits SOL to get mSOL.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `lamports`: The amount of SOL to deposit.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::Marinade" + ], "discriminator": [ 64, 140, @@ -2171,7 +2470,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -2350,6 +2649,19 @@ }, { "name": "marinade_deposit_stake", + "docs": [ + "Deposits a stake account to get mSOL.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `validator_idx`: Validator index.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::Marinade" + ], "discriminator": [ 69, 207, @@ -2362,7 +2674,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -2552,6 +2864,19 @@ }, { "name": "marinade_liquid_unstake", + "docs": [ + "Unstakes mSOL to get SOL immediately.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `msol_amount`: Amount of mSOL to unstake.", + "", + "# Permission required", + "- Permission::LiquidUnstake", + "", + "# Integration required", + "- IntegrationName::Marinade" + ], "discriminator": [ 29, 146, @@ -2564,7 +2889,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "signer": true }, { @@ -2648,6 +2973,18 @@ }, { "name": "merge_partial_unstaking", + "docs": [ + "Merges partial unstaking.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::UnstakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 190, 154, @@ -2717,6 +3054,18 @@ }, { "name": "merge_stake_accounts", + "docs": [ + "Merges two stake accounts.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 173, 206, @@ -2729,7 +3078,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -2794,6 +3143,20 @@ }, { "name": "mint_share", + "docs": [ + "Mints a specified amount of shares for the given share class.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to mint shares for.", + "- `amount`: The amount of shares to mint.", + "", + "# Permission required", + "- Permission::MintShare", + "", + "# Integration required", + "- IntegrationName::Mint" + ], "discriminator": [ 145, 1, @@ -2874,12 +3237,9 @@ "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token_2022_program", @@ -2899,6 +3259,18 @@ }, { "name": "new_vote", + "docs": [ + "Creates a new vote.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::VoteOnProposal", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 163, 108, @@ -2964,6 +3336,20 @@ }, { "name": "open_partial_unstaking", + "docs": [ + "Partially unstakes JUP.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount of JUP to partially unstake.", + "- `memo`: The memo for the partial unstaking.", + "", + "# Permission required", + "- Permission::UnstakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 201, 137, @@ -3042,6 +3428,15 @@ }, { "name": "redeem", + "docs": [ + "Redeems a specified amount of shares.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount of shares to redeem.", + "- `in_kind`: Whether to redeem in kind.", + "- `skip_state`: Should always be true (state check to be implemented)." + ], "discriminator": [ 184, 12, @@ -3156,6 +3551,20 @@ }, { "name": "redelegate_stake", + "docs": [ + "Redelegates an existing stake account to a new validator (a new stake account will be created).", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `new_stake_account_id`: The ID of the new stake account.", + "- `new_stake_account_bump`: The bump seed for the new stake account.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 240, 90, @@ -3168,7 +3577,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3244,6 +3653,18 @@ }, { "name": "set_subscribe_redeem_enabled", + "docs": [ + "Enables or disables the subscribe and redeem functionality for the fund.", + "", + "This allows the manager to pause/unpause subscription and redemption of a fund.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `enabled`: A boolean indicating whether to enable or disable the subscribe and redeem functionality.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 189, 56, @@ -3260,12 +3681,9 @@ "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true } ], "args": [ @@ -3277,6 +3695,20 @@ }, { "name": "set_token_accounts_states", + "docs": [ + "Sets the frozen state of the token accounts for the specified share class.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to set the frozen state for.", + "- `frozen`: The new frozen state.", + "", + "# Permission required", + "- Permission::SetTokenAccountsStates", + "", + "# Integration required", + "- IntegrationName::Mint" + ], "discriminator": [ 50, 133, @@ -3297,12 +3729,9 @@ "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token_2022_program", @@ -3322,6 +3751,21 @@ }, { "name": "split_stake_account", + "docs": [ + "Splits from an existing stake account to get a new stake account.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `lamports`: The amount of SOL to split.", + "- `new_stake_account_id`: The ID of the new stake account.", + "- `new_stake_account_bump`: The bump seed for the new stake account.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 130, 42, @@ -3334,7 +3778,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3408,6 +3852,19 @@ }, { "name": "stake_pool_deposit_sol", + "docs": [ + "Deposits SOL to a stake pool to get pool token.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `lamports`: The amount of SOL to deposit.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + ], "discriminator": [ 147, 187, @@ -3420,7 +3877,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3455,9 +3912,6 @@ "fund" ] }, - { - "name": "stake_pool_program" - }, { "name": "stake_pool", "writable": true @@ -3567,6 +4021,9 @@ } } }, + { + "name": "stake_pool_program" + }, { "name": "associated_token_program", "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" @@ -3589,7 +4046,16 @@ { "name": "stake_pool_deposit_stake", "docs": [ - "Deposit a stake account into the stake pool and receive pool token" + "Deposits a stake account to a stake pool to get pool token.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." ], "discriminator": [ 212, @@ -3603,7 +4069,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3763,9 +4229,6 @@ "name": "reserve_stake_account", "writable": true }, - { - "name": "stake_pool_program" - }, { "name": "clock", "address": "SysvarC1ock11111111111111111111111111111111" @@ -3774,6 +4237,9 @@ "name": "stake_history", "address": "SysvarStakeHistory1111111111111111111111111" }, + { + "name": "stake_pool_program" + }, { "name": "associated_token_program", "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" @@ -3794,6 +4260,19 @@ }, { "name": "stake_pool_withdraw_sol", + "docs": [ + "Unstakes from pool token to get SOL immediately.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `pool_token_amount`: Amount of pool token to unstake.", + "", + "# Permission required", + "- Permission::LiquidUnstake", + "", + "# Integration required", + "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + ], "discriminator": [ 179, 100, @@ -3806,7 +4285,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3841,9 +4320,6 @@ "fund" ] }, - { - "name": "stake_pool_program" - }, { "name": "stake_pool", "writable": true @@ -3875,6 +4351,9 @@ "name": "stake_history", "address": "SysvarStakeHistory1111111111111111111111111" }, + { + "name": "stake_pool_program" + }, { "name": "system_program", "address": "11111111111111111111111111111111" @@ -3896,6 +4375,21 @@ }, { "name": "stake_pool_withdraw_stake", + "docs": [ + "Unstakes from pool token into a stake account.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `pool_token_amount`: Amount of pool token to unstake.", + "- `stake_account_id`: Stake account ID.", + "- `stake_account_bump`: Stake account bump seed.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + ], "discriminator": [ 7, 70, @@ -3908,7 +4402,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3975,13 +4469,13 @@ "name": "pool_token_ata", "writable": true }, - { - "name": "stake_pool_program" - }, { "name": "clock", "address": "SysvarC1ock11111111111111111111111111111111" }, + { + "name": "stake_pool_program" + }, { "name": "system_program", "address": "11111111111111111111111111111111" @@ -4011,6 +4505,15 @@ }, { "name": "subscribe", + "docs": [ + "Investor", + "Subscribes to a specified amount of shares.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount of shares to subscribe.", + "- `skip_state`: Should always be true (state check to be implemented)." + ], "discriminator": [ 254, 28, @@ -4177,6 +4680,10 @@ } ], "args": [ + { + "name": "share_class_id", + "type": "u8" + }, { "name": "amount", "type": "u64" @@ -4189,6 +4696,19 @@ }, { "name": "toggle_max_lock", + "docs": [ + "Toggles max lock.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `value`: The value to toggle.", + "", + "# Permission required", + "- Permission::UnstakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 163, 157, @@ -4390,6 +4910,16 @@ }, { "name": "update_fund", + "docs": [ + "Updates an existing fund with new parameters.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `fund`: An instance of `FundModel` containing the updated details of the fund.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 132, 171, @@ -4424,6 +4954,17 @@ }, { "name": "update_share_class", + "docs": [ + "Updates an existing share class with new metadata.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to be updated.", + "- `share_class_metadata`: An instance of `ShareClassModel` containing the updated metadata for the new share class.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 196, 227, @@ -4444,12 +4985,9 @@ "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token_2022_program", @@ -4473,6 +5011,16 @@ }, { "name": "withdraw", + "docs": [ + "Withdraw an asset from fund treasury into manager's wallet.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount to withdraw.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 183, 18, @@ -4580,7 +5128,7 @@ "seeds": [ { "kind": "account", - "path": "manager" + "path": "signer" }, { "kind": "account", @@ -4631,12 +5179,9 @@ } }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token_program" @@ -4651,6 +5196,18 @@ }, { "name": "withdraw_all_staked_jup", + "docs": [ + "Withdraws all unstaked JUP.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::UnstakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 210, 124, @@ -4724,6 +5281,18 @@ }, { "name": "withdraw_from_stake_accounts", + "docs": [ + "Withdraws SOL from stake accounts.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 93, 209, @@ -4736,7 +5305,7 @@ ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -4789,6 +5358,18 @@ }, { "name": "withdraw_partial_unstaking", + "docs": [ + "Withdraws JUP from partial unstaking.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::UnstakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 201, 202, @@ -4866,6 +5447,15 @@ }, { "name": "wsol_unwrap", + "docs": [ + "Unwraps all wSOL to get SOL.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::WSolUnwrap" + ], "discriminator": [ 123, 189, @@ -5013,6 +5603,16 @@ }, { "name": "wsol_wrap", + "docs": [ + "Wraps SOL to get wSOL.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `lamports`: The amount of SOL to wrap.", + "", + "# Permission required", + "- Permission::WSolWrap" + ], "discriminator": [ 26, 2, @@ -6562,7 +7162,7 @@ "name": "Marinade" }, { - "name": "Jupiter" + "name": "JupiterSwap" }, { "name": "Mint" diff --git a/anchor/target/types/glam.ts b/anchor/target/types/glam.ts index dbbb69ee..434b3727 100644 --- a/anchor/target/types/glam.ts +++ b/anchor/target/types/glam.ts @@ -15,6 +15,17 @@ export type Glam = { "instructions": [ { "name": "addShareClass", + "docs": [ + "Share class", + "Adds a new share class to a fund.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_metadata`: An instance of `ShareClassModel` containing the metadata for the new share class.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 34, 49, @@ -75,12 +86,9 @@ export type Glam = { "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "systemProgram", @@ -104,6 +112,20 @@ export type Glam = { }, { "name": "burnShare", + "docs": [ + "Burns a specified amount of shares for the given share class.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to burn shares for.", + "- `amount`: The amount of shares to burn.", + "", + "# Permission required", + "- Permission::BurnShare", + "", + "# Integration required", + "- IntegrationName::Mint" + ], "discriminator": [ 111, 41, @@ -184,12 +206,9 @@ export type Glam = { "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token2022Program", @@ -209,6 +228,19 @@ export type Glam = { }, { "name": "castVote", + "docs": [ + "Casts a vote.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `side`: The side to vote for.", + "", + "# Permission required", + "- Permission::VoteOnProposal", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 20, 212, @@ -289,6 +321,15 @@ export type Glam = { }, { "name": "closeFund", + "docs": [ + "Closes a fund and releases its resources.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 230, 183, @@ -334,12 +375,9 @@ export type Glam = { } }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "systemProgram", @@ -350,6 +388,16 @@ export type Glam = { }, { "name": "closeShareClass", + "docs": [ + "Closes a share class and releases its resources.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to be closed.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 35, 248, @@ -435,12 +483,9 @@ export type Glam = { "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token2022Program", @@ -456,6 +501,15 @@ export type Glam = { }, { "name": "closeTokenAccounts", + "docs": [ + "Closes token accounts owned by the treasury.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 199, 170, @@ -497,12 +551,9 @@ export type Glam = { } }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "tokenProgram", @@ -517,6 +568,18 @@ export type Glam = { }, { "name": "deactivateStakeAccounts", + "docs": [ + "Deactivates stake accounts.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 58, 18, @@ -529,7 +592,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -577,6 +640,21 @@ export type Glam = { }, { "name": "driftCancelOrders", + "docs": [ + "Cancels drift orders.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `market_type`:", + "- `market_index`:", + "- `direction`:", + "", + "# Permission required", + "- Permission::DriftCancelOrders", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 98, 107, @@ -624,7 +702,7 @@ export type Glam = { } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -668,6 +746,18 @@ export type Glam = { }, { "name": "driftDeleteUser", + "docs": [ + "Deletes a drift user (sub account).", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::DriftDeleteUser", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 179, 118, @@ -719,7 +809,7 @@ export type Glam = { } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -732,15 +822,24 @@ export type Glam = { "address": "11111111111111111111111111111111" } ], - "args": [ - { - "name": "subAccountId", - "type": "u16" - } - ] + "args": [] }, { "name": "driftDeposit", + "docs": [ + "Deposits to drift.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `market_index`: Index of the drift spot market.", + "- `amount`: Amount of asset to deposit.", + "", + "# Permission required", + "- Permission::DriftDeposit", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 252, 63, @@ -800,7 +899,7 @@ export type Glam = { "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -826,6 +925,19 @@ export type Glam = { }, { "name": "driftInitialize", + "docs": [ + "drift", + "Initializes a drift account owned by fund treasury and creates a subaccount.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::DriftInitialize", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 21, 21, @@ -877,7 +989,7 @@ export type Glam = { } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -898,6 +1010,20 @@ export type Glam = { }, { "name": "driftPlaceOrders", + "docs": [ + "Places orders on drift.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `order_params`: A list of orders.", + "", + "# Permissions required", + "- Permission::DriftPlaceOrders", + "- Additional permission Permission::DriftSpotMarket or Permission::DriftPerpMarket is required depending on market type.", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 117, 18, @@ -945,7 +1071,7 @@ export type Glam = { } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -973,6 +1099,20 @@ export type Glam = { }, { "name": "driftUpdateUserCustomMarginRatio", + "docs": [ + "Updates custom margin ratio.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `sub_account_id`: Sub account.", + "- `margin_ratio`: Margin ratio.", + "", + "# Permission required", + "- Permission::DriftUpdateUser", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 4, 47, @@ -1016,7 +1156,7 @@ export type Glam = { } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1038,6 +1178,20 @@ export type Glam = { }, { "name": "driftUpdateUserDelegate", + "docs": [ + "Sets a delegate on the specified sub account.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `sub_account_id`: Sub account.", + "- `delegate`: Delegate's wallet address.", + "", + "# Permission required", + "- Permission::DriftUpdateUser", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 36, 181, @@ -1081,7 +1235,7 @@ export type Glam = { } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1103,6 +1257,20 @@ export type Glam = { }, { "name": "driftUpdateUserMarginTradingEnabled", + "docs": [ + "Enables/Disables margin trading.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `sub_account_id`: Sub account.", + "- `margin_trading_enabled`: Whether to enable or disable margin trading.", + "", + "# Permission required", + "- Permission::DriftUpdateUser", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 157, 175, @@ -1146,7 +1314,7 @@ export type Glam = { } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1168,6 +1336,20 @@ export type Glam = { }, { "name": "driftWithdraw", + "docs": [ + "Withdraws from drift.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `market_index`: Index of the drift spot market.", + "- `amount`: Amount to withdraw.", + "", + "# Permission required", + "- Permission::DriftWithdraw", + "", + "# Integration required", + "- IntegrationName::Drift" + ], "discriminator": [ 86, 59, @@ -1230,7 +1412,7 @@ export type Glam = { "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1256,6 +1438,20 @@ export type Glam = { }, { "name": "forceTransferShare", + "docs": [ + "Forcefully transfers a specified amount of shares from one account to another.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to transfer shares for.", + "- `amount`: The amount of shares to transfer.", + "", + "# Permission required", + "- Permission::ForceTransferShare", + "", + "# Integration required", + "- IntegrationName::Mint" + ], "discriminator": [ 71, 90, @@ -1396,12 +1592,9 @@ export type Glam = { "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token2022Program", @@ -1421,6 +1614,19 @@ export type Glam = { }, { "name": "increaseLockedAmount", + "docs": [ + "Increases the locked amount (aka stakes JUP).", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount of JUP to stake.", + "", + "# Permission required", + "- Permission::StakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 5, 168, @@ -1499,6 +1705,18 @@ export type Glam = { }, { "name": "initLockedVoterEscrow", + "docs": [ + "Initializes a locked voter escrow.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::StakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 148, 74, @@ -1564,6 +1782,21 @@ export type Glam = { }, { "name": "initializeAndDelegateStake", + "docs": [ + "Initializes a stake account and delegates it to a validator.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `lamports`: The amount of SOL to initialize the stake account with.", + "- `stake_account_id`: The ID of the stake account to initialize.", + "- `stake_account_bump`: The bump seed for the stake account.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 71, 101, @@ -1576,7 +1809,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1660,6 +1893,17 @@ export type Glam = { }, { "name": "initializeFund", + "docs": [ + "Fund", + "Initializes a fund from the provided FundModel instance.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `fund`: An instance of `FundModel` containing the details of the fund to be initialized.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 212, 42, @@ -1687,7 +1931,7 @@ export type Glam = { }, { "kind": "account", - "path": "manager" + "path": "signer" }, { "kind": "arg", @@ -1748,7 +1992,7 @@ export type Glam = { } }, { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -1770,6 +2014,20 @@ export type Glam = { }, { "name": "jupiterSwap", + "docs": [ + "Swaps assets using Jupiter.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount of asset to swap.", + "- `data`: The data for the swap.", + "", + "# Permission required", + "- Permission::JupiterSwapFundAssets or Permission::JupiterSwapAnyAsset", + "", + "# Integration required", + "- IntegrationName::JupiterSwap" + ], "discriminator": [ 116, 207, @@ -1978,6 +2236,18 @@ export type Glam = { }, { "name": "marinadeClaimTickets", + "docs": [ + "Claims tickets that were unstaked in the previous epoch to get SOL.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::Marinade" + ], "discriminator": [ 14, 146, @@ -1990,7 +2260,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -2059,6 +2329,21 @@ export type Glam = { }, { "name": "marinadeDelayedUnstake", + "docs": [ + "Unstakes mSOL to get a ticket that can be claimed at the next epoch.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `msol_amount`: Amount of mSOL to unstake.", + "- `ticket_id`: Ticket ID.", + "- `bump`: Bump seed.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::Marinade" + ], "discriminator": [ 117, 66, @@ -2071,7 +2356,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -2165,6 +2450,20 @@ export type Glam = { }, { "name": "marinadeDepositSol", + "docs": [ + "marinade", + "Deposits SOL to get mSOL.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `lamports`: The amount of SOL to deposit.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::Marinade" + ], "discriminator": [ 64, 140, @@ -2177,7 +2476,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -2356,6 +2655,19 @@ export type Glam = { }, { "name": "marinadeDepositStake", + "docs": [ + "Deposits a stake account to get mSOL.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `validator_idx`: Validator index.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::Marinade" + ], "discriminator": [ 69, 207, @@ -2368,7 +2680,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -2558,6 +2870,19 @@ export type Glam = { }, { "name": "marinadeLiquidUnstake", + "docs": [ + "Unstakes mSOL to get SOL immediately.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `msol_amount`: Amount of mSOL to unstake.", + "", + "# Permission required", + "- Permission::LiquidUnstake", + "", + "# Integration required", + "- IntegrationName::Marinade" + ], "discriminator": [ 29, 146, @@ -2570,7 +2895,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "signer": true }, { @@ -2654,6 +2979,18 @@ export type Glam = { }, { "name": "mergePartialUnstaking", + "docs": [ + "Merges partial unstaking.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::UnstakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 190, 154, @@ -2723,6 +3060,18 @@ export type Glam = { }, { "name": "mergeStakeAccounts", + "docs": [ + "Merges two stake accounts.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 173, 206, @@ -2735,7 +3084,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -2800,6 +3149,20 @@ export type Glam = { }, { "name": "mintShare", + "docs": [ + "Mints a specified amount of shares for the given share class.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to mint shares for.", + "- `amount`: The amount of shares to mint.", + "", + "# Permission required", + "- Permission::MintShare", + "", + "# Integration required", + "- IntegrationName::Mint" + ], "discriminator": [ 145, 1, @@ -2880,12 +3243,9 @@ export type Glam = { "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token2022Program", @@ -2905,6 +3265,18 @@ export type Glam = { }, { "name": "newVote", + "docs": [ + "Creates a new vote.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::VoteOnProposal", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 163, 108, @@ -2970,6 +3342,20 @@ export type Glam = { }, { "name": "openPartialUnstaking", + "docs": [ + "Partially unstakes JUP.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount of JUP to partially unstake.", + "- `memo`: The memo for the partial unstaking.", + "", + "# Permission required", + "- Permission::UnstakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 201, 137, @@ -3048,6 +3434,15 @@ export type Glam = { }, { "name": "redeem", + "docs": [ + "Redeems a specified amount of shares.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount of shares to redeem.", + "- `in_kind`: Whether to redeem in kind.", + "- `skip_state`: Should always be true (state check to be implemented)." + ], "discriminator": [ 184, 12, @@ -3162,6 +3557,20 @@ export type Glam = { }, { "name": "redelegateStake", + "docs": [ + "Redelegates an existing stake account to a new validator (a new stake account will be created).", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `new_stake_account_id`: The ID of the new stake account.", + "- `new_stake_account_bump`: The bump seed for the new stake account.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 240, 90, @@ -3174,7 +3583,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3250,6 +3659,18 @@ export type Glam = { }, { "name": "setSubscribeRedeemEnabled", + "docs": [ + "Enables or disables the subscribe and redeem functionality for the fund.", + "", + "This allows the manager to pause/unpause subscription and redemption of a fund.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `enabled`: A boolean indicating whether to enable or disable the subscribe and redeem functionality.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 189, 56, @@ -3266,12 +3687,9 @@ export type Glam = { "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true } ], "args": [ @@ -3283,6 +3701,20 @@ export type Glam = { }, { "name": "setTokenAccountsStates", + "docs": [ + "Sets the frozen state of the token accounts for the specified share class.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to set the frozen state for.", + "- `frozen`: The new frozen state.", + "", + "# Permission required", + "- Permission::SetTokenAccountsStates", + "", + "# Integration required", + "- IntegrationName::Mint" + ], "discriminator": [ 50, 133, @@ -3303,12 +3735,9 @@ export type Glam = { "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token2022Program", @@ -3328,6 +3757,21 @@ export type Glam = { }, { "name": "splitStakeAccount", + "docs": [ + "Splits from an existing stake account to get a new stake account.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `lamports`: The amount of SOL to split.", + "- `new_stake_account_id`: The ID of the new stake account.", + "- `new_stake_account_bump`: The bump seed for the new stake account.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 130, 42, @@ -3340,7 +3784,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3414,6 +3858,19 @@ export type Glam = { }, { "name": "stakePoolDepositSol", + "docs": [ + "Deposits SOL to a stake pool to get pool token.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `lamports`: The amount of SOL to deposit.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + ], "discriminator": [ 147, 187, @@ -3426,7 +3883,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3461,9 +3918,6 @@ export type Glam = { "fund" ] }, - { - "name": "stakePoolProgram" - }, { "name": "stakePool", "writable": true @@ -3573,6 +4027,9 @@ export type Glam = { } } }, + { + "name": "stakePoolProgram" + }, { "name": "associatedTokenProgram", "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" @@ -3595,7 +4052,16 @@ export type Glam = { { "name": "stakePoolDepositStake", "docs": [ - "Deposit a stake account into the stake pool and receive pool token" + "Deposits a stake account to a stake pool to get pool token.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::Stake", + "", + "# Integration required", + "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." ], "discriminator": [ 212, @@ -3609,7 +4075,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3769,9 +4235,6 @@ export type Glam = { "name": "reserveStakeAccount", "writable": true }, - { - "name": "stakePoolProgram" - }, { "name": "clock", "address": "SysvarC1ock11111111111111111111111111111111" @@ -3780,6 +4243,9 @@ export type Glam = { "name": "stakeHistory", "address": "SysvarStakeHistory1111111111111111111111111" }, + { + "name": "stakePoolProgram" + }, { "name": "associatedTokenProgram", "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" @@ -3800,6 +4266,19 @@ export type Glam = { }, { "name": "stakePoolWithdrawSol", + "docs": [ + "Unstakes from pool token to get SOL immediately.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `pool_token_amount`: Amount of pool token to unstake.", + "", + "# Permission required", + "- Permission::LiquidUnstake", + "", + "# Integration required", + "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + ], "discriminator": [ 179, 100, @@ -3812,7 +4291,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3847,9 +4326,6 @@ export type Glam = { "fund" ] }, - { - "name": "stakePoolProgram" - }, { "name": "stakePool", "writable": true @@ -3881,6 +4357,9 @@ export type Glam = { "name": "stakeHistory", "address": "SysvarStakeHistory1111111111111111111111111" }, + { + "name": "stakePoolProgram" + }, { "name": "systemProgram", "address": "11111111111111111111111111111111" @@ -3902,6 +4381,21 @@ export type Glam = { }, { "name": "stakePoolWithdrawStake", + "docs": [ + "Unstakes from pool token into a stake account.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `pool_token_amount`: Amount of pool token to unstake.", + "- `stake_account_id`: Stake account ID.", + "- `stake_account_bump`: Stake account bump seed.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + ], "discriminator": [ 7, 70, @@ -3914,7 +4408,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -3981,13 +4475,13 @@ export type Glam = { "name": "poolTokenAta", "writable": true }, - { - "name": "stakePoolProgram" - }, { "name": "clock", "address": "SysvarC1ock11111111111111111111111111111111" }, + { + "name": "stakePoolProgram" + }, { "name": "systemProgram", "address": "11111111111111111111111111111111" @@ -4017,6 +4511,15 @@ export type Glam = { }, { "name": "subscribe", + "docs": [ + "Investor", + "Subscribes to a specified amount of shares.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount of shares to subscribe.", + "- `skip_state`: Should always be true (state check to be implemented)." + ], "discriminator": [ 254, 28, @@ -4183,6 +4686,10 @@ export type Glam = { } ], "args": [ + { + "name": "shareClassId", + "type": "u8" + }, { "name": "amount", "type": "u64" @@ -4195,6 +4702,19 @@ export type Glam = { }, { "name": "toggleMaxLock", + "docs": [ + "Toggles max lock.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `value`: The value to toggle.", + "", + "# Permission required", + "- Permission::UnstakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 163, 157, @@ -4396,6 +4916,16 @@ export type Glam = { }, { "name": "updateFund", + "docs": [ + "Updates an existing fund with new parameters.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `fund`: An instance of `FundModel` containing the updated details of the fund.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 132, 171, @@ -4430,6 +4960,17 @@ export type Glam = { }, { "name": "updateShareClass", + "docs": [ + "Updates an existing share class with new metadata.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `share_class_id`: The id of the share class to be updated.", + "- `share_class_metadata`: An instance of `ShareClassModel` containing the updated metadata for the new share class.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 196, 227, @@ -4450,12 +4991,9 @@ export type Glam = { "writable": true }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "token2022Program", @@ -4479,6 +5017,16 @@ export type Glam = { }, { "name": "withdraw", + "docs": [ + "Withdraw an asset from fund treasury into manager's wallet.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `amount`: The amount to withdraw.", + "", + "# Permission required", + "- Manager only, delegates not allowed" + ], "discriminator": [ 183, 18, @@ -4586,7 +5134,7 @@ export type Glam = { "seeds": [ { "kind": "account", - "path": "manager" + "path": "signer" }, { "kind": "account", @@ -4637,12 +5185,9 @@ export type Glam = { } }, { - "name": "manager", + "name": "signer", "writable": true, - "signer": true, - "relations": [ - "fund" - ] + "signer": true }, { "name": "tokenProgram" @@ -4657,6 +5202,18 @@ export type Glam = { }, { "name": "withdrawAllStakedJup", + "docs": [ + "Withdraws all unstaked JUP.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::UnstakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 210, 124, @@ -4730,6 +5287,18 @@ export type Glam = { }, { "name": "withdrawFromStakeAccounts", + "docs": [ + "Withdraws SOL from stake accounts.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::Unstake", + "", + "# Integration required", + "- IntegrationName::NativeStaking" + ], "discriminator": [ 93, 209, @@ -4742,7 +5311,7 @@ export type Glam = { ], "accounts": [ { - "name": "manager", + "name": "signer", "writable": true, "signer": true }, @@ -4795,6 +5364,18 @@ export type Glam = { }, { "name": "withdrawPartialUnstaking", + "docs": [ + "Withdraws JUP from partial unstaking.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::UnstakeJup", + "", + "# Integration required", + "- IntegrationName::JupiterVote" + ], "discriminator": [ 201, 202, @@ -4872,6 +5453,15 @@ export type Glam = { }, { "name": "wsolUnwrap", + "docs": [ + "Unwraps all wSOL to get SOL.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "", + "# Permission required", + "- Permission::WSolUnwrap" + ], "discriminator": [ 123, 189, @@ -5019,6 +5609,16 @@ export type Glam = { }, { "name": "wsolWrap", + "docs": [ + "Wraps SOL to get wSOL.", + "", + "# Parameters", + "- `ctx`: The context for the transaction.", + "- `lamports`: The amount of SOL to wrap.", + "", + "# Permission required", + "- Permission::WSolWrap" + ], "discriminator": [ 26, 2, @@ -6568,7 +7168,7 @@ export type Glam = { "name": "marinade" }, { - "name": "jupiter" + "name": "jupiterSwap" }, { "name": "mint" diff --git a/anchor/tests/glam_crud.spec.ts b/anchor/tests/glam_crud.spec.ts index 768f9685..3ff5ef04 100644 --- a/anchor/tests/glam_crud.spec.ts +++ b/anchor/tests/glam_crud.spec.ts @@ -27,8 +27,11 @@ describe("glam_crud", () => { let fundPDA: PublicKey; it("Initialize fund", async () => { + const shareClassAllowlist = [glamClient.getSigner()]; + const shareClassBlocklist = []; + const fundForTest = { ...testFundModel }; - fundForTest.shareClasses![0].allowlist = [glamClient.getManager()]; + fundForTest.shareClasses![0].allowlist = shareClassAllowlist; const fundData = await createFundForTest(glamClient, fundForTest); fundPDA = fundData.fundPDA; @@ -36,10 +39,8 @@ describe("glam_crud", () => { const fundModel = await glamClient.fetchFund(fundPDA); expect(fundModel.shareClasses.length).toEqual(1); - expect(fundModel.shareClasses[0].allowlist).toEqual([ - glamClient.getManager(), - ]); - expect(fundModel.shareClasses[0].blocklist).toEqual([]); + expect(fundModel.shareClasses[0].allowlist).toEqual(shareClassAllowlist); + expect(fundModel.shareClasses[0].blocklist).toEqual(shareClassBlocklist); }); it("Update fund name", async () => { @@ -51,7 +52,7 @@ describe("glam_crud", () => { console.error(e); throw e; } - const fund = await glamClient.program.account.fundAccount.fetch(fundPDA); + const fund = await glamClient.fetchFundAccount(fundPDA); expect(fund.name).toEqual(updatedFund.name); }); @@ -105,36 +106,45 @@ describe("glam_crud", () => { }); it("[integration-acl] add and update", async () => { - const updatedFund1 = { + // 0 by default + let fundModel = await glamClient.fetchFund(fundPDA); + expect(fundModel.integrationAcls.length).toEqual(0); + + // 1 acl + let updatedFund = { integrationAcls: [{ name: { drift: {} }, features: [] }], - }; + } as Partial; try { - const txSig = await glamClient.fund.updateFund(fundPDA, updatedFund1); + const txSig = await glamClient.fund.updateFund(fundPDA, updatedFund); console.log("Update integration acl txSig", txSig); } catch (e) { console.error(e); throw e; } - const fundModel1 = await glamClient.fetchFund(fundPDA); - expect(fundModel1.integrationAcls.length).toEqual(1); - expect(fundModel1.integrationAcls).toEqual(updatedFund1.integrationAcls); + fundModel = await glamClient.fetchFund(fundPDA); + expect(fundModel.integrationAcls.length).toEqual(1); + expect(fundModel.integrationAcls).toEqual(updatedFund.integrationAcls); - const updatedFund2 = { + // 5 acls + updatedFund = { integrationAcls: [ { name: { drift: {} }, features: [] }, - { name: { jupiter: {} }, features: [] }, + { name: { jupiterSwap: {} }, features: [] }, { name: { marinade: {} }, features: [] }, { name: { splStakePool: {} }, features: [] }, { name: { sanctumStakePool: {} }, features: [] }, ], }; try { - const txSig = await glamClient.fund.updateFund(fundPDA, updatedFund2); + const txSig = await glamClient.fund.updateFund(fundPDA, updatedFund); console.log("Update integration acl txSig", txSig); } catch (e) { console.error(e); throw e; } + fundModel = await glamClient.fetchFund(fundPDA); + expect(fundModel.integrationAcls.length).toEqual(5); + expect(fundModel.integrationAcls).toEqual(updatedFund.integrationAcls); }); it("[delegate-acl] upsert", async () => { @@ -223,12 +233,12 @@ describe("glam_crud", () => { // transfer 0.1 SOL to key1 as it needs to pay for treasury wsol ata creation const tranferTx = new Transaction().add( SystemProgram.transfer({ - fromPubkey: glamClient.getManager(), + fromPubkey: glamClient.getSigner(), toPubkey: glamClient.getTreasuryPDA(fundPDA), lamports: 1_000_000_000, }), SystemProgram.transfer({ - fromPubkey: glamClient.getManager(), + fromPubkey: glamClient.getSigner(), toPubkey: key1.publicKey, lamports: 100_000_000, }), @@ -292,7 +302,7 @@ describe("glam_crud", () => { }); it("Update manager", async () => { - const defaultManager = glamClient.getManager(); + const defaultManager = glamClient.getSigner(); const newManager = Keypair.fromSeed(str2seed("new-manager")); const updatedFund = new FundModel({ @@ -368,7 +378,7 @@ describe("glam_crud", () => { for (const mint of [WSOL, MSOL]) { transaction.add( createAssociatedTokenAccountIdempotentInstruction( - glamClient.getManager(), + glamClient.getSigner(), glamClient.getTreasuryAta(fundPDA, mint), treasury, mint, diff --git a/anchor/tests/glam_drift.spec.ts b/anchor/tests/glam_drift.spec.ts index 79077bbf..eee5aac6 100644 --- a/anchor/tests/glam_drift.spec.ts +++ b/anchor/tests/glam_drift.spec.ts @@ -1,5 +1,5 @@ import * as anchor from "@coral-xyz/anchor"; -import { GlamClient, GlamProgram } from "../src"; +import { DriftMarketConfigs, GlamClient } from "../src"; import { createFundForTest } from "./setup"; import { getOrderParams, @@ -7,23 +7,102 @@ import { OrderType, PositionDirection, } from "@drift-labs/sdk"; -import { PublicKey } from "@solana/web3.js"; +import { jest } from "@jest/globals"; -const SOL_PERP_MARKET = new PublicKey( - "8UJgxaiQx5nTrdDgph5FiahMmzduuLTLf5WmsPegYA6W" -); +const mockPositionsData = { + spotPositions: [ + { + scaledBalance: 8173, + openBids: 0, + openAsks: 0, + cumulativeDeposits: 18446744073705673000, + marketIndex: 0, + balanceType: "Deposit", + openOrders: 0, + cumulativeDepositInterest: "11245879976", + cumulativeBorrowInterest: "12560874142", + balance: "0.000009", + }, + { + scaledBalance: 46, + openBids: 0, + openAsks: 0, + cumulativeDeposits: 18446744073709255000, + marketIndex: 1, + balanceType: "Deposit", + openOrders: 0, + cumulativeDepositInterest: "10370779453", + cumulativeBorrowInterest: "11047388421", + balance: "0.000000047", + }, + ], + perpPositions: [], +}; +// @ts-ignore +global.fetch = jest.fn(() => + Promise.resolve({ + json: () => Promise.resolve(mockPositionsData), + }), +) as jest.Mock; describe("glam_drift", () => { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - const glamClient = new GlamClient(); - const manager = glamClient.getManager(); - - const program = anchor.workspace.Glam as GlamProgram; const commitment = "confirmed"; let fundPDA, treasuryPDA, sharePDA; + const marketConfigs: DriftMarketConfigs = { + orderConstants: { + perpBaseScale: 9, + quoteScale: 6, + }, + perp: [ + { + fullName: "Solana", + categories: ["Solana", "L1", "Infra"], + symbol: "SOL-PERP", + baseAsset: "SOL", + marketIndex: 0, + launchTs: "2022-11-04T11:15:05Z", + oracle: "BAtFj4kQttZRVep3UZS2aZRDixkGYgWsbqTBVDbnSsPF", + oracleSource: "PythPull", + pythPullOraclePDA: "BAtFj4kQttZRVep3UZS2aZRDixkGYgWsbqTBVDbnSsPF", + pythFeedId: + "ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d", + marketPDA: "8UJgxaiQx5nTrdDgph5FiahMmzduuLTLf5WmsPegYA6W", + }, + ], + spot: [ + { + symbol: "USDC", + marketIndex: 0, + decimals: 6, + oracle: "En8hkHLkRe9d9DraYmBTrus518BvmVH448YcvmrFM6Ce", + oracleSource: "PythStableCoinPull", + pythPullOraclePDA: "En8hkHLkRe9d9DraYmBTrus518BvmVH448YcvmrFM6Ce", + pythFeedId: + "eaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a", + marketPDA: "6gMq3mRCKf8aP3ttTyYhuijVZ2LGi14oDsBbkgubfLB3", + mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + vaultPDA: "GXWqPpjQpdz7KZw9p7f5PX2eGxHAhvpNXiviFkAB8zXg", + }, + { + symbol: "SOL", + marketIndex: 1, + decimals: 9, + oracle: "BAtFj4kQttZRVep3UZS2aZRDixkGYgWsbqTBVDbnSsPF", + oracleSource: "PythPull", + pythPullOraclePDA: "BAtFj4kQttZRVep3UZS2aZRDixkGYgWsbqTBVDbnSsPF", + pythFeedId: + "ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d", + marketPDA: "3x85u7SWkmmr7YQGYhtjARgxwegTLJgkSLRprfXod6rh", + mint: "So11111111111111111111111111111111111111112", + serumMarket: "8BnEgHoWFysVcuFFX7QztDmzuH8r5ZFvyP3sYwn1XTh6", + phoenixMarket: "4DoNfFBfF7UokCC2FQzriy7yHK6DY6NVdYpuekQ5pRgg", + openBookMarket: "AFgkED1FUVfBe2trPUDqSqK9QKd4stJrfzq5q1RwAFTa", + vaultPDA: "DfYCNezifxAEsQbAJ1b3j6PX3JVBe8fu11KBhxsbw5d2", + }, + ], + }; it("Create and initialize fund", async () => { const fundData = await createFundForTest(); @@ -31,22 +110,16 @@ describe("glam_drift", () => { treasuryPDA = fundData.treasuryPDA; sharePDA = fundData.sharePDA; - const fund = await program.account.fundAccount.fetch(fundPDA); + const fund = await glamClient.fetchFundAccount(fundPDA); expect(fund.shareClasses.length).toEqual(1); expect(fund.name).toEqual("Glam Fund SOL-mSOL"); // Enable drift integration - const updatedFund = glamClient.getFundModel({ + const updatedFund = { integrationAcls: [{ name: { drift: {} }, features: [] }], - }); + }; try { - const txSig = await glamClient.program.methods - .updateFund(updatedFund) - .accounts({ - fund: fundPDA, - signer: glamClient.getManager(), - }) - .rpc(); + const txSig = await glamClient.fund.updateFund(fundPDA, updatedFund); console.log("Enable drift integration tx:", txSig); } catch (e) { console.error(e); @@ -63,13 +136,13 @@ describe("glam_drift", () => { ...(await connection.getLatestBlockhash()), signature: airdropTx, }, - commitment + commitment, ); try { const txSig = await glamClient.wsol.wrap( fundPDA, - new anchor.BN(lamports) + new anchor.BN(lamports), ); console.log("Wrappped 10 SOL in treasury:", txSig); } catch (e) { @@ -89,7 +162,7 @@ describe("glam_drift", () => { }); it("Drift: update trader", async () => { - const trader = manager; + const trader = glamClient.getSigner(); try { const txId = await glamClient.drift.updateUserDelegate(fundPDA, trader); console.log("driftUpdateUserDelegate", txId); @@ -103,7 +176,13 @@ describe("glam_drift", () => { const amount = new anchor.BN(10_000_000_000); try { - const txId = await glamClient.drift.deposit(fundPDA, amount); + const txId = await glamClient.drift.deposit( + fundPDA, + amount, + 1, + 0, + marketConfigs, + ); console.log("driftDeposit", txId); } catch (e) { @@ -114,9 +193,14 @@ describe("glam_drift", () => { it("Drift: withdraw 1 SOL to trading account", async () => { const amount = new anchor.BN(1_000_000_000); - try { - const txId = await glamClient.drift.withdraw(fundPDA, amount); + const txId = await glamClient.drift.withdraw( + fundPDA, + amount, + 1, + 0, + marketConfigs, + ); console.log("driftWithdraw", txId); } catch (e) { @@ -136,9 +220,12 @@ describe("glam_drift", () => { }); try { - const txId = await glamClient.drift.placeOrder(fundPDA, orderParams, 0, { - perpMarket: SOL_PERP_MARKET, - }); + const txId = await glamClient.drift.placeOrder( + fundPDA, + orderParams, + 0, + marketConfigs, + ); console.log("driftPlaceOrders", txId); } catch (e) { console.error(e); @@ -155,7 +242,7 @@ describe("glam_drift", () => { 0, PositionDirection.LONG, 0, - { perpMarket: SOL_PERP_MARKET } + marketConfigs, ); console.log("driftCancelOrders", txId); @@ -167,7 +254,7 @@ describe("glam_drift", () => { it("Drift: constrain market", async () => { try { - const txId = await glamClient.updateFund(fundPDA, { + const txId = await glamClient.fund.updateFund(fundPDA, { driftMarketIndexesPerp: [2, 3], }); console.log("driftPlaceOrders", txId); @@ -188,9 +275,12 @@ describe("glam_drift", () => { }); try { - const txId = await glamClient.drift.placeOrder(fundPDA, orderParams, 0, { - perpMarket: SOL_PERP_MARKET, - }); + const txId = await glamClient.drift.placeOrder( + fundPDA, + orderParams, + 0, + marketConfigs, + ); expect(txId).toBeUndefined(); } catch (err) { const errMsg = err.message + err.logs; diff --git a/anchor/tests/glam_investor.spec.ts b/anchor/tests/glam_investor.spec.ts index c5fbf71a..60d7d095 100644 --- a/anchor/tests/glam_investor.spec.ts +++ b/anchor/tests/glam_investor.spec.ts @@ -26,6 +26,7 @@ import { GlamClient, WSOL } from "../src"; describe("glam_investor", () => { const glamClient = new GlamClient(); + const wallet = glamClient.getWallet(); const useWsolInsteadOfEth = true; @@ -52,32 +53,29 @@ describe("glam_investor", () => { const BTC_TOKEN_PROGRAM_ID = TOKEN_2022_PROGRAM_ID; const ethOrWsol = useWsolInsteadOfEth ? WSOL : eth.publicKey; - const client = new GlamClient(); - const manager = (client.provider as anchor.AnchorProvider) - .wallet as anchor.Wallet; - const fundModel = { ...testFundModel, name: "Glam Investment", assets: [usdc.publicKey, btc.publicKey, ethOrWsol], + integrationAcls: [{ name: { marinade: {} }, features: [] }], // overwrite share class acls: alice and manager are allowed to subscribe, // bob and eve will be blocked. shareClasses: [ { ...testFundModel.shareClasses![0], - allowlist: [alice.publicKey, bob.publicKey, manager.publicKey], + allowlist: [alice.publicKey, bob.publicKey, wallet.publicKey], blocklist: [bob.publicKey, eve.publicKey], }, ], }; - const fundPDA = client.getFundPDA(fundModel); - const treasuryPDA = client.getTreasuryPDA(fundPDA); - const sharePDA = client.getShareClassPDA(fundPDA, 0); + const fundPDA = glamClient.getFundPDA(fundModel); + const treasuryPDA = glamClient.getTreasuryPDA(fundPDA); + const sharePDA = glamClient.getShareClassPDA(fundPDA); - const connection = client.provider.connection; + const connection = glamClient.provider.connection; const commitment = "confirmed"; - const program = client.program; + const program = glamClient.program; const treasuryUsdcAta = getAssociatedTokenAddressSync( usdc.publicKey, @@ -104,28 +102,28 @@ describe("glam_investor", () => { // manager const managerUsdcAta = getAssociatedTokenAddressSync( usdc.publicKey, - manager.publicKey, + wallet.publicKey, false, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, ); const managerEthAta = getAssociatedTokenAddressSync( ethOrWsol, - manager.publicKey, + wallet.publicKey, false, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, ); const managerBtcAta = getAssociatedTokenAddressSync( btc.publicKey, - manager.publicKey, + wallet.publicKey, false, BTC_TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, ); const managerSharesAta = getAssociatedTokenAddressSync( sharePDA, - manager.publicKey, + wallet.publicKey, false, TOKEN_2022_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, @@ -141,9 +139,9 @@ describe("glam_investor", () => { // exec in parallel, but await before ending the test tokenKeypairs.map(async (token, idx) => { const mint = await createMint( - client.provider.connection, - manager.payer, - manager.publicKey, + glamClient.provider.connection, + wallet.payer, + wallet.publicKey, null, idx == 0 ? 6 : 8, token, @@ -173,7 +171,7 @@ describe("glam_investor", () => { user, token.publicKey, userATA, - manager.payer, + wallet.payer, idx == 2 ? 10_000_000_000 : 1000_000_000, [], {}, @@ -183,19 +181,19 @@ describe("glam_investor", () => { const managerAta = await createAssociatedTokenAccount( connection, - manager.payer, + wallet.payer, token.publicKey, - manager.publicKey, + wallet.publicKey, {}, idx == 2 ? BTC_TOKEN_PROGRAM_ID : TOKEN_PROGRAM_ID, ); await mintTo( connection, - manager.payer, + wallet.payer, token.publicKey, managerAta, - manager.payer, + wallet.payer, idx == 2 ? 1000_000_000_000 : 1000_000_000, [], { commitment }, // await 'confirmed' @@ -214,9 +212,9 @@ describe("glam_investor", () => { // // create fund // - const fundData = await createFundForTest(client, fundModel); - const fund = await program.account.fundAccount.fetch(fundPDA); - expect(fund.shareClasses[0]).toEqual(sharePDA); + const fundData = await createFundForTest(glamClient, fundModel); + const fundAccount = await glamClient.fetchFundAccount(fundData.fundPDA); + expect(fundAccount.shareClasses[0]).toEqual(sharePDA); } catch (e) { console.error(e); throw e; @@ -267,7 +265,7 @@ describe("glam_investor", () => { const keypair = Keypair.generate(); const invalidShareClass = await createMint( connection, - manager.payer, + wallet.payer, keypair.publicKey, null, 6, @@ -277,14 +275,14 @@ describe("glam_investor", () => { ); const shareAta = getAssociatedTokenAddressSync( invalidShareClass, - manager.publicKey, + wallet.publicKey, false, TOKEN_2022_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, ); const txId = await program.methods - .subscribe(new BN(1 * 10 ** 8), true) - .accounts({ + .subscribe(0, new BN(1 * 10 ** 8), true) + .accountsPartial({ fund: fundPDA, treasury: treasuryPDA, shareClass: invalidShareClass, @@ -292,15 +290,15 @@ describe("glam_investor", () => { asset: btc.publicKey, treasuryAta: treasuryEthAta, signerAssetAta: managerEthAta, - signer: manager.publicKey, + signer: wallet.publicKey, tokenProgram: TOKEN_PROGRAM_ID, token2022Program: TOKEN_2022_PROGRAM_ID, }) .preInstructions([ createAssociatedTokenAccountIdempotentInstruction( - manager.publicKey, + wallet.publicKey, shareAta, - manager.publicKey, + wallet.publicKey, invalidShareClass, TOKEN_2022_PROGRAM_ID, ), @@ -353,7 +351,7 @@ describe("glam_investor", () => { commitment, TOKEN_2022_PROGRAM_ID, ); - const amount = new BN(shares.supply / 2n); + const amount = new BN((shares.supply / 2n).toString()); console.log("total shares:", shares.supply, "amount:", amount); try { const txId = await glamClient.investor.redeem(fundPDA, amount, true); @@ -383,11 +381,11 @@ describe("glam_investor", () => { it("Manager adds more tokens and redeems USDC", async () => { // transfer 250 USDC into the treasury (e.g. fees) - const amountExt = new BN(250_000_000); + const amountExt = 250_000_000; try { const tx1 = new Transaction().add( createAssociatedTokenAccountInstruction( - manager.publicKey, + wallet.publicKey, treasuryUsdcAta, treasuryPDA, usdc.publicKey, @@ -396,19 +394,19 @@ describe("glam_investor", () => { managerUsdcAta, usdc.publicKey, treasuryUsdcAta, - manager.publicKey, + wallet.publicKey, amountExt, 6, [], TOKEN_PROGRAM_ID, ), SystemProgram.transfer({ - fromPubkey: manager.publicKey, + fromPubkey: wallet.publicKey, toPubkey: treasuryPDA, lamports: 1_000_000_000, }), ); - await sendAndConfirmTransaction(connection, tx1, [manager.payer], { + await sendAndConfirmTransaction(connection, tx1, [wallet.payer], { skipPreflight: true, commitment, }); diff --git a/anchor/tests/glam_jupiter.spec.ts b/anchor/tests/glam_jupiter.spec.ts index 17a43870..dec0648f 100644 --- a/anchor/tests/glam_jupiter.spec.ts +++ b/anchor/tests/glam_jupiter.spec.ts @@ -24,7 +24,10 @@ describe("glam_jupiter", () => { it("Initialize fund", async () => { const fundData = await createFundForTest(glamClient, { ...testFundModel, - integrationAcls: [{ name: { jupiterVote: {} }, features: [] }], + integrationAcls: [ + { name: { jupiterSwap: {} }, features: [] }, + { name: { jupiterVote: {} }, features: [] }, + ], }); fundPDA = fundData.fundPDA; @@ -42,13 +45,13 @@ describe("glam_jupiter", () => { it("Swap end to end", async () => { const treasury = glamClient.getTreasuryPDA(fundPDA); - const manager = glamClient.getManager(); - const inputSignerAta = glamClient.getManagerAta(WSOL); - const outputSignerAta = glamClient.getManagerAta(MSOL); + const signer = glamClient.getSigner(); + const inputSignerAta = glamClient.getAta(WSOL); + const outputSignerAta = glamClient.getAta(MSOL); const quoteResponse = quoteResponseForTest; const swapInstructions = swapInstructionsForTest( - manager, + signer, inputSignerAta, outputSignerAta, ); @@ -58,8 +61,8 @@ describe("glam_jupiter", () => { await glamClient.provider.connection.getBalance(treasury); expect(beforeTreasuryBalance).toEqual(1_000_000_000); const beforeNoAccounts = [ - glamClient.getManagerAta(WSOL), - glamClient.getManagerAta(MSOL), + glamClient.getAta(WSOL), + glamClient.getAta(MSOL), glamClient.getTreasuryAta(fundPDA, WSOL), glamClient.getTreasuryAta(fundPDA, MSOL), ]; @@ -90,8 +93,8 @@ describe("glam_jupiter", () => { // Post-checks: the following accounts should exist and have 0 balance const afterAccounts = [ - glamClient.getManagerAta(WSOL), - glamClient.getManagerAta(MSOL), + glamClient.getAta(WSOL), + glamClient.getAta(MSOL), glamClient.getTreasuryAta(fundPDA, WSOL), ]; afterAccounts.forEach(async (account) => { @@ -152,7 +155,6 @@ describe("glam_jupiter", () => { .updateFund(updatedFund) .accounts({ fund: fundPDA, - signer: glamClient.getManager(), }) .rpc(); } catch (e) { @@ -229,9 +231,9 @@ describe("glam_jupiter", () => { }, 30_000); it("Swap back end to end", async () => { - const manager = glamClient.getManager(); - const inputSignerAta = glamClient.getManagerAta(MSOL); - const outputSignerAta = glamClient.getManagerAta(WSOL); + const signer = glamClient.getSigner(); + const inputSignerAta = glamClient.getAta(MSOL); + const outputSignerAta = glamClient.getAta(WSOL); const swapInstructions = { tokenLedgerInstruction: null, @@ -289,7 +291,7 @@ describe("glam_jupiter", () => { isWritable: false, }, { - pubkey: manager.toBase58(), + pubkey: signer.toBase58(), isSigner: true, isWritable: false, }, @@ -349,7 +351,7 @@ describe("glam_jupiter", () => { isWritable: false, }, { - pubkey: manager.toBase58(), + pubkey: signer.toBase58(), isSigner: false, isWritable: false, }, @@ -485,7 +487,7 @@ describe("glam_jupiter", () => { ).json(); const swapInstructions = await glamClient.jupiter.getSwapInstructions( quoteResponse, - glamClient.getManager(), + glamClient.getSigner(), ); try { diff --git a/anchor/tests/glam_marinade.spec.ts b/anchor/tests/glam_marinade.spec.ts index 107aeb73..5a1a2cc9 100644 --- a/anchor/tests/glam_marinade.spec.ts +++ b/anchor/tests/glam_marinade.spec.ts @@ -12,6 +12,14 @@ describe("glam_marinade", () => { const fundData = await createFundForTest(glamClient); fundPDA = fundData.fundPDA; + const txSig = await glamClient.fund.updateFund(fundPDA, { + integrationAcls: [ + { name: { marinade: {} }, features: [] }, + { name: { nativeStaking: {} }, features: [] }, + ], + }); + console.log("Marinade integration enabled:", txSig); + const connection = glamClient.provider.connection; const airdropTx = await connection.requestAirdrop( fundData.treasuryPDA, diff --git a/anchor/tests/glam_openfunds.spec.ts b/anchor/tests/glam_openfunds.spec.ts index c2d77c72..56137db9 100644 --- a/anchor/tests/glam_openfunds.spec.ts +++ b/anchor/tests/glam_openfunds.spec.ts @@ -12,7 +12,7 @@ import { describe("glam_openfunds", () => { const glamClient = new GlamClient(); - const manager = glamClient.getManager(); + const manager = glamClient.getSigner(); // fund1: 1 share class + implicit fields const fund1 = { diff --git a/anchor/tests/glam_policy_hook.spec.ts b/anchor/tests/glam_policy_hook.spec.ts index 7ae0141f..1610b6d4 100644 --- a/anchor/tests/glam_policy_hook.spec.ts +++ b/anchor/tests/glam_policy_hook.spec.ts @@ -18,8 +18,6 @@ import { getMint, getAccount, createTransferCheckedWithTransferHookInstruction, - PermanentDelegateLayout, - defaultAccountStateInstructionData, } from "@solana/spl-token"; import { testFundModel, createFundForTest, str2seed, sleep } from "./setup"; @@ -27,6 +25,7 @@ import { GlamClient, WSOL } from "../src"; describe("glam_policy_hook", () => { const glamClient = new GlamClient(); + const wallet = glamClient.getWallet(); const userKeypairs = [ Keypair.generate(), // alice @@ -41,15 +40,10 @@ describe("glam_policy_hook", () => { const usdc = tokenKeypairs[0]; // 6 decimals const btc = tokenKeypairs[1]; // 8 decimals, token2022 const BTC_TOKEN_PROGRAM_ID = TOKEN_2022_PROGRAM_ID; - const ethOrWsol = WSOL; - - const client = new GlamClient(); - const manager = (client.provider as anchor.AnchorProvider) - .wallet as anchor.Wallet; const shareClass = { - ...testFundModel.shareClasses[0], - lockUpPeriodInSeconds: 3, + ...testFundModel.shareClasses![0], + lockUpPeriodInSeconds: 5, lockUpComment: "lock-up test", permanentDelegate: new PublicKey(0), } as any; @@ -60,18 +54,16 @@ describe("glam_policy_hook", () => { shareClasses: [shareClass], } as any; - const fundPDA = client.getFundPDA(fundExample); - const treasuryPDA = client.getTreasuryPDA(fundPDA); - const sharePDA = client.getShareClassPDA(fundPDA, 0); + const fundPDA = glamClient.getFundPDA(fundExample); + const sharePDA = glamClient.getShareClassPDA(fundPDA, 0); - const connection = client.provider.connection; + const connection = glamClient.provider.connection; const commitment = "confirmed"; - const program = client.program; // manager const managerSharesAta = getAssociatedTokenAddressSync( sharePDA, - manager.publicKey, + wallet.publicKey, false, TOKEN_2022_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, @@ -92,9 +84,9 @@ describe("glam_policy_hook", () => { // exec in parallel, but await before ending the test tokenKeypairs.map(async (token, idx) => { const mint = await createMint( - client.provider.connection, - manager.payer, - manager.publicKey, + glamClient.provider.connection, + wallet.payer, + wallet.publicKey, null, idx == 0 ? 6 : 8, token, @@ -124,7 +116,7 @@ describe("glam_policy_hook", () => { user, token.publicKey, userATA, - manager.payer, + wallet.payer, idx == 1 ? 10_000_000_000 : 1000_000_000, [], {}, @@ -134,19 +126,19 @@ describe("glam_policy_hook", () => { const managerAta = await createAssociatedTokenAccount( connection, - manager.payer, + wallet.payer, token.publicKey, - manager.publicKey, + wallet.publicKey, {}, idx == 1 ? BTC_TOKEN_PROGRAM_ID : TOKEN_PROGRAM_ID, ); await mintTo( connection, - manager.payer, + wallet.payer, token.publicKey, managerAta, - manager.payer, + wallet.payer, idx == 1 ? 1000_000_000_000 : 1000_000_000, [], { commitment }, // await 'confirmed' @@ -158,43 +150,20 @@ describe("glam_policy_hook", () => { // // create fund // - const fundData = await createFundForTest(client, fundExample); + const fundData = await createFundForTest(glamClient, fundExample); } catch (e) { console.error(e); throw e; } }, /* timeout */ 15_000); - /*afterAll(async () => { - await program.methods - .close() - .accounts({ - fund: fundPDA, - manager: manager.publicKey - }) - .rpc(); - - // The account should no longer exist, returning null. - const closedAccount = await program.account.fund.fetchNullable(fundPDA); - expect(closedAccount).toBeNull(); - });*/ - it("Fund created", async () => { try { const fund = await glamClient.fetchFund(fundPDA); console.log("fund", fund); - expect(fund.shareClasses[0]?.id).toEqual(sharePDA); - // params - expect(fund.shareClasses[0]?.lockUpPeriodInSeconds).toEqual(new BN(3)); - // mint - expect(fund.shareClasses[0]?.shareClassSymbol).toEqual("GBS"); - expect(fund.shareClasses[0]?.shareClassDecimals).toEqual(9); + expect(fund.shareClasses[0]?.lockUpPeriodInSeconds).toEqual(5); + expect(fund.shareClasses[0]?.symbol).toEqual("GBS"); expect(fund.shareClasses[0]?.permanentDelegate).toEqual(sharePDA); - // computed - expect(fund.shareClasses[0]?.hasPermanentDelegate).toEqual("yes"); - // openfunds - expect(fund.shareClasses[0]?.lockUpPeriodInDays).toEqual("1"); - expect(fund.shareClasses[0]?.lockUpComment).toEqual("lock-up test"); } catch (e) { console.error(e); throw e; @@ -241,7 +210,7 @@ describe("glam_policy_hook", () => { const tx = new Transaction().add( createAssociatedTokenAccountInstruction( - manager.publicKey, + wallet.publicKey, aliceSharesAta, alice.publicKey, sharePDA, @@ -252,7 +221,7 @@ describe("glam_policy_hook", () => { managerSharesAta, sharePDA, aliceSharesAta, - manager.publicKey, + wallet.publicKey, amount, 9, [], @@ -263,7 +232,7 @@ describe("glam_policy_hook", () => { try { const txId = await sendAndConfirmTransaction(connection, tx, [ - manager.payer, + wallet.payer, ]); expect(txId).toBeUndefined(); } catch (err) { @@ -273,7 +242,7 @@ describe("glam_policy_hook", () => { it("Wait of lock-up. Then manager redeems + transfers shares to Alice: both succeed", async () => { console.log("Zzz..."); - await sleep(5_000); + await sleep(10_000); const amount = new BN(10 * 10 ** 9); try { @@ -285,7 +254,7 @@ describe("glam_policy_hook", () => { const tx = new Transaction().add( createAssociatedTokenAccountInstruction( - manager.publicKey, + wallet.publicKey, aliceSharesAta, alice.publicKey, sharePDA, @@ -296,7 +265,7 @@ describe("glam_policy_hook", () => { managerSharesAta, sharePDA, aliceSharesAta, - manager.publicKey, + wallet.publicKey, amount, 9, [], @@ -307,7 +276,7 @@ describe("glam_policy_hook", () => { try { const txId = await sendAndConfirmTransaction(connection, tx, [ - manager.payer, + wallet.payer, ]); console.log("manager transfers shares:", txId); } catch (err) { @@ -320,5 +289,5 @@ describe("glam_policy_hook", () => { } catch (err) { throw err; } - }, 10_000); + }, 15_000); }); diff --git a/anchor/tests/glam_share_class.spec.ts b/anchor/tests/glam_share_class.spec.ts index 8582007f..76e2b90a 100644 --- a/anchor/tests/glam_share_class.spec.ts +++ b/anchor/tests/glam_share_class.spec.ts @@ -23,7 +23,7 @@ describe("glam_share_class", () => { shareClasses: [ { ...testFundModel.shareClasses![0], - allowlist: [glamClient.getManager()], + allowlist: [glamClient.getSigner()], defaultAccountStateFrozen: true, permanentDelegate: new PublicKey(0), // set permanent delegate to share class itself }, @@ -36,7 +36,7 @@ describe("glam_share_class", () => { const fund = await glamClient.fetchFund(fundPDA); expect(fund.shareClasses.length).toEqual(1); - expect(fund.shareClasses[0].allowlist).toEqual([glamClient.getManager()]); + expect(fund.shareClasses[0].allowlist).toEqual([glamClient.getSigner()]); expect(fund.shareClasses[0].blocklist).toEqual([]); }); diff --git a/anchor/tests/glam_sol_msol.spec.ts b/anchor/tests/glam_sol_msol.spec.ts index a112a6d2..bed5df06 100644 --- a/anchor/tests/glam_sol_msol.spec.ts +++ b/anchor/tests/glam_sol_msol.spec.ts @@ -9,13 +9,13 @@ import { } from "@solana/spl-token"; import { - fundTestExample, + testFundModel, createFundForTest, quoteResponseForTest, swapInstructionsForTest, sleep, } from "./setup"; -import { GlamClient, MSOL, WSOL } from "../src"; +import { FundModel, GlamClient, MSOL, WSOL } from "../src"; describe("glam_sol_msol", () => { const glamClient = new GlamClient(); @@ -32,13 +32,18 @@ describe("glam_sol_msol", () => { const glamClientBob = new GlamClient({ wallet: new Wallet(bob) }); const glamClientEve = new GlamClient({ wallet: new Wallet(eve) }); - const fundExample = { - ...fundTestExample, + const testFund = { + ...testFundModel, name: "Glam SOL-mSOL", assets: [WSOL, MSOL], - } as any; - - const fundPDA = glamClient.getFundPDA(fundExample); + integrationAcls: [ + { name: { marinade: {} }, features: [] }, + { name: { jupiterSwap: {} }, features: [] }, + { name: { nativeStaking: {} }, features: [] }, + ], + } as Partial; + + const fundPDA = glamClient.getFundPDA(testFund); const treasuryPDA = glamClient.getTreasuryPDA(fundPDA); const sharePDA = glamClient.getShareClassPDA(fundPDA, 0); @@ -63,7 +68,7 @@ describe("glam_sol_msol", () => { // // create fund // - const fundData = await createFundForTest(glamClient, fundExample); + const fundData = await createFundForTest(glamClient, testFund); // default vote account const voteAccountStatus = await connection.getVoteAccounts(); @@ -80,7 +85,7 @@ describe("glam_sol_msol", () => { it("Fund created", async () => { try { const fund = await glamClient.fetchFundAccount(fundPDA); - expect(fund.name).toEqual(fundExample.name); + expect(fund.name).toEqual(testFund.name); } catch (e) { console.error(e); } @@ -302,13 +307,13 @@ describe("glam_sol_msol", () => { mSOL price is 1.186356194 SOL. The value of the fund is almost unchanged. */ - const manager = glamClient.getManager(); - const inputSignerAta = glamClient.getManagerAta(WSOL); - const outputSignerAta = glamClient.getManagerAta(MSOL); + const signer = glamClient.getSigner(); + const inputSignerAta = glamClient.getAta(WSOL); + const outputSignerAta = glamClient.getAta(MSOL); const quoteResponse = quoteResponseForTest; const swapInstructions = swapInstructionsForTest( - manager, + signer, inputSignerAta, outputSignerAta, ); diff --git a/anchor/tests/glam_staking.spec.ts b/anchor/tests/glam_staking.spec.ts index 2d4e7228..a77412f3 100644 --- a/anchor/tests/glam_staking.spec.ts +++ b/anchor/tests/glam_staking.spec.ts @@ -5,14 +5,14 @@ import { GlamClient } from "../src"; import { PublicKey } from "@solana/web3.js"; const JITO_STAKE_POOL = new PublicKey( - "Jito4APyf642JPZPx3hGc6WWJ8zPKtRbRs4P815Awbb" + "Jito4APyf642JPZPx3hGc6WWJ8zPKtRbRs4P815Awbb", ); const BONK_STAKE_POOL = new PublicKey( - "ArAQfbzsdotoKB5jJcZa3ajQrrPcWr2YQoDAEAiFxJAC" + "ArAQfbzsdotoKB5jJcZa3ajQrrPcWr2YQoDAEAiFxJAC", ); const PHASE_LABS_STAKE_POOL = new PublicKey( - "phasejkG1akKgqkLvfWzWY17evnH6mSWznnUspmpyeG" + "phasejkG1akKgqkLvfWzWY17evnH6mSWznnUspmpyeG", ); describe("glam_staking", () => { @@ -25,7 +25,7 @@ describe("glam_staking", () => { beforeAll(async () => { const voteAccountStatus = await connection.getVoteAccounts(); const vote = voteAccountStatus.current.sort( - (a, b) => b.activatedStake - a.activatedStake + (a, b) => b.activatedStake - a.activatedStake, )[0].votePubkey; defaultVote = new PublicKey(vote); }); @@ -34,9 +34,17 @@ describe("glam_staking", () => { const fundData = await createFundForTest(glamClient); fundPDA = fundData.fundPDA; + const txSig = await glamClient.fund.updateFund(fundPDA, { + integrationAcls: [ + { name: { nativeStaking: {} }, features: [] }, + { name: { splStakePool: {} }, features: [] }, + { name: { sanctumStakePool: {} }, features: [] }, + ], + }); + const airdropTx = await connection.requestAirdrop( fundData.treasuryPDA, - 100_000_000_000 + 100_000_000_000, ); await connection.confirmTransaction({ ...(await connection.getLatestBlockhash()), @@ -49,7 +57,7 @@ describe("glam_staking", () => { const txSig = await glamClient.staking.initializeAndDelegateStake( fundPDA, defaultVote, - new BN(10_000_000_000) + new BN(10_000_000_000), ); console.log("initializeAndDelegateStake tx:", txSig); } catch (e) { @@ -58,7 +66,7 @@ describe("glam_staking", () => { } const stakeAccounts = await glamClient.staking.getStakeAccounts( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); expect(stakeAccounts.length).toEqual(1); }); @@ -68,7 +76,7 @@ describe("glam_staking", () => { await sleep(75_000); let stakeAccounts = await glamClient.staking.getStakeAccountsWithStates( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); expect(stakeAccounts.length).toEqual(1); @@ -77,7 +85,7 @@ describe("glam_staking", () => { const { txSig } = await glamClient.staking.redelegateStake( fundPDA, stakeAccounts[0].address, - new PublicKey("GJQjnyhSG9jN1AdMHTSyTxUR44hJHEGCmNzkidw9z3y8") + new PublicKey("GJQjnyhSG9jN1AdMHTSyTxUR44hJHEGCmNzkidw9z3y8"), ); console.log("redelegateStake tx:", txSig); } catch (e) { @@ -88,30 +96,30 @@ describe("glam_staking", () => { // 2 stake accounts after re-delegation // the existing stake account is not closed by default stakeAccounts = await glamClient.staking.getStakeAccountsWithStates( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); expect(stakeAccounts.length).toEqual(2); }, 90_000); it("Spilt stake account", async () => { let stakeAccounts = await glamClient.staking.getStakeAccountsWithStates( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); try { const { newStake, txSig } = await glamClient.staking.splitStakeAccount( fundPDA, stakeAccounts[0].address, - new BN(2_000_000_000) + new BN(2_000_000_000), ); console.log("splitStakeAccount tx:", txSig); stakeAccounts = await glamClient.staking.getStakeAccountsWithStates( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); expect(stakeAccounts.length).toEqual(3); expect( - stakeAccounts.some((account) => account.address.equals(newStake)) + stakeAccounts.some((account) => account.address.equals(newStake)), ).toBeTruthy(); } catch (e) { console.error(e); @@ -121,7 +129,7 @@ describe("glam_staking", () => { it("Merge stake accounts", async () => { let stakeAccounts = await glamClient.staking.getStakeAccountsWithStates( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); expect(stakeAccounts.length).toEqual(3); @@ -129,7 +137,7 @@ describe("glam_staking", () => { const txId = await glamClient.staking.mergeStakeAccounts( fundPDA, stakeAccounts[0].address, - stakeAccounts[1].address + stakeAccounts[1].address, ); console.log("mergeStakeAccounts tx:", txId); } catch (e) { @@ -139,7 +147,7 @@ describe("glam_staking", () => { // Only 1 stake account should be left after merging stakeAccounts = await glamClient.staking.getStakeAccountsWithStates( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); expect(stakeAccounts.length).toEqual(2); }); @@ -147,17 +155,17 @@ describe("glam_staking", () => { it("[spl-stake-pool] Deposit stake account to jito stake pool", async () => { try { let stakeAccounts = await glamClient.staking.getStakeAccounts( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); const txSig = await glamClient.staking.stakePoolDepositStake( fundPDA, JITO_STAKE_POOL, - stakeAccounts[0] + stakeAccounts[0], ); console.log("stakePoolDepositStake tx:", txSig); stakeAccounts = await glamClient.staking.getStakeAccounts( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); expect(stakeAccounts.length).toEqual(0); } catch (e) { @@ -171,7 +179,7 @@ describe("glam_staking", () => { const txSig = await glamClient.staking.stakePoolDepositSol( fundPDA, JITO_STAKE_POOL, - new BN(10_000_000_000) + new BN(10_000_000_000), ); console.log("stakePoolDepositSol tx:", txSig); } catch (e) { @@ -185,12 +193,12 @@ describe("glam_staking", () => { const txSig = await glamClient.staking.stakePoolWithdrawStake( fundPDA, JITO_STAKE_POOL, - new BN(1_000_000_000) + new BN(1_000_000_000), ); console.log("stakePoolWithdrawStake tx:", txSig); const stakeAccounts = await glamClient.staking.getStakeAccounts( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); expect(stakeAccounts.length).toEqual(1); } catch (e) { @@ -204,7 +212,7 @@ describe("glam_staking", () => { const txSig = await glamClient.staking.stakePoolDepositSol( fundPDA, BONK_STAKE_POOL, - new BN(10_000_000_000) + new BN(10_000_000_000), ); console.log("stakePoolDepositSol tx:", txSig); } catch (e) { @@ -218,13 +226,13 @@ describe("glam_staking", () => { const txSig = await glamClient.staking.stakePoolWithdrawStake( fundPDA, BONK_STAKE_POOL, - new BN(1_000_000_000) + new BN(1_000_000_000), ); console.log("stakePoolWithdrawStake tx:", txSig); // Now we should have 2 stake accounts: 1 from jito and 1 from bonk const stakeAccounts = await glamClient.staking.getStakeAccounts( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); expect(stakeAccounts.length).toEqual(2); } catch (e) { @@ -238,7 +246,7 @@ describe("glam_staking", () => { const txSig = await glamClient.staking.stakePoolDepositSol( fundPDA, PHASE_LABS_STAKE_POOL, - new BN(10_000_000_000) + new BN(10_000_000_000), ); console.log("stakePoolDepositSol tx:", txSig); } catch (e) { @@ -252,12 +260,12 @@ describe("glam_staking", () => { const txSig = await glamClient.staking.stakePoolWithdrawStake( fundPDA, PHASE_LABS_STAKE_POOL, - new BN(1_000_000_000) + new BN(1_000_000_000), ); console.log("stakePoolWithdrawStake tx:", txSig); const stakeAccounts = await glamClient.staking.getStakeAccounts( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); expect(stakeAccounts.length).toEqual(3); } catch (e) { @@ -268,12 +276,12 @@ describe("glam_staking", () => { it("Deactivate stake accounts", async () => { const stakeAccounts = await glamClient.staking.getStakeAccounts( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); try { const txSig = await glamClient.staking.deactivateStakeAccounts( fundPDA, - stakeAccounts + stakeAccounts, ); console.log("deactivateStakeAccounts tx:", txSig); } catch (e) { @@ -284,7 +292,7 @@ describe("glam_staking", () => { it("Withdraw from stake accounts", async () => { const stakeAccounts = await glamClient.staking.getStakeAccounts( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); let lamportsInStakeAccounts = 0; @@ -294,13 +302,13 @@ describe("glam_staking", () => { } const treasuryLamportsBefore = (await connection.getAccountInfo( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ))!.lamports; try { const txSig = await glamClient.staking.withdrawFromStakeAccounts( fundPDA, - stakeAccounts + stakeAccounts, ); console.log("withdrawFromStakeAccounts tx:", txSig); } catch (e) { @@ -309,14 +317,14 @@ describe("glam_staking", () => { } const treasuryLamportsAfter = (await connection.getAccountInfo( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ))!.lamports; expect(treasuryLamportsAfter).toEqual( - treasuryLamportsBefore + lamportsInStakeAccounts + treasuryLamportsBefore + lamportsInStakeAccounts, ); const stakeAccountsAfter = await glamClient.staking.getStakeAccounts( - glamClient.getTreasuryPDA(fundPDA) + glamClient.getTreasuryPDA(fundPDA), ); expect(stakeAccountsAfter.length).toEqual(0); }); diff --git a/anchor/tests/glam_vault.spec.ts b/anchor/tests/glam_vault.spec.ts index 015c4a68..7b31bc3b 100644 --- a/anchor/tests/glam_vault.spec.ts +++ b/anchor/tests/glam_vault.spec.ts @@ -6,7 +6,7 @@ import { } from "@solana/web3.js"; import { BN, Wallet } from "@coral-xyz/anchor"; -import { createFundForTest, fundTestExample, sleep, str2seed } from "./setup"; +import { createFundForTest, testFundModel, str2seed } from "./setup"; import { FundModel, GlamClient, MSOL, USDC, WSOL } from "../src"; import { createMint, @@ -26,7 +26,7 @@ describe("glam_vault", () => { let fundPDA: PublicKey; it("Initialize vault", async () => { - let fundForTest = { ...fundTestExample }; + let fundForTest = { ...testFundModel }; fundForTest.shareClasses = []; const fundData = await createFundForTest(glamClient, fundForTest); @@ -34,32 +34,30 @@ describe("glam_vault", () => { let fund; try { - //@ts-ignore fund = await glamClient.fetchFund(fundPDA); } catch (e) { console.error(e); throw e; } - console.log("fund.shareClasses.length", fund.shareClasses.length); expect(fund.shareClasses.length).toEqual(0); }); it("Update vault name", async () => { - const updatedFund = glamClient.getFundModel({ name: "Updated vault name" }); + const updated = { name: "Updated vault name" }; try { - const txSig = await glamClient.fund.updateFund(fundPDA, updatedFund); + const txSig = await glamClient.fund.updateFund(fundPDA, updated); console.log("Update vault name txSig", txSig); } catch (e) { console.error(e); throw e; } - const fund = await glamClient.program.account.fundAccount.fetch(fundPDA); - expect(fund.name).toEqual(updatedFund.name); + const fundAccount = await glamClient.fetchFundAccount(fundPDA); + expect(fundAccount.name).toEqual(updated.name); }); it("Update vault asset allowlist", async () => { // The test fund has 2 assets, WSOL and MSOL. Update to USDC. - let updatedFund = glamClient.getFundModel({ assets: [USDC] }); + let updatedFund = { assets: [USDC] }; try { const txSig = await glamClient.fund.updateFund(fundPDA, updatedFund); console.log("Update vault assets (USDC) txSig", txSig); @@ -71,7 +69,7 @@ describe("glam_vault", () => { expect(fundModel.assets).toEqual([USDC]); // Update assets back to WSOL and MSOL - updatedFund = glamClient.getFundModel({ assets: [WSOL, MSOL] }); + updatedFund = { assets: [WSOL, MSOL] }; try { const txSig = await glamClient.fund.updateFund(fundPDA, updatedFund); console.log("Update vault assets (WSOL and MSOL) txSig", txSig); @@ -84,9 +82,9 @@ describe("glam_vault", () => { }); it("[integration-acl] add and update", async () => { - const updatedFund1 = glamClient.getFundModel({ + const updatedFund1 = { integrationAcls: [{ name: { drift: {} }, features: [] }], - }); + }; try { const txSig = await glamClient.fund.updateFund(fundPDA, updatedFund1); console.log("Update integration acl txSig", txSig); @@ -98,15 +96,15 @@ describe("glam_vault", () => { expect(fundModel1.integrationAcls.length).toEqual(1); expect(fundModel1.integrationAcls).toEqual(updatedFund1?.integrationAcls); - const updatedFund2 = glamClient.getFundModel({ + const updatedFund2 = { integrationAcls: [ { name: { drift: {} }, features: [] }, - { name: { jupiter: {} }, features: [] }, + { name: { jupiterSwap: {} }, features: [] }, { name: { marinade: {} }, features: [] }, { name: { splStakePool: {} }, features: [] }, { name: { sanctumStakePool: {} }, features: [] }, ], - }); + }; try { const txSig = await glamClient.fund.updateFund(fundPDA, updatedFund2); console.log("Update integration acl txSig", txSig); @@ -201,7 +199,7 @@ describe("glam_vault", () => { // transfer 1 SOL to treasury const tranferTx = new Transaction().add( SystemProgram.transfer({ - fromPubkey: glamClient.getManager(), + fromPubkey: glamClient.getSigner(), toPubkey: glamClient.getTreasuryPDA(fundPDA), lamports: 1_000_000_000, }), @@ -211,7 +209,7 @@ describe("glam_vault", () => { // transfer 0.1 SOL to key1 as it needs to pay for treasury wsol ata creation const tranferTx2 = new Transaction().add( SystemProgram.transfer({ - fromPubkey: glamClient.getManager(), + fromPubkey: glamClient.getSigner(), toPubkey: key1.publicKey, lamports: 100_000_000, }), @@ -219,11 +217,11 @@ describe("glam_vault", () => { await glamClient.sendAndConfirm(tranferTx2); // grant key1 wSolWrap permission - let updatedFund = glamClient.getFundModel({ + let updatedFund = { delegateAcls: [ { pubkey: key1.publicKey, permissions: [{ wSolWrap: {} }] }, ], - }); + }; try { const txSig = await glamClient.fund.updateFund(fundPDA, updatedFund); console.log("Update delegate acl txSig", txSig); @@ -261,10 +259,10 @@ describe("glam_vault", () => { }, 15_000); it("Update fund unauthorized", async () => { - const updatedFund = glamClient.getFundModel({ name: "Updated fund name" }); + const updatedFund = { name: "Updated fund name" }; try { const txId = await glamClient.program.methods - .updateFund(updatedFund) + .updateFund(new FundModel(updatedFund)) .accounts({ fund: fundPDA, signer: key1.publicKey, @@ -278,42 +276,32 @@ describe("glam_vault", () => { }); it("Update manager", async () => { - const manager = glamClient.getManager(); + const manager = glamClient.getSigner(); const newManager = Keypair.generate(); console.log("New manager:", newManager.publicKey); - const updatedFund = glamClient.getFundModel({ - fundManagers: [ - { - name: "New Manager", - pubkey: newManager.publicKey, - // kind: "Wallet" - }, - ], - }); + const updatedFund = { + manager: { + portfolioManagerName: "New Manager", + pubkey: newManager.publicKey, + kind: { wallet: {} }, + }, + }; try { - await glamClient.program.methods - .updateFund(updatedFund) - .accounts({ - fund: fundPDA, - signer: manager, - }) - .rpc(); + await glamClient.fund.updateFund(fundPDA, updatedFund); } catch (e) { console.error(e); throw e; } - let fund = await glamClient.program.account.fundAccount.fetch(fundPDA); + let fund = await glamClient.fetchFundAccount(fundPDA); expect(fund.manager.toString()).toEqual(newManager.publicKey.toString()); - const updatedFund2 = glamClient.getFundModel({ - fundManagers: [ - { - name: "Old Manager", - pubkey: manager, - // kind: "Wallet" - }, - ], + const updatedFund2 = new FundModel({ + manager: { + portfolioManagerName: "Old Manager", + pubkey: manager, + kind: { wallet: {} }, + }, }); // old manager can NOT update back @@ -356,7 +344,7 @@ describe("glam_vault", () => { for (const mint of [WSOL, MSOL]) { transaction.add( createAssociatedTokenAccountIdempotentInstruction( - glamClient.getManager(), + glamClient.getSigner(), glamClient.getTreasuryAta(fundPDA, mint), treasury, mint, @@ -462,9 +450,8 @@ describe("glam_vault", () => { }); it("Close vault", async () => { - const fund = await glamClient.program.account.fundAccount.fetchNullable( - fundPDA, - ); + const fund = + await glamClient.program.account.fundAccount.fetchNullable(fundPDA); expect(fund).not.toBeNull(); try { diff --git a/anchor/tests/glam_wsol.spec.ts b/anchor/tests/glam_wsol.spec.ts index cb510dc4..1fa911ad 100644 --- a/anchor/tests/glam_wsol.spec.ts +++ b/anchor/tests/glam_wsol.spec.ts @@ -12,14 +12,13 @@ describe("glam_wsol", () => { const fundData = await createFundForTest(glamClient); fundPDA = fundData.fundPDA; - const connection = glamClient.provider.connection; // transfer 0.1 SOL to treasury const tranferTx = new Transaction().add( SystemProgram.transfer({ - fromPubkey: glamClient.getManager(), + fromPubkey: glamClient.getSigner(), toPubkey: glamClient.getTreasuryPDA(fundPDA), lamports: 100_000_000, - }) + }), ); await glamClient.sendAndConfirm(tranferTx); }); diff --git a/cli/src/main.ts b/cli/src/main.ts index ef1705cc..a3f637b5 100644 --- a/cli/src/main.ts +++ b/cli/src/main.ts @@ -60,7 +60,7 @@ program .command("env") .description("Show environment setup") .action(async () => { - console.log("Wallet connected:", glamClient.getManager().toBase58()); + console.log("Wallet connected:", glamClient.getSigner().toBase58()); console.log("RPC endpoint:", glamClient.provider.connection.rpcEndpoint); console.log("Priority fee level:", priorityFeeLevel); console.log("Active fund:", fundPDA ? fundPDA.toBase58() : "not set"); diff --git a/package.json b/package.json index 33745a60..5b1f41ea 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "devDependencies": { "@babel/core": "^7.24.7", "@babel/preset-react": "^7.24.7", + "@jest/globals": "^29.7.0", "@nx/eslint": "^19.4.1", "@nx/eslint-plugin": "^19.4.1", "@nx/jest": "^19.4.1", diff --git a/playground/src/app/(mint)/mint/create/createMintForm.tsx b/playground/src/app/(mint)/mint/create/createMintForm.tsx index 429b6809..195b5713 100644 --- a/playground/src/app/(mint)/mint/create/createMintForm.tsx +++ b/playground/src/app/(mint)/mint/create/createMintForm.tsx @@ -135,7 +135,7 @@ export default function MultiStepForm() { fundGroupName: openfundsData.company.fundGroupName, } as Partial, manager: { - pubkey: glamClient.getManager(), + pubkey: glamClient.getSigner(), kind: { wallet: {} }, } as Partial, shareClasses: [ diff --git a/playground/src/app/(vault)/vault/trade/page.tsx b/playground/src/app/(vault)/vault/trade/page.tsx index 1311fdaa..be425e7a 100644 --- a/playground/src/app/(vault)/vault/trade/page.tsx +++ b/playground/src/app/(vault)/vault/trade/page.tsx @@ -642,7 +642,8 @@ export default function Trade() { if (!validated) { return; } - const { orderType, direction, size, price, marketConfig } = validated; + const { orderType, direction, size, price } = validated; + const marketConfig = validated.marketConfig as SpotMarketConfig; const orderParams = getOrderParams({ orderType, diff --git a/playground/src/hooks/useTokenList.ts b/playground/src/hooks/useTokenList.ts deleted file mode 100644 index 85eb619e..00000000 --- a/playground/src/hooks/useTokenList.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { useEffect, useState, useCallback, useMemo } from "react"; -import { JupTokenListItem, useGlam } from "@glam/anchor/react"; - -export interface TokenData { - value: string; // mint address - label: string; // token symbol - symbol: string; - name: string; - address: string; - logoURI?: string; - [key: string]: any; -} - -const CACHE_KEY = "tokenListCache"; -const CACHE_VERSION = 1; - -export function useTokenList() { - const { jupTokenList } = useGlam(); - const [isLoading, setIsLoading] = useState(true); - const [processedTokens, setProcessedTokens] = useState([]); - - const getCachedTokens = useCallback(() => { - if (typeof window === "undefined") return null; - const cached = localStorage.getItem(CACHE_KEY); - if (!cached) return null; - const { version, data } = JSON.parse(cached); - return version === CACHE_VERSION ? data : null; - }, []); - - const setCachedTokens = useCallback((tokens: TokenData[]) => { - localStorage.setItem( - CACHE_KEY, - JSON.stringify({ version: CACHE_VERSION, data: tokens }), - ); - }, []); - - const processTokens = useCallback( - (tokens: JupTokenListItem[]): TokenData[] => { - return tokens.map((token) => ({ - value: token.address, - label: token.symbol, - ...token, - })); - }, - [], - ); - - useEffect(() => { - const loadTokens = async () => { - const cachedTokens = getCachedTokens(); - - if (cachedTokens) { - setProcessedTokens(cachedTokens); - setIsLoading(false); - } else if (jupTokenList && jupTokenList.length > 0) { - const processed = processTokens(jupTokenList); - setProcessedTokens(processed); - setCachedTokens(processed); - setIsLoading(false); - } - }; - - loadTokens(); - }, [jupTokenList, getCachedTokens, setCachedTokens, processTokens]); - - const tokens = useMemo(() => processedTokens, [processedTokens]); - - return { - tokens, - isLoaded: !isLoading, - }; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee5eeb05..ed83baea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -244,6 +244,9 @@ importers: '@babel/preset-react': specifier: ^7.24.7 version: 7.24.7(@babel/core@7.24.7) + '@jest/globals': + specifier: ^29.7.0 + version: 29.7.0 '@nx/eslint': specifier: ^19.4.1 version: 19.4.1(@babel/traverse@7.24.7)(@swc-node/register@1.10.2(@swc/core@1.6.13(@swc/helpers@0.5.11))(@swc/types@0.1.9)(typescript@5.5.3))(@swc/core@1.6.13(@swc/helpers@0.5.11))(@types/node@18.19.39)(@zkochan/js-yaml@0.0.7)(eslint@8.57.0)(nx@19.4.1(@swc-node/register@1.10.2(@swc/core@1.6.13(@swc/helpers@0.5.11))(@swc/types@0.1.9)(typescript@5.5.3))(@swc/core@1.6.13(@swc/helpers@0.5.11))) @@ -345,7 +348,7 @@ importers: version: 9.1.0(eslint@8.57.0) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + version: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-jsx-a11y: specifier: ^6.9.0 version: 6.9.0(eslint@8.57.0) @@ -4445,8 +4448,8 @@ packages: '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} - '@types/node-fetch@2.6.11': - resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} + '@types/node-fetch@2.6.12': + resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} '@types/node-forge@1.3.11': resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} @@ -13119,12 +13122,6 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - '@nrwl/devkit@19.0.0(nx@19.0.0(@swc-node/register@1.10.2(@swc/core@1.6.13(@swc/helpers@0.5.11))(@swc/types@0.1.9)(typescript@5.5.3))(@swc/core@1.6.13(@swc/helpers@0.5.11)))': - dependencies: - '@nx/devkit': 19.0.0(nx@19.0.0(@swc-node/register@1.10.2(@swc/core@1.6.13(@swc/helpers@0.5.11))(@swc/types@0.1.9)(typescript@5.5.3))(@swc/core@1.6.13(@swc/helpers@0.5.11))) - transitivePeerDependencies: - - nx - '@nrwl/devkit@19.0.0(nx@19.4.1(@swc-node/register@1.10.2(@swc/core@1.6.13(@swc/helpers@0.5.11))(@swc/types@0.1.9)(typescript@5.5.3))(@swc/core@1.6.13(@swc/helpers@0.5.11)))': dependencies: '@nx/devkit': 19.0.0(nx@19.4.1(@swc-node/register@1.10.2(@swc/core@1.6.13(@swc/helpers@0.5.11))(@swc/types@0.1.9)(typescript@5.5.3))(@swc/core@1.6.13(@swc/helpers@0.5.11))) @@ -13580,7 +13577,7 @@ snapshots: '@nx/devkit@19.0.0(nx@19.0.0(@swc-node/register@1.10.2(@swc/core@1.6.13(@swc/helpers@0.5.11))(@swc/types@0.1.9)(typescript@5.5.3))(@swc/core@1.6.13(@swc/helpers@0.5.11)))': dependencies: - '@nrwl/devkit': 19.0.0(nx@19.0.0(@swc-node/register@1.10.2(@swc/core@1.6.13(@swc/helpers@0.5.11))(@swc/types@0.1.9)(typescript@5.5.3))(@swc/core@1.6.13(@swc/helpers@0.5.11))) + '@nrwl/devkit': 19.0.0(nx@19.4.1(@swc-node/register@1.10.2(@swc/core@1.6.13(@swc/helpers@0.5.11))(@swc/types@0.1.9)(typescript@5.5.3))(@swc/core@1.6.13(@swc/helpers@0.5.11))) ejs: 3.1.10 enquirer: 2.3.6 ignore: 5.3.1 @@ -16850,7 +16847,7 @@ snapshots: '@solana/web3.js': 1.95.3(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@types/bn.js': 5.1.6 '@types/node': 18.19.39 - '@types/node-fetch': 2.6.11 + '@types/node-fetch': 2.6.12 bn.js: 5.2.1 decimal.js: 10.4.3 typescript: 4.9.5 @@ -17746,7 +17743,7 @@ snapshots: '@types/ms@0.7.34': {} - '@types/node-fetch@2.6.11': + '@types/node-fetch@2.6.12': dependencies: '@types/node': 18.19.39 form-data: 4.0.0 @@ -20311,8 +20308,8 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.5.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.34.3(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -20334,13 +20331,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 4.3.5 enhanced-resolve: 5.17.0 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.14.0 @@ -20351,18 +20348,18 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.5.3) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -20372,7 +20369,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.14.0 is-glob: 4.0.3