diff --git a/anchor/Anchor.toml b/anchor/Anchor.toml index 24628c0d..75509f0f 100644 --- a/anchor/Anchor.toml +++ b/anchor/Anchor.toml @@ -25,8 +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_mint" #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" #test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_marinade" @@ -34,7 +33,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_sol_msol" #test = "../node_modules/.bin/nx run --skip-nx-cache anchor:jest --verbose --testPathPattern tests/ --testNamePattern glam_policy_hook" [test] diff --git a/anchor/libs/macros/src/lib.rs b/anchor/libs/macros/src/lib.rs index 160b8c06..0c424f9d 100644 --- a/anchor/libs/macros/src/lib.rs +++ b/anchor/libs/macros/src/lib.rs @@ -40,7 +40,7 @@ pub fn share_class_signer_seeds(_attr: TokenStream, item: TokenStream) -> TokenS // We assume the fund account and the treasury bump seed are available in the context let state_key = ctx.accounts.state.key(); let seeds = &[ - "share".as_bytes(), + "mint".as_bytes(), &[share_class_id], state_key.as_ref(), &[ctx.bumps.share_class_mint], @@ -71,7 +71,7 @@ pub fn vault_signer_seeds(_attr: TokenStream, item: TokenStream) -> TokenStream // We assume the fund account and the vault bump seed are available in the context let state_key = ctx.accounts.state.key(); let seeds = [ - "treasury".as_ref(), + "vault".as_ref(), state_key.as_ref(), &[ctx.bumps.vault], ]; diff --git a/anchor/programs/glam/src/constants.rs b/anchor/programs/glam/src/constants.rs index f94eba8c..0f3c63a6 100644 --- a/anchor/programs/glam/src/constants.rs +++ b/anchor/programs/glam/src/constants.rs @@ -2,24 +2,24 @@ use anchor_lang::prelude::*; use solana_program::pubkey; #[constant] -pub const SEED_STATE: &str = "fund"; +pub const SEED_STATE: &str = "state"; #[constant] -pub const SEED_VAULT: &str = "treasury"; +pub const SEED_VAULT: &str = "vault"; #[constant] -pub const SEED_METADATA: &str = "openfunds"; +pub const SEED_METADATA: &str = "metadata"; #[constant] -pub const SEED_MINT: &str = "share"; +pub const SEED_MINT: &str = "mint"; pub const DEFAULT_DRIFT_USER_NAME: [u8; 32] = [ b'G', b'L', b'A', b'M', b' ', b'*', b'.', b'+', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; -pub const MAX_ASSETS: usize = 5; -pub const MAX_SHARE_CLASSES: usize = 3; -pub const MAX_SIZE_NAME: usize = 50; -pub const MAX_SIZE_SYMBOL: usize = 20; -pub const MAX_SIZE_URI: usize = 100; +pub const MAX_ASSETS: usize = 100; +pub const MAX_MINTS: usize = 1; +pub const MAX_SIZE_SYMBOL: usize = 32; +pub const MAX_SIZE_NAME: usize = 64; +pub const MAX_SIZE_URI: usize = 128; pub const WSOL: Pubkey = pubkey!("So11111111111111111111111111111111111111112"); pub const MSOL: Pubkey = pubkey!("mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So"); diff --git a/anchor/programs/glam/src/error.rs b/anchor/programs/glam/src/error.rs index 92bc12d0..44fdb2dc 100644 --- a/anchor/programs/glam/src/error.rs +++ b/anchor/programs/glam/src/error.rs @@ -7,17 +7,23 @@ pub enum AccessError { #[msg("Integration is disabled")] IntegrationDisabled, + + #[msg("Wrong state account type")] + WrongStateType, } #[error_code] pub enum StateError { - #[msg("Name too long: max 50 chars")] + #[msg("Invalid account type")] + InvalidAccountType, + + #[msg("Name too long: max 64 chars")] InvalidName, - #[msg("Symbol too long: max 50 chars")] + #[msg("Symbol too long: max 32 chars")] InvalidSymbol, - #[msg("Uri too long: max 20")] + #[msg("Uri too long: max 128 chars")] InvalidUri, #[msg("Too many assets: max 100")] diff --git a/anchor/programs/glam/src/instructions/drift.rs b/anchor/programs/glam/src/instructions/drift.rs index fcf0293f..d550ab41 100644 --- a/anchor/programs/glam/src/instructions/drift.rs +++ b/anchor/programs/glam/src/instructions/drift.rs @@ -46,7 +46,7 @@ pub struct DriftInitialize<'info> { } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::DriftInitialize))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Drift))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Drift))] #[vault_signer_seeds] pub fn initialize_handler(ctx: Context) -> Result<()> { initialize_user_stats(CpiContext::new_with_signer( @@ -102,7 +102,7 @@ pub struct DriftUpdate<'info> { } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::DriftUpdateUser))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Drift))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Drift))] #[vault_signer_seeds] pub fn update_user_custom_margin_ratio_handler( ctx: Context, @@ -126,7 +126,7 @@ pub fn update_user_custom_margin_ratio_handler( } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::DriftUpdateUser))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Drift))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Drift))] #[vault_signer_seeds] pub fn update_user_margin_trading_enabled_handler( ctx: Context, @@ -150,7 +150,7 @@ pub fn update_user_margin_trading_enabled_handler( } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::DriftUpdateUser))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Drift))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Drift))] #[vault_signer_seeds] pub fn update_user_delegate_handler( ctx: Context, @@ -204,7 +204,7 @@ pub struct DriftDeposit<'info> { } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::DriftDeposit))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Drift))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Drift))] #[vault_signer_seeds] pub fn deposit_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftDeposit<'info>>, @@ -267,7 +267,7 @@ pub struct DriftWithdraw<'info> { } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::DriftWithdraw))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Drift))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Drift))] #[vault_signer_seeds] pub fn withdraw_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftWithdraw<'info>>, @@ -324,7 +324,7 @@ pub struct DriftDeleteUser<'info> { } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::DriftDeleteUser))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Drift))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Drift))] #[vault_signer_seeds] pub fn delete_user_handler(ctx: Context) -> Result<()> { delete_user(CpiContext::new_with_signer( @@ -365,7 +365,7 @@ pub struct DriftPlaceOrders<'info> { } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::DriftPlaceOrders))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Drift))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Drift))] #[vault_signer_seeds] pub fn place_orders_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftPlaceOrders<'info>>, @@ -452,7 +452,7 @@ pub struct DriftCancelOrders<'info> { } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::DriftCancelOrders))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Drift))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Drift))] #[vault_signer_seeds] pub fn cancel_orders_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftCancelOrders<'info>>, diff --git a/anchor/programs/glam/src/instructions/investor.rs b/anchor/programs/glam/src/instructions/investor.rs index 15bf83d4..a639d685 100644 --- a/anchor/programs/glam/src/instructions/investor.rs +++ b/anchor/programs/glam/src/instructions/investor.rs @@ -8,6 +8,7 @@ use anchor_spl::token::Token; use anchor_spl::token_interface::{ burn, mint_to, transfer_checked, Burn, Mint, MintTo, Token2022, TokenAccount, TransferChecked, }; +use glam_macros::share_class_signer_seeds; use glam_macros::vault_signer_seeds; use marinade::state::delayed_unstake_ticket::TicketAccountData; use pyth_solana_receiver_sdk::price_update::Price; @@ -24,7 +25,7 @@ fn log_decimal(amount: u64, minus_decimals: i32) -> f64 { } #[derive(Accounts)] -#[instruction(_share_class_id: u8)] +#[instruction(share_class_id: u8)] pub struct Subscribe<'info> { #[account()] pub state: Box>, @@ -35,16 +36,16 @@ pub struct Subscribe<'info> { // the shares to mint #[account( mut, - seeds = [SEED_MINT.as_bytes(), &[_share_class_id], state.key().as_ref()], + seeds = [SEED_MINT.as_bytes(), &[share_class_id], state.key().as_ref()], bump, - mint::authority = share_class, + mint::authority = share_class_mint, mint::token_program = token_2022_program )] - pub share_class: Box>, + pub share_class_mint: Box>, #[account( mut, - associated_token::mint = share_class, + associated_token::mint = share_class_mint, associated_token::authority = signer, associated_token::token_program = token_2022_program )] @@ -83,14 +84,15 @@ pub struct Subscribe<'info> { pub token_2022_program: Program<'info, Token2022>, } +#[share_class_signer_seeds] pub fn subscribe_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, Subscribe<'info>>, - _share_class_id: u8, + share_class_id: u8, amount: u64, skip_state: bool, ) -> Result<()> { let state = &ctx.accounts.state; - require!(state.is_enabled(), StateError::Disabled); + require!(state.enabled, StateError::Disabled); let external_vault_accounts = state.get_pubkeys_from_engine_field(EngineFieldName::ExternalVaultAccounts); @@ -107,7 +109,7 @@ pub fn subscribe_handler<'c: 'info, 'info>( } require!(state.mints.len() > 0, StateError::NoShareClass); require!( - state.mints[0] == ctx.accounts.share_class.key(), + state.mints[0] == ctx.accounts.share_class_mint.key(), InvestorError::InvalidShareClass ); @@ -154,7 +156,7 @@ pub fn subscribe_handler<'c: 'info, 'info>( } } - let state_assets = state.assets().unwrap(); + let state_assets = &state.assets; let asset_idx = state_assets .iter() .position(|&asset| asset == ctx.accounts.asset.key()); @@ -168,14 +170,14 @@ pub fn subscribe_handler<'c: 'info, 'info>( // // Compute amount of shares to mint // - let share_class = &ctx.accounts.share_class; + let share_class = &ctx.accounts.share_class_mint; let share_expo = -(share_class.decimals as i32); let total_shares = share_class.supply; let use_fixed_price = total_shares == 0; let aum_components = get_aum_components( Action::Subscribe, - state_assets, + &state_assets, ctx.remaining_accounts, &ctx.accounts.vault, &external_vault_accounts, @@ -256,23 +258,16 @@ pub fn subscribe_handler<'c: 'info, 'info>( if skip_state { // TODO: we should read share class symbol from metadata so that we don't need to pass it as an argument // mint shares to signer - let state_key = ctx.accounts.state.key(); - let seeds = &[ - "share".as_bytes(), - &[0u8], - state_key.as_ref(), - &[ctx.bumps.share_class], - ]; - let signer_seeds = &[&seeds[..]]; + mint_to( CpiContext::new_with_signer( ctx.accounts.token_2022_program.to_account_info(), MintTo { - authority: ctx.accounts.share_class.to_account_info(), + authority: ctx.accounts.share_class_mint.to_account_info(), to: ctx.accounts.signer_share_ata.to_account_info(), - mint: ctx.accounts.share_class.to_account_info(), + mint: ctx.accounts.share_class_mint.to_account_info(), }, - signer_seeds, + share_class_signer_seeds, ), amount_shares, )?; @@ -325,7 +320,7 @@ pub fn redeem_handler<'c: 'info, 'info>( skip_state: bool, ) -> Result<()> { let state = &ctx.accounts.state; - require!(state.is_enabled(), StateError::Disabled); + require!(state.enabled, StateError::Disabled); let external_vault_accounts = state.get_pubkeys_from_engine_field(EngineFieldName::ExternalVaultAccounts); @@ -397,11 +392,11 @@ pub fn redeem_handler<'c: 'info, 'info>( share_expo, ); - let assets = state.assets().unwrap(); + let assets = &state.assets; let skip_prices = should_transfer_everything || in_kind; let aum_components = get_aum_components( Action::Redeem, - assets, + &assets, ctx.remaining_accounts, &ctx.accounts.vault, &external_vault_accounts, diff --git a/anchor/programs/glam/src/instructions/jupiter.rs b/anchor/programs/glam/src/instructions/jupiter.rs index 4df85871..6d1f0794 100644 --- a/anchor/programs/glam/src/instructions/jupiter.rs +++ b/anchor/programs/glam/src/instructions/jupiter.rs @@ -189,7 +189,7 @@ fn is_lst<'info>(mint: &Pubkey, stake_pool_account: Option<&AccountInfo<'info>>) } #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::JupiterSwap) + acl::check_integration(&ctx.accounts.state, Integration::JupiterSwap) )] #[vault_signer_seeds] pub fn swap_handler<'c: 'info, 'info>( @@ -197,12 +197,11 @@ pub fn swap_handler<'c: 'info, 'info>( amount: u64, data: Vec, ) -> Result<()> { - let state = ctx.accounts.state.clone(); - let assets = ctx.accounts.state.assets_mut().unwrap(); + let state = &mut ctx.accounts.state; // Check if input and output mints are in the assets allowlist - let input_in_assets = assets.contains(&ctx.accounts.input_mint.key()); - let output_in_assets = assets.contains(&ctx.accounts.output_mint.key()); + let input_in_assets = state.assets.contains(&ctx.accounts.input_mint.key()); + let output_in_assets = state.assets.contains(&ctx.accounts.output_mint.key()); let input_is_lst = is_lst( &ctx.accounts.input_mint.key(), @@ -228,10 +227,10 @@ pub fn swap_handler<'c: 'info, 'info>( // TODO: should we add missing assets to the list after permission check? // This will gradually expand the assets allowlist and auto escalate JupiterSwapAllowlisted privilege over time if !input_in_assets { - assets.push(ctx.accounts.input_mint.key()); + state.assets.push(ctx.accounts.input_mint.key()); } if !output_in_assets { - assets.push(ctx.accounts.output_mint.key()); + state.assets.push(ctx.accounts.output_mint.key()); } // Parse Jupiter Swap accounts @@ -332,7 +331,7 @@ pub struct InitLockedVoterEscrow<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::StakeJup) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::JupiterVote) + acl::check_integration(&ctx.accounts.state, Integration::JupiterVote) )] #[vault_signer_seeds] pub fn init_locked_voter_escrow_handler<'info>(ctx: Context) -> Result<()> { @@ -379,7 +378,7 @@ pub struct ToogleMaxLock<'info> { vec![Permission::StakeJup, Permission::UnstakeJup]) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::JupiterVote) + acl::check_integration(&ctx.accounts.state, Integration::JupiterVote) )] #[vault_signer_seeds] pub fn toggle_max_lock_handler<'info>(ctx: Context, value: bool) -> Result<()> { @@ -430,7 +429,7 @@ pub struct IncreaseLockedAmount<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::StakeJup) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::JupiterVote) + acl::check_integration(&ctx.accounts.state, Integration::JupiterVote) )] #[vault_signer_seeds] pub fn increase_locked_amount_handler<'info>( @@ -485,7 +484,7 @@ pub struct PartialUnstaking<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::UnstakeJup) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::JupiterVote) + acl::check_integration(&ctx.accounts.state, Integration::JupiterVote) )] #[vault_signer_seeds] pub fn open_partial_unstaking_handler<'info>( @@ -515,7 +514,7 @@ pub fn open_partial_unstaking_handler<'info>( acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::UnstakeJup) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::JupiterVote) + acl::check_integration(&ctx.accounts.state, Integration::JupiterVote) )] #[vault_signer_seeds] pub fn merge_partial_unstaking_handler<'info>(ctx: Context) -> Result<()> { @@ -563,7 +562,7 @@ pub struct WithdrawAllStakedJup<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::UnstakeJup) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::JupiterVote) + acl::check_integration(&ctx.accounts.state, Integration::JupiterVote) )] #[vault_signer_seeds] pub fn withdraw_all_staked_jup_handler<'info>(ctx: Context) -> Result<()> { @@ -618,7 +617,7 @@ pub struct WithdrawPartialUnstaking<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::UnstakeJup) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::JupiterVote) + acl::check_integration(&ctx.accounts.state, Integration::JupiterVote) )] #[vault_signer_seeds] pub fn withdraw_partial_unstaking_handler<'info>( @@ -668,7 +667,7 @@ pub struct NewVote<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::VoteOnProposal) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::JupiterVote) + acl::check_integration(&ctx.accounts.state, Integration::JupiterVote) )] #[vault_signer_seeds] pub fn new_vote_handler<'info>(ctx: Context) -> Result<()> { @@ -723,7 +722,7 @@ pub struct CastVote<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::VoteOnProposal) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::JupiterVote) + acl::check_integration(&ctx.accounts.state, Integration::JupiterVote) )] #[vault_signer_seeds] pub fn cast_vote_handler<'info>(ctx: Context, side: u8) -> Result<()> { diff --git a/anchor/programs/glam/src/instructions/marinade.rs b/anchor/programs/glam/src/instructions/marinade.rs index 2321ed5f..07518700 100644 --- a/anchor/programs/glam/src/instructions/marinade.rs +++ b/anchor/programs/glam/src/instructions/marinade.rs @@ -14,7 +14,7 @@ use marinade::state::delayed_unstake_ticket::TicketAccountData; #[access_control( acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::Stake) )] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Marinade))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Marinade))] #[vault_signer_seeds] pub fn marinade_deposit_sol_handler<'c: 'info, 'info>( ctx: Context, @@ -45,7 +45,7 @@ pub fn marinade_deposit_sol_handler<'c: 'info, 'info>( #[access_control( acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::Stake) )] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Marinade))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Marinade))] #[vault_signer_seeds] pub fn marinade_deposit_stake_handler<'c: 'info, 'info>( ctx: Context, @@ -78,7 +78,7 @@ pub fn marinade_deposit_stake_handler<'c: 'info, 'info>( #[access_control( acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::Unstake) )] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Marinade))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Marinade))] #[vault_signer_seeds] pub fn delayed_unstake_handler<'c: 'info, 'info>( ctx: Context, @@ -142,7 +142,7 @@ pub fn delayed_unstake_handler<'c: 'info, 'info>( #[access_control( acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::Unstake) )] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Marinade))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Marinade))] #[vault_signer_seeds] pub fn claim_tickets_handler<'info>( ctx: Context<'_, '_, '_, 'info, MarinadeClaimTickets<'info>>, @@ -180,7 +180,7 @@ pub fn claim_tickets_handler<'info>( #[access_control( acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::LiquidUnstake) )] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Marinade))] +#[access_control(acl::check_integration(&ctx.accounts.state, Integration::Marinade))] #[vault_signer_seeds] pub fn liquid_unstake_handler<'c: 'info, 'info>( ctx: Context, diff --git a/anchor/programs/glam/src/instructions/share_class.rs b/anchor/programs/glam/src/instructions/share_class.rs index 7592bc47..fd2aab3b 100644 --- a/anchor/programs/glam/src/instructions/share_class.rs +++ b/anchor/programs/glam/src/instructions/share_class.rs @@ -57,8 +57,8 @@ pub struct AddShareClass<'info> { #[account(mut, constraint = state.owner == signer.key() @ AccessError::NotAuthorized)] pub state: Box>, - #[account(mut)] - pub metadata: Box>, + #[account(mut, seeds = [SEED_METADATA.as_bytes(), state.key().as_ref()], bump)] + pub openfunds_metadata: Option>>, #[account(mut)] pub signer: Signer<'info>, @@ -69,7 +69,7 @@ pub struct AddShareClass<'info> { pub fn add_share_class_handler<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, AddShareClass<'info>>, - share_class_metadata: ShareClassModel, + share_class_model: ShareClassModel, ) -> Result<()> { // // Add share class to state @@ -86,17 +86,17 @@ pub fn add_share_class_handler<'c: 'info, 'info>( EngineField { name: EngineFieldName::ShareClassAllowlist, value: EngineFieldValue::VecPubkey { - val: share_class_metadata.allowlist.clone(), + val: share_class_model.clone().allowlist.unwrap_or_default(), }, }, EngineField { name: EngineFieldName::ShareClassBlocklist, value: EngineFieldValue::VecPubkey { - val: share_class_metadata.blocklist.clone(), + val: share_class_model.clone().blocklist.unwrap_or_default(), }, }, ]; - let share_class_metadata = &mut share_class_metadata.clone(); + let share_class_metadata = &mut share_class_model.clone(); let mut raw_openfunds = share_class_metadata .raw_openfunds .clone() @@ -109,24 +109,30 @@ pub fn add_share_class_handler<'c: 'info, 'info>( // Output: // - has_lock_up_for_redemption (openfunds) // - lock_up_period_in_days (openfunds) - let policy_has_lock_up = share_class_metadata.lock_up_period_in_seconds > 0; - if policy_has_lock_up { - share_class_params.push(EngineField { - name: EngineFieldName::LockUp, - value: EngineFieldValue::Timestamp { - // lock_up_period_in_seconds is i32 so it's easier to use in js, - // we can express it as a number instead of requiring BN. - // the max lock up is 24k+ days, so it should be good. - val: share_class_metadata.lock_up_period_in_seconds.into(), - }, - }); - raw_openfunds.lock_up_period_in_days = - Some((1 + share_class_metadata.lock_up_period_in_seconds / 24 * 60 * 60).to_string()); - } else { - raw_openfunds.lock_up_period_in_days = None; - raw_openfunds.lock_up_comment = None; + + let mut transfer_hook_active = false; + if let Some(lock_up_period_in_seconds) = share_class_metadata.lock_up_period_in_seconds { + let policy_has_lock_up = lock_up_period_in_seconds > 0; + transfer_hook_active = policy_has_lock_up; + + if policy_has_lock_up { + share_class_params.push(EngineField { + name: EngineFieldName::LockUp, + value: EngineFieldValue::Timestamp { + // lock_up_period_in_seconds is i32 so it's easier to use in js, + // we can express it as a number instead of requiring BN. + // the max lock up is 24k+ days, so it should be good. + val: lock_up_period_in_seconds.into(), + }, + }); + raw_openfunds.lock_up_period_in_days = + Some((1 + lock_up_period_in_seconds / 24 * 60 * 60).to_string()); + } else { + raw_openfunds.lock_up_period_in_days = None; + raw_openfunds.lock_up_comment = None; + } + raw_openfunds.has_lock_up_for_redemption = Some(policy_has_lock_up); } - raw_openfunds.has_lock_up_for_redemption = Some(policy_has_lock_up); share_class_metadata.raw_openfunds = Some(raw_openfunds); state.params.push(share_class_params); @@ -134,10 +140,17 @@ pub fn add_share_class_handler<'c: 'info, 'info>( let share_class_fields = Vec::::from(&share_class_metadata.clone()); // - // Add share class data to openfunds + // Add share class data metadata, currently only openfunds metadata is supported // - let openfunds = &mut ctx.accounts.metadata; - openfunds.share_classes.push(share_class_fields.clone()); + if let Some(metadata) = state.metadata.clone() { + if metadata.template == MetadataTemplate::Openfunds { + if let Some(openfunds_metadata) = &mut ctx.accounts.openfunds_metadata { + openfunds_metadata + .share_classes + .push(share_class_fields.clone()); + } + } + } // // Initialize share class mint, extensions and metadata @@ -151,17 +164,18 @@ pub fn add_share_class_handler<'c: 'info, 'info>( other => other, }; - let default_account_state_frozen = share_class_metadata.default_account_state_frozen; + let default_account_state_frozen = share_class_metadata + .default_account_state_frozen + .unwrap_or(false); let seeds = &[ - "share".as_bytes(), + SEED_MINT.as_bytes(), &[share_class_idx], state_key.as_ref(), &[ctx.bumps.share_class_mint], ]; let signer_seeds = &[&seeds[..]]; - let transfer_hook_active = policy_has_lock_up; let mut extension_types = vec![ ExtensionType::MetadataPointer, // always present ExtensionType::MintCloseAuthority, // always present @@ -175,6 +189,7 @@ pub fn add_share_class_handler<'c: 'info, 'info>( let metadata_space = 1024; let lamports_required = (Rent::get()?).minimum_balance(space + metadata_space); + #[cfg(not(feature = "mainnet"))] msg!( "Create Mint and metadata account size and cost: {} lamports: {}", space as u64, @@ -360,7 +375,7 @@ pub struct SetTokenAccountsStates<'info> { } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::SetTokenAccountsStates))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Mint))] +#[access_control(acl::check_state_type(&ctx.accounts.state, AccountType::Mint))] #[share_class_signer_seeds] pub fn set_token_accounts_states_handler<'info>( ctx: Context<'_, '_, 'info, 'info, SetTokenAccountsStates<'info>>, @@ -461,7 +476,7 @@ pub struct ForceTransferShare<'info> { } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::ForceTransferShare))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Mint))] +#[access_control(acl::check_state_type(&ctx.accounts.state, AccountType::Mint))] #[share_class_signer_seeds] pub fn force_transfer_share_handler( ctx: Context, @@ -530,7 +545,7 @@ pub struct BurnShare<'info> { } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::BurnShare))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Mint))] +#[access_control(acl::check_state_type(&ctx.accounts.state, AccountType::Mint))] #[share_class_signer_seeds] pub fn burn_share_handler(ctx: Context, share_class_id: u8, amount: u64) -> Result<()> { #[cfg(not(feature = "mainnet"))] @@ -589,7 +604,7 @@ pub struct MintShare<'info> { } #[access_control(acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::MintShare))] -#[access_control(acl::check_integration(&ctx.accounts.state, IntegrationName::Mint))] +#[access_control(acl::check_state_type(&ctx.accounts.state, AccountType::Mint))] #[share_class_signer_seeds] pub fn mint_share_handler<'info>( ctx: Context<'_, '_, '_, 'info, MintShare<'info>>, @@ -639,18 +654,18 @@ pub fn update_share_class_handler( share_class_model: ShareClassModel, ) -> Result<()> { let state = &mut ctx.accounts.state; - if !share_class_model.allowlist.is_empty() { + if let Some(share_class_allowlist) = share_class_model.allowlist { let allowlist = state.share_class_allowlist_mut(share_class_id as usize); if let Some(_allowlist) = allowlist { _allowlist.clear(); - _allowlist.extend(share_class_model.allowlist.clone()); + _allowlist.extend(share_class_allowlist.clone()); } } - if !share_class_model.blocklist.is_empty() { + if let Some(share_class_blocklist) = share_class_model.blocklist { let blocklist = state.share_class_blocklist_mut(share_class_id as usize); if let Some(_blocklist) = blocklist { _blocklist.clear(); - _blocklist.extend(share_class_model.blocklist.clone()); + _blocklist.extend(share_class_blocklist.clone()); } } Ok(()) @@ -686,8 +701,9 @@ pub struct CloseShareClass<'info> { )] pub extra_account_meta_list: UncheckedAccount<'info>, - #[account(mut)] - pub metadata: Box>, + /// CHECK: Metadata + #[account(mut, seeds = [SEED_METADATA.as_bytes(), state.key().as_ref()], bump)] + pub metadata: AccountInfo<'info>, #[account(mut)] pub signer: Signer<'info>, @@ -721,10 +737,14 @@ pub fn close_share_class_handler(ctx: Context, share_class_id: ctx.accounts.state.mints.remove(share_class_id as usize); - ctx.accounts - .metadata - .share_classes - .remove(share_class_id as usize); + if let Some(metadata) = ctx.accounts.state.metadata.clone() { + if metadata.template == MetadataTemplate::Openfunds { + let mut data_slice = ctx.accounts.metadata.data.borrow_mut(); // Borrow the mutable data + let data: &mut [u8] = &mut *data_slice; // Dereference and convert to `&mut [u8]` + let mut openfunds = OpenfundsMetadataAccount::try_deserialize(&mut &data[..])?; + openfunds.share_classes.remove(share_class_id as usize); + } + } close_account_info( ctx.accounts.extra_account_meta_list.to_account_info(), diff --git a/anchor/programs/glam/src/instructions/stake.rs b/anchor/programs/glam/src/instructions/stake.rs index 57694949..0e18c7bb 100644 --- a/anchor/programs/glam/src/instructions/stake.rs +++ b/anchor/programs/glam/src/instructions/stake.rs @@ -38,7 +38,7 @@ pub struct InitializeAndDelegateStake<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::Stake) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::NativeStaking) + acl::check_integration(&ctx.accounts.state, Integration::NativeStaking) )] #[vault_signer_seeds] pub fn initialize_and_delegate_stake_handler<'c: 'info, 'info>( @@ -140,7 +140,7 @@ pub struct DeactivateStakeAccounts<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::Unstake) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::NativeStaking) + acl::check_integration(&ctx.accounts.state, Integration::NativeStaking) )] #[vault_signer_seeds] pub fn deactivate_stake_accounts_handler<'info>( @@ -181,7 +181,7 @@ pub struct WithdrawFromStakeAccounts<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::Unstake) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::NativeStaking) + acl::check_integration(&ctx.accounts.state, Integration::NativeStaking) )] #[vault_signer_seeds] pub fn withdraw_from_stake_accounts_handler<'info>( @@ -238,7 +238,7 @@ pub struct MergeStakeAccounts<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::Stake) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::NativeStaking) + acl::check_integration(&ctx.accounts.state, Integration::NativeStaking) )] #[vault_signer_seeds] pub fn merge_stake_accounts_handler<'c: 'info, 'info>( @@ -296,7 +296,7 @@ pub struct SplitStakeAccount<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::Unstake) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::NativeStaking) + acl::check_integration(&ctx.accounts.state, Integration::NativeStaking) )] #[vault_signer_seeds] pub fn split_stake_account_handler<'c: 'info, 'info>( @@ -395,7 +395,7 @@ pub struct RedelegateStake<'info> { acl::check_access(&ctx.accounts.state, &ctx.accounts.signer.key, Permission::Stake) )] #[access_control( - acl::check_integration(&ctx.accounts.state, IntegrationName::NativeStaking) + acl::check_integration(&ctx.accounts.state, Integration::NativeStaking) )] #[vault_signer_seeds] pub fn redelegate_stake_handler<'c: 'info, 'info>( diff --git a/anchor/programs/glam/src/instructions/state.rs b/anchor/programs/glam/src/instructions/state.rs index cbcb90a7..09bda0cf 100644 --- a/anchor/programs/glam/src/instructions/state.rs +++ b/anchor/programs/glam/src/instructions/state.rs @@ -1,9 +1,11 @@ +use std::collections::HashMap; + use crate::{ constants::*, error::{AccessError, StateError}, state::*, }; -use anchor_lang::{prelude::*, system_program}; +use anchor_lang::{prelude::*, solana_program, system_program}; use anchor_spl::{ token::{close_account as close_token_account, CloseAccount as CloseTokenAccount, Token}, token_2022::{ @@ -29,15 +31,21 @@ pub struct InitializeState<'info> { )] pub state: Box>, - #[account(init, seeds = [SEED_METADATA.as_bytes(), state.key().as_ref()], bump, payer = signer, space = MetadataAccount::INIT_SIZE)] - pub metadata: Box>, - #[account(mut, seeds = [SEED_VAULT.as_bytes(), state.key().as_ref()], bump)] pub vault: SystemAccount<'info>, #[account(mut)] pub signer: Signer<'info>, + #[account( + init, + seeds = [SEED_METADATA.as_bytes(), state.key().as_ref()], + bump, + payer = signer, + space = 8 + OpenfundsMetadataAccount::INIT_SIZE + )] + pub openfunds_metadata: Option>>, + pub system_program: Program<'info, System>, } @@ -50,52 +58,50 @@ pub fn initialize_state_handler<'c: 'info, 'info>( // let state = &mut ctx.accounts.state; let model = state_model.clone(); + + state.account_type = model.account_type.ok_or(StateError::InvalidAccountType)?; + if let Some(name) = model.name { - require!(name.len() < MAX_SIZE_NAME, StateError::InvalidName); + require!(name.len() <= MAX_SIZE_NAME, StateError::InvalidName); state.name = name; } - if let Some(fund_uri) = model.uri { - require!(fund_uri.len() < MAX_SIZE_URI, StateError::InvalidUri); - state.uri = fund_uri; + if let Some(uri) = model.uri { + require!(uri.len() < MAX_SIZE_URI, StateError::InvalidUri); + state.uri = uri; + } + if let Some(created) = model.created { + state.created = CreatedModel { + key: created.key, + created_by: ctx.accounts.signer.key(), + created_at: Clock::get()?.unix_timestamp, + }; } - if let Some(metadata_uri) = model.metadata_uri { - require!(metadata_uri.len() < MAX_SIZE_URI, StateError::InvalidUri); - state.metadata_uri = metadata_uri; + if let Some(metadata) = model.metadata { + require!(metadata.uri.len() < MAX_SIZE_URI, StateError::InvalidUri); + state.metadata = Some(Metadata { + template: metadata.template, + pubkey: metadata.pubkey, + uri: metadata.uri, + }); + + if metadata.template == MetadataTemplate::Openfunds { + if let Some(openfunds_metadata) = &mut ctx.accounts.openfunds_metadata { + openfunds_metadata.set_inner(OpenfundsMetadataAccount::from(state_model)); + openfunds_metadata.fund_id = state.key(); + + // Update metadata pubkey + state.metadata.as_mut().unwrap().pubkey = openfunds_metadata.key(); + } + } } state.vault = ctx.accounts.vault.key(); - state.metadata = ctx.accounts.metadata.key(); state.owner = ctx.accounts.signer.key(); - - // - // Set state params - // - // state.params[0][0]: assets allowlists - // state.params[0][1]: integration acls - // - state.params = vec![vec![ - EngineField { - name: EngineFieldName::Assets, - value: EngineFieldValue::VecPubkey { val: model.assets }, - }, - EngineField { - name: EngineFieldName::IntegrationAcls, - value: EngineFieldValue::VecIntegrationAcl { - val: model.integration_acls, - }, - }, - ]]; - - // - // Initialize metadata account - // - let metadata = &mut ctx.accounts.metadata; - let openfunds_metadata = MetadataAccount::from(state_model); - metadata.state_pubkey = state.key(); - metadata.company = openfunds_metadata.company; - metadata.fund = openfunds_metadata.fund; - metadata.share_classes = openfunds_metadata.share_classes; - metadata.fund_managers = openfunds_metadata.fund_managers; + state.enabled = model.enabled.unwrap_or(true); + state.assets = model.assets.unwrap_or_default(); + state.integrations = model.integrations.unwrap_or_default(); + state.delegate_acls = model.delegate_acls.unwrap_or_default(); + state.params = vec![vec![]]; msg!("State account created: {}", ctx.accounts.state.key()); Ok(()) @@ -124,10 +130,7 @@ pub fn update_state_handler<'c: 'info, 'info>( state.name = name; } if let Some(uri) = state_model.uri { - require!( - uri.as_bytes().len() <= MAX_SIZE_URI, - StateError::InvalidName - ); + require!(uri.as_bytes().len() <= MAX_SIZE_URI, StateError::InvalidUri); state.uri = uri; } @@ -137,162 +140,82 @@ pub fn update_state_handler<'c: 'info, 'info>( } } - if !state_model.assets.is_empty() { - let assets = state.assets_mut().unwrap(); - assets.clear(); - assets.extend(state_model.assets.clone()); + if let Some(assets) = state_model.assets { + state.assets = assets; } - // One of the engine field in `fund.params[0]` stores the existing acls of the fund, - // and `fund_model.acls` is new acls to be upserted or deleted. - // - // For each acl in `fund_model.acls` we check two cases: - // - // 1) a fund acl with same pubkey exists - // - acl.permissions is empty, delete the fund acl - // - acl.permissions is not empty, update permissions - // - // 2) a fund acl with same pubkey doesn't exist - // - add the acl - if !state_model.delegate_acls.is_empty() { - // Add the acls field if it doesn't exist - let delegate_acls_field_exists = state.params[0] - .iter() - .any(|field| field.name == EngineFieldName::DelegateAcls); - - if !delegate_acls_field_exists { - msg!("Adding acls field to state params"); - state.params[0].push(EngineField { - name: EngineFieldName::DelegateAcls, - value: EngineFieldValue::VecDelegateAcl { val: Vec::new() }, - }); - } + if let Some(integrations) = state_model.integrations { + state.integrations = integrations; + } - let to_delete: Vec = state_model + // Update or add delegate acls + // If permissions is empty, delete the entry + if let Some(delegate_acls) = state_model.delegate_acls { + let mut existing_pubkeys: HashMap<_, _> = state .delegate_acls - .clone() .iter() - .filter(|acl| acl.permissions.is_empty()) - .map(|acl| acl.pubkey) + .map(|da| (da.pubkey, da.clone())) .collect(); - if !to_delete.is_empty() { - for EngineField { name, value } in &mut state.params[0] { - if let (EngineFieldName::DelegateAcls, EngineFieldValue::VecDelegateAcl { val }) = - (name, value) - { - val.retain(|acl| !to_delete.contains(&acl.pubkey)); - } - } - } - let to_upsert = state_model - .delegate_acls - .clone() - .into_iter() - .filter(|acl| !acl.permissions.is_empty()); - - for new_acl in to_upsert { - for EngineField { name, value } in &mut state.params[0] { - if let (EngineFieldName::DelegateAcls, EngineFieldValue::VecDelegateAcl { val }) = - (name, value) - { - if let Some(existing_acl) = - val.iter_mut().find(|acl| acl.pubkey == new_acl.pubkey) - { - existing_acl.permissions = new_acl.permissions.clone(); - } else { - val.push(new_acl.clone()); - } - } - } - } - } - - // Update integration acls for the state - if !state_model.integration_acls.is_empty() { - // Check if the integrations field exists - // Add the integrations field if it doesn't exist - let integration_acl_field_exists = state.params[0] - .iter() - .any(|field| field.name == EngineFieldName::IntegrationAcls); - if !integration_acl_field_exists { - msg!("Adding integrations field to state params"); - state.params[0].push(EngineField { - name: EngineFieldName::IntegrationAcls, - value: EngineFieldValue::VecIntegrationAcl { val: Vec::new() }, - }); + for da in delegate_acls { + existing_pubkeys.insert(da.pubkey, da.clone()); } - for EngineField { name, value } in &mut state.params[0] { - if let (EngineFieldName::IntegrationAcls, EngineFieldValue::VecIntegrationAcl { val }) = - (name, value) - { - val.clear(); - val.extend(state_model.integration_acls.clone()); - } - } + state.delegate_acls = existing_pubkeys + .into_values() + .filter(|da| !da.permissions.is_empty()) + .collect(); } - if !state_model.drift_market_indexes_perp.is_empty() { - let mut found = false; - for EngineField { name, value } in &mut state.params[0] { - if let (EngineFieldName::DriftMarketIndexesPerp, EngineFieldValue::VecU32 { val }) = - (name, value) - { - val.clear(); - val.extend(state_model.drift_market_indexes_perp.clone()); - found = true; + if let Some(market_indexes_perp) = state_model.drift_market_indexes_perp { + if let Some(EngineField { value, .. }) = state.params[0] + .iter_mut() + .find(|f| f.name == EngineFieldName::DriftMarketIndexesPerp) + { + if let EngineFieldValue::VecU32 { val } = value { + *val = market_indexes_perp; } - } - if !found { + } else { state.params[0].push(EngineField { name: EngineFieldName::DriftMarketIndexesPerp, value: EngineFieldValue::VecU32 { - val: state_model.drift_market_indexes_perp, + val: market_indexes_perp.clone(), }, }); - } + }; } - if !state_model.drift_market_indexes_spot.is_empty() { - let mut found = false; - for EngineField { name, value } in &mut state.params[0] { - if let (EngineFieldName::DriftMarketIndexesSpot, EngineFieldValue::VecU32 { val }) = - (name, value) - { - val.clear(); - val.extend(state_model.drift_market_indexes_spot.clone()); - found = true; + if let Some(market_indexes_spot) = state_model.drift_market_indexes_spot { + if let Some(EngineField { value, .. }) = state.params[0] + .iter_mut() + .find(|f| f.name == EngineFieldName::DriftMarketIndexesSpot) + { + if let EngineFieldValue::VecU32 { val } = value { + *val = market_indexes_spot; } - } - if !found { + } else { state.params[0].push(EngineField { name: EngineFieldName::DriftMarketIndexesSpot, value: EngineFieldValue::VecU32 { - val: state_model.drift_market_indexes_spot, + val: market_indexes_spot.clone(), }, }); - } + }; } - if !state_model.drift_order_types.is_empty() { - let mut found = false; - for EngineField { name, value } in &mut state.params[0] { - if let (EngineFieldName::DriftOrderTypes, EngineFieldValue::VecU32 { val }) = - (name, value) - { - val.clear(); - val.extend(state_model.drift_order_types.clone()); - found = true; - } - } - if !found { - state.params[0].push(EngineField { - name: EngineFieldName::DriftOrderTypes, - value: EngineFieldValue::VecU32 { - val: state_model.drift_order_types, - }, + if let Some(drift_order_types) = state_model.drift_order_types { + let idx = state.params[0] + .iter() + .position(|f| f.name == EngineFieldName::DriftOrderTypes) + .unwrap_or_else(|| { + state.params[0].push(EngineField { + name: EngineFieldName::DriftOrderTypes, + value: EngineFieldValue::VecU32 { val: Vec::new() }, + }); + state.params[0].len() - 1 }); + if let EngineFieldValue::VecU32 { val } = &mut state.params[0][idx].value { + *val = drift_order_types; } } @@ -304,8 +227,9 @@ pub struct CloseState<'info> { #[account(mut, close = signer, constraint = state.owner == signer.key() @ AccessError::NotAuthorized)] pub state: Account<'info, StateAccount>, - #[account(mut, close = signer)] - pub metadata: Account<'info, MetadataAccount>, + /// CHECK: Manually deserialized + #[account(mut, seeds = [SEED_METADATA.as_bytes(), state.key().as_ref()], bump)] + pub metadata: AccountInfo<'info>, #[account(mut, seeds = [SEED_VAULT.as_bytes(), state.key().as_ref()], bump)] pub vault: SystemAccount<'info>, @@ -338,6 +262,11 @@ pub fn close_state_handler(ctx: Context) -> Result<()> { )?; } + close_account_info( + ctx.accounts.metadata.to_account_info(), + ctx.accounts.signer.to_account_info(), + )?; + msg!("State account closed: {}", ctx.accounts.state.key()); Ok(()) } diff --git a/anchor/programs/glam/src/lib.rs b/anchor/programs/glam/src/lib.rs index 8cced6dd..4cdc3fde 100644 --- a/anchor/programs/glam/src/lib.rs +++ b/anchor/programs/glam/src/lib.rs @@ -169,7 +169,7 @@ pub mod glam { /// - Permission::MintShare /// /// # Integration required - /// - IntegrationName::Mint + /// - Integration::Mint pub fn mint_share<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, MintShare<'info>>, share_class_id: u8, @@ -189,7 +189,7 @@ pub mod glam { /// - Permission::ForceTransferShare /// /// # Integration required - /// - IntegrationName::Mint + /// - Integration::Mint pub fn force_transfer_share( ctx: Context, share_class_id: u8, @@ -209,7 +209,7 @@ pub mod glam { /// - Permission::BurnShare /// /// # Integration required - /// - IntegrationName::Mint + /// - Integration::Mint pub fn burn_share(ctx: Context, share_class_id: u8, amount: u64) -> Result<()> { share_class::burn_share_handler(ctx, share_class_id, amount) } @@ -225,7 +225,7 @@ pub mod glam { /// - Permission::SetTokenAccountsStates /// /// # Integration required - /// - IntegrationName::Mint + /// - Integration::Mint pub fn set_token_accounts_states<'info>( ctx: Context<'_, '_, 'info, 'info, SetTokenAccountsStates<'info>>, share_class_id: u8, @@ -282,7 +282,7 @@ pub mod glam { /// - Permission::DriftInitialize /// /// # Integration required - /// - IntegrationName::Drift + /// - Integration::Drift pub fn drift_initialize(ctx: Context) -> Result<()> { drift::initialize_handler(ctx) } @@ -298,7 +298,7 @@ pub mod glam { /// - Permission::DriftUpdateUser /// /// # Integration required - /// - IntegrationName::Drift + /// - Integration::Drift pub fn drift_update_user_custom_margin_ratio( ctx: Context, sub_account_id: u16, @@ -318,7 +318,7 @@ pub mod glam { /// - Permission::DriftUpdateUser /// /// # Integration required - /// - IntegrationName::Drift + /// - Integration::Drift pub fn drift_update_user_margin_trading_enabled( ctx: Context, sub_account_id: u16, @@ -342,7 +342,7 @@ pub mod glam { /// - Permission::DriftUpdateUser /// /// # Integration required - /// - IntegrationName::Drift + /// - Integration::Drift pub fn drift_update_user_delegate( ctx: Context, sub_account_id: u16, @@ -362,7 +362,7 @@ pub mod glam { /// - Permission::DriftDeposit /// /// # Integration required - /// - IntegrationName::Drift + /// - Integration::Drift pub fn drift_deposit<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftDeposit<'info>>, market_index: u16, @@ -382,7 +382,7 @@ pub mod glam { /// - Permission::DriftWithdraw /// /// # Integration required - /// - IntegrationName::Drift + /// - Integration::Drift pub fn drift_withdraw<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftWithdraw<'info>>, market_index: u16, @@ -400,7 +400,7 @@ pub mod glam { /// - Permission::DriftDeleteUser /// /// # Integration required - /// - IntegrationName::Drift + /// - Integration::Drift pub fn drift_delete_user(ctx: Context) -> Result<()> { drift::delete_user_handler(ctx) } @@ -416,7 +416,7 @@ pub mod glam { /// - Additional permission Permission::DriftSpotMarket or Permission::DriftPerpMarket is required depending on market type. /// /// # Integration required - /// - IntegrationName::Drift + /// - Integration::Drift pub fn drift_place_orders<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftPlaceOrders<'info>>, order_params: Vec, @@ -436,7 +436,7 @@ pub mod glam { /// - Permission::DriftCancelOrders /// /// # Integration required - /// - IntegrationName::Drift + /// - Integration::Drift pub fn drift_cancel_orders<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DriftCancelOrders<'info>>, market_type: Option, @@ -460,7 +460,7 @@ pub mod glam { /// - Permission::Stake /// /// # Integration required - /// - IntegrationName::Marinade + /// - Integration::Marinade pub fn marinade_deposit_sol(ctx: Context, lamports: u64) -> Result<()> { marinade::marinade_deposit_sol_handler(ctx, lamports) } @@ -475,7 +475,7 @@ pub mod glam { /// - Permission::Stake /// /// # Integration required - /// - IntegrationName::Marinade + /// - Integration::Marinade pub fn marinade_deposit_stake( ctx: Context, validator_idx: u32, @@ -493,7 +493,7 @@ pub mod glam { /// - Permission::LiquidUnstake /// /// # Integration required - /// - IntegrationName::Marinade + /// - Integration::Marinade pub fn marinade_liquid_unstake( ctx: Context, msol_amount: u64, @@ -513,7 +513,7 @@ pub mod glam { /// - Permission::Unstake /// /// # Integration required - /// - IntegrationName::Marinade + /// - Integration::Marinade pub fn marinade_delayed_unstake( ctx: Context, msol_amount: u64, @@ -532,7 +532,7 @@ pub mod glam { /// - Permission::Unstake /// /// # Integration required - /// - IntegrationName::Marinade + /// - Integration::Marinade pub fn marinade_claim_tickets<'info>( ctx: Context<'_, '_, '_, 'info, MarinadeClaimTickets<'info>>, ) -> Result<()> { @@ -553,7 +553,7 @@ pub mod glam { /// - Permission::Stake /// /// # Integration required - /// - IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used. + /// - Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used. pub fn stake_pool_deposit_sol(ctx: Context, lamports: u64) -> Result<()> { stake_pool::deposit_sol_handler(ctx, lamports) } @@ -567,7 +567,7 @@ pub mod glam { /// - Permission::Stake /// /// # Integration required - /// - IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used. + /// - Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used. pub fn stake_pool_deposit_stake(ctx: Context) -> Result<()> { stake_pool::deposit_stake_handler(ctx) } @@ -582,7 +582,7 @@ pub mod glam { /// - Permission::LiquidUnstake /// /// # Integration required - /// - IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used. + /// - Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used. pub fn stake_pool_withdraw_sol( ctx: Context, pool_token_amount: u64, @@ -602,7 +602,7 @@ pub mod glam { /// - Permission::Unstake /// /// # Integration required - /// - IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used. + /// - Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used. pub fn stake_pool_withdraw_stake( ctx: Context, pool_token_amount: u64, @@ -633,7 +633,7 @@ pub mod glam { /// - Permission::Stake /// /// # Integration required - /// - IntegrationName::NativeStaking + /// - Integration::NativeStaking pub fn initialize_and_delegate_stake<'info>( ctx: Context<'_, '_, '_, 'info, InitializeAndDelegateStake<'info>>, lamports: u64, @@ -657,7 +657,7 @@ pub mod glam { /// - Permission::Unstake /// /// # Integration required - /// - IntegrationName::NativeStaking + /// - Integration::NativeStaking pub fn deactivate_stake_accounts<'info>( ctx: Context<'_, '_, '_, 'info, DeactivateStakeAccounts<'info>>, ) -> Result<()> { @@ -673,7 +673,7 @@ pub mod glam { /// - Permission::Unstake /// /// # Integration required - /// - IntegrationName::NativeStaking + /// - Integration::NativeStaking pub fn withdraw_from_stake_accounts<'info>( ctx: Context<'_, '_, '_, 'info, WithdrawFromStakeAccounts<'info>>, ) -> Result<()> { @@ -689,7 +689,7 @@ pub mod glam { /// - Permission::Stake /// /// # Integration required - /// - IntegrationName::NativeStaking + /// - Integration::NativeStaking pub fn merge_stake_accounts<'info>( ctx: Context<'_, '_, '_, 'info, MergeStakeAccounts<'info>>, ) -> Result<()> { @@ -708,7 +708,7 @@ pub mod glam { /// - Permission::Unstake /// /// # Integration required - /// - IntegrationName::NativeStaking + /// - Integration::NativeStaking pub fn split_stake_account<'info>( ctx: Context<'_, '_, '_, 'info, SplitStakeAccount<'info>>, lamports: u64, @@ -734,7 +734,7 @@ pub mod glam { /// - Permission::Unstake /// /// # Integration required - /// - IntegrationName::NativeStaking + /// - Integration::NativeStaking pub fn redelegate_stake<'info>( ctx: Context<'_, '_, '_, 'info, RedelegateStake<'info>>, new_stake_account_id: String, @@ -761,7 +761,7 @@ pub mod glam { /// - Permission::JupiterSwapLst: input and output assets are both LST. /// /// # Integration required - /// - IntegrationName::JupiterSwap + /// - Integration::JupiterSwap pub fn jupiter_swap<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, JupiterSwap<'info>>, amount: u64, @@ -783,7 +783,7 @@ pub mod glam { /// - Permission::StakeJup /// /// # Integration required - /// - IntegrationName::JupiterVote + /// - Integration::JupiterVote pub fn init_locked_voter_escrow<'info>(ctx: Context) -> Result<()> { jupiter::init_locked_voter_escrow_handler(ctx) } @@ -798,7 +798,7 @@ pub mod glam { /// - Permission::UnstakeJup /// /// # Integration required - /// - IntegrationName::JupiterVote + /// - Integration::JupiterVote pub fn toggle_max_lock<'info>(ctx: Context, value: bool) -> Result<()> { jupiter::toggle_max_lock_handler(ctx, value) } @@ -813,7 +813,7 @@ pub mod glam { /// - Permission::StakeJup /// /// # Integration required - /// - IntegrationName::JupiterVote + /// - Integration::JupiterVote pub fn increase_locked_amount<'info>( ctx: Context, amount: u64, @@ -832,7 +832,7 @@ pub mod glam { /// - Permission::UnstakeJup /// /// # Integration required - /// - IntegrationName::JupiterVote + /// - Integration::JupiterVote pub fn open_partial_unstaking<'info>( ctx: Context, amount: u64, @@ -850,7 +850,7 @@ pub mod glam { /// - Permission::UnstakeJup /// /// # Integration required - /// - IntegrationName::JupiterVote + /// - Integration::JupiterVote pub fn merge_partial_unstaking<'info>(ctx: Context) -> Result<()> { jupiter::merge_partial_unstaking_handler(ctx) } @@ -864,7 +864,7 @@ pub mod glam { /// - Permission::UnstakeJup /// /// # Integration required - /// - IntegrationName::JupiterVote + /// - Integration::JupiterVote pub fn withdraw_partial_unstaking<'info>(ctx: Context) -> Result<()> { jupiter::withdraw_partial_unstaking_handler(ctx) } @@ -878,7 +878,7 @@ pub mod glam { /// - Permission::UnstakeJup /// /// # Integration required - /// - IntegrationName::JupiterVote + /// - Integration::JupiterVote pub fn withdraw_all_staked_jup<'info>(ctx: Context) -> Result<()> { jupiter::withdraw_all_staked_jup_handler(ctx) } @@ -892,7 +892,7 @@ pub mod glam { /// - Permission::VoteOnProposal /// /// # Integration required - /// - IntegrationName::JupiterVote + /// - Integration::JupiterVote pub fn new_vote<'info>(ctx: Context) -> Result<()> { jupiter::new_vote_handler(ctx) } @@ -907,7 +907,7 @@ pub mod glam { /// - Permission::VoteOnProposal /// /// # Integration required - /// - IntegrationName::JupiterVote + /// - Integration::JupiterVote pub fn cast_vote<'info>(ctx: Context, side: u8) -> Result<()> { jupiter::cast_vote_handler(ctx, side) } diff --git a/anchor/programs/glam/src/state/accounts.rs b/anchor/programs/glam/src/state/accounts.rs index 77fdff45..69263322 100644 --- a/anchor/programs/glam/src/state/accounts.rs +++ b/anchor/programs/glam/src/state/accounts.rs @@ -7,15 +7,9 @@ use super::openfunds::*; #[derive(AnchorDeserialize, AnchorSerialize, PartialEq, Clone, Debug, Copy)] pub enum EngineFieldName { - TimeCreated, - IsEnabled, - Assets, - AssetsWeights, - ShareClassAllowlist, // share class - ShareClassBlocklist, // share class - DelegateAcls, - IntegrationAcls, - ExternalVaultAccounts, // external accounts with vaultassets + ShareClassAllowlist, // share class + ShareClassBlocklist, // share class + ExternalVaultAccounts, // external accounts with vault assets LockUp, // share class DriftMarketIndexesPerp, DriftMarketIndexesSpot, @@ -40,8 +34,6 @@ pub enum EngineFieldValue { Timestamp { val: i64 }, VecPubkey { val: Vec }, VecU32 { val: Vec }, - VecDelegateAcl { val: Vec }, - VecIntegrationAcl { val: Vec }, } #[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)] @@ -50,244 +42,170 @@ pub struct EngineField { pub value: EngineFieldValue, } -pub type StateAccount = FundAccount; -pub type MetadataAccount = FundMetadataAccount; +#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug, PartialEq)] +pub enum AccountType { + Vault, + Mint, + Fund, + // ... more account types +} + +#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug, PartialEq, Copy)] +pub enum MetadataTemplate { + Openfunds, + // ... more metadata templates +} + +#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)] +pub struct Metadata { + pub template: MetadataTemplate, + pub pubkey: Pubkey, // metadata account pubkey + pub uri: String, +} + +#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)] +pub struct CreatedModel { + pub key: [u8; 8], // seed for computing state PDA + pub created_by: Pubkey, // original pubkey that created the state account + pub created_at: i64, +} #[account] -pub struct FundAccount { +pub struct StateAccount { + pub account_type: AccountType, pub owner: Pubkey, pub vault: Pubkey, - pub metadata: Pubkey, + pub enabled: bool, + pub created: CreatedModel, pub engine: Pubkey, pub mints: Vec, + pub metadata: Option, pub name: String, pub uri: String, - pub metadata_uri: String, + pub assets: Vec, + pub delegate_acls: Vec, + pub integrations: Vec, - // params[0]: fund params + // params[0]: state params // params[1..n+1]: mints [0..n] params pub params: Vec>, } -impl FundAccount { - pub const INIT_SIZE: usize = 2048; // TODO: auto extend account size if needed - - pub fn is_enabled(&self) -> bool { - return true; - } +impl StateAccount { + pub const INIT_SIZE: usize = 1024; // TODO: auto extend account size if needed // return the share class lockup period in s. 0 == no lockup (default). pub fn share_class_lock_up(&self, share_class_id: usize) -> i64 { - let param_idx = share_class_id + 1; - for EngineField { name, value } in &self.params[param_idx] { - match name { - EngineFieldName::LockUp => { - return match value { - EngineFieldValue::Timestamp { val: v } => { - if *v > 0 { - *v - } else { - 0 - } - } - _ => 0, - }; - } - _ => { /* ignore */ } - } - } - return 0; + self.params + .get(share_class_id + 1) + .and_then(|params| { + params + .iter() + .find(|EngineField { name, .. }| *name == EngineFieldName::LockUp) + .and_then(|EngineField { value, .. }| match value { + EngineFieldValue::Timestamp { val: v } if *v > 0 => Some(*v), + _ => None, + }) + }) + .unwrap_or(0) } pub fn share_class_allowlist(&self, share_class_id: usize) -> Option<&Vec> { - // params[1]: share class 0 acls - // params[2]: share class 1 acls - // ... - let param_idx = share_class_id + 1; - for EngineField { name, value } in &self.params[param_idx] { - match name { - EngineFieldName::ShareClassAllowlist => { - return match value { - EngineFieldValue::VecPubkey { val: v } => Some(v), - _ => None, - }; - } - _ => { /* ignore */ } - } - } - return None; + self.params.get(share_class_id + 1).and_then(|params| { + params + .iter() + .find(|EngineField { name, .. }| *name == EngineFieldName::ShareClassAllowlist) + .and_then(|EngineField { value, .. }| match value { + EngineFieldValue::VecPubkey { val } => Some(val), + _ => None, + }) + }) } pub fn share_class_allowlist_mut(&mut self, share_class_id: usize) -> Option<&mut Vec> { - // params[1]: share class 0 acls - // params[2]: share class 1 acls - // ... - let param_idx = share_class_id + 1; - self.params[param_idx] - .iter_mut() - .find_map(|EngineField { name, value }| { - if *name == EngineFieldName::ShareClassAllowlist { - match value { - EngineFieldValue::VecPubkey { val } => Some(val), - _ => None, - } - } else { - None - } - }) + self.params.get_mut(share_class_id + 1).and_then(|params| { + params + .iter_mut() + .find(|EngineField { name, .. }| *name == EngineFieldName::ShareClassAllowlist) + .and_then(|EngineField { value, .. }| match value { + EngineFieldValue::VecPubkey { val } => Some(val), + _ => None, + }) + }) } pub fn share_class_blocklist(&self, share_class_id: usize) -> Option<&Vec> { - // params[1]: share class 0 acls - // params[2]: share class 1 acls - // ... - let param_idx = share_class_id + 1; - for EngineField { name, value } in &self.params[param_idx] { - match name { - EngineFieldName::ShareClassBlocklist => { - return match value { - EngineFieldValue::VecPubkey { val: v } => Some(v), - _ => None, - }; - } - _ => { /* ignore */ } - } - } - return None; + self.params.get(share_class_id + 1).and_then(|params| { + params + .iter() + .find(|EngineField { name, .. }| *name == EngineFieldName::ShareClassBlocklist) + .and_then(|EngineField { value, .. }| match value { + EngineFieldValue::VecPubkey { val } => Some(val), + _ => None, + }) + }) } pub fn share_class_blocklist_mut(&mut self, share_class_id: usize) -> Option<&mut Vec> { - // params[1]: share class 0 acls - // params[2]: share class 1 acls - // ... - let param_idx = share_class_id + 1; - self.params[param_idx] - .iter_mut() - .find_map(|EngineField { name, value }| { - if *name == EngineFieldName::ShareClassBlocklist { - match value { - EngineFieldValue::VecPubkey { val } => Some(val), - _ => None, - } - } else { - None - } - }) - } - - pub fn assets(&self) -> Option<&Vec> { - for EngineField { name, value } in &self.params[0] { - match name { - EngineFieldName::Assets => { - return match value { - EngineFieldValue::VecPubkey { val: v } => Some(v), - _ => None, - }; - } - _ => { /* ignore */ } - } - } - return None; - } - - pub fn assets_mut(&mut self) -> Option<&mut Vec> { - for EngineField { name, value } in &mut self.params[0] { - match name { - EngineFieldName::Assets => { - return match value { - EngineFieldValue::VecPubkey { val: v } => Some(v), - _ => None, - }; - } - _ => { /* ignore */ } - } - } - return None; - } - - pub fn delegate_acls(&self) -> Option<&Vec> { - for EngineField { name, value } in &self.params[0] { - match name { - EngineFieldName::DelegateAcls => { - return match value { - EngineFieldValue::VecDelegateAcl { val: v } => Some(v), - _ => None, - }; - } - _ => { /* ignore */ } - } - } - return None; - } - - pub fn integration_acls(&self) -> Option<&Vec> { - for EngineField { name, value } in &self.params[0] { - match name { - EngineFieldName::IntegrationAcls => { - return match value { - EngineFieldValue::VecIntegrationAcl { val: v } => Some(v), - _ => None, - }; - } - _ => { /* ignore */ } - } - } - return None; + self.params.get_mut(share_class_id + 1).and_then(|params| { + params + .iter_mut() + .find(|EngineField { name, .. }| *name == EngineFieldName::ShareClassBlocklist) + .and_then(|EngineField { value, .. }| match value { + EngineFieldValue::VecPubkey { val } => Some(val), + _ => None, + }) + }) } pub fn drift_order_types(&self) -> Option<&Vec> { - for EngineField { name, value } in &self.params[0] { - match name { - EngineFieldName::DriftOrderTypes => { - return match value { - EngineFieldValue::VecU32 { val: v } => Some(v), - _ => None, - }; - } - _ => { /* ignore */ } - } - } - return None; + self.params.get(0).and_then(|params| { + params + .iter() + .find(|EngineField { name, .. }| *name == EngineFieldName::DriftOrderTypes) + .and_then(|EngineField { value, .. }| match value { + EngineFieldValue::VecU32 { val } => Some(val), + _ => None, + }) + }) } pub fn drift_market_indexes_perp(&self) -> Option<&Vec> { - for EngineField { name, value } in &self.params[0] { - match name { - EngineFieldName::DriftMarketIndexesPerp => { - return match value { - EngineFieldValue::VecU32 { val: v } => Some(v), - _ => None, - }; - } - _ => { /* ignore */ } - } - } - return None; + self.params.get(0).and_then(|params| { + params + .iter() + .find(|EngineField { name, .. }| *name == EngineFieldName::DriftMarketIndexesPerp) + .and_then(|EngineField { value, .. }| match value { + EngineFieldValue::VecU32 { val } => Some(val), + _ => None, + }) + }) } pub fn drift_market_indexes_spot(&self) -> Option<&Vec> { - for EngineField { name, value } in &self.params[0] { - match name { - EngineFieldName::DriftMarketIndexesSpot => { - return match value { - EngineFieldValue::VecU32 { val: v } => Some(v), - _ => None, - }; - } - _ => { /* ignore */ } - } - } - return None; + self.params.get(0).and_then(|params| { + params + .iter() + .find(|EngineField { name, .. }| *name == EngineFieldName::DriftMarketIndexesSpot) + .and_then(|EngineField { value, .. }| match value { + EngineFieldValue::VecU32 { val } => Some(val), + _ => None, + }) + }) } pub fn add_to_engine_field(&mut self, engine_field_name: EngineFieldName, pubkey: Pubkey) { - // Try to find the MarinadeTickets field, if it exists. - let mut engine_field = self.params[0] - .iter_mut() - .find(|field| field.name == engine_field_name); + let mut engine_field = self.params.get_mut(0).and_then(|params| { + params + .iter_mut() + .find(|field| field.name == engine_field_name) + }); // If the field does not exist, create it and push it to params. if engine_field.is_none() { - msg!("Adding engine field {:?} to fund params", engine_field_name); + msg!( + "Adding engine field {:?} to state params", + engine_field_name + ); self.params[0].push(EngineField { name: engine_field_name, value: EngineFieldValue::VecPubkey { val: Vec::new() }, @@ -314,17 +232,19 @@ impl FundAccount { } pub fn delete_from_engine_field(&mut self, engine_field_name: EngineFieldName, pubkey: Pubkey) { - for EngineField { name, value } in &mut self.params[0] { - if *name == engine_field_name { - if let EngineFieldValue::VecPubkey { val } = value { - if let Some(pos) = val.iter().position(|t| *t == pubkey) { - val.remove(pos); - msg!( - "Removed pubkey {:?} from engine field {:?}", - pubkey, - engine_field_name - ); - } + if let Some(field) = self + .params + .get_mut(0) + .and_then(|params| params.iter_mut().find(|f| f.name == engine_field_name)) + { + if let EngineFieldValue::VecPubkey { val } = &mut field.value { + if let Some(pos) = val.iter().position(|t| *t == pubkey) { + val.remove(pos); + msg!( + "Removed pubkey {:?} from engine field {:?}", + pubkey, + engine_field_name + ); } } } @@ -343,37 +263,36 @@ impl FundAccount { } #[account] -pub struct FundMetadataAccount { - pub state_pubkey: Pubkey, +pub struct OpenfundsMetadataAccount { + pub fund_id: Pubkey, pub company: Vec, pub fund: Vec, pub share_classes: Vec>, pub fund_managers: Vec>, } -impl FundMetadataAccount { +impl OpenfundsMetadataAccount { pub const INIT_SIZE: usize = 1024; } -impl From for FundMetadataAccount { +impl From for OpenfundsMetadataAccount { fn from(model: StateModel) -> Self { - let company = if let Some(company) = &model.company { - company.into() - } else { - vec![] - }; - let fund_managers = if let Some(manager) = &model.owner { - vec![manager.into()] - } else { - vec![] - }; + let fund = model.clone().into(); + let company = model.company.as_ref().map(|c| c.into()).unwrap_or_default(); + let fund_managers = model + .owner + .as_ref() + .map(|m| vec![m.into()]) + .unwrap_or_default(); + let share_classes = model .mints + .unwrap_or_default() .iter() .map(|share_class| share_class.into()) .collect::>(); - let fund = model.into(); - FundMetadataAccount { - state_pubkey: Pubkey::default(), + + OpenfundsMetadataAccount { + fund_id: Pubkey::default(), company, fund, share_classes, diff --git a/anchor/programs/glam/src/state/acl.rs b/anchor/programs/glam/src/state/acl.rs index 68ef1e16..8098d410 100644 --- a/anchor/programs/glam/src/state/acl.rs +++ b/anchor/programs/glam/src/state/acl.rs @@ -1,6 +1,6 @@ use anchor_lang::prelude::*; -use super::StateAccount; +use super::{AccountType, StateAccount}; use crate::error::AccessError; use spl_stake_pool::ID as SPL_STAKE_POOL_PROGRAM_ID; @@ -35,40 +35,26 @@ pub enum Permission { JupiterSwapLst, // Swap LSTs } -#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)] +#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug, PartialEq)] pub struct DelegateAcl { pub pubkey: Pubkey, pub permissions: Vec, + pub expires_at: i64, // Unix timestamp in seconds, 0 means no expiration } -/** - * Integration ACL - */ #[derive(AnchorDeserialize, AnchorSerialize, Clone, PartialEq, Debug)] -pub enum IntegrationName { +pub enum Integration { Drift, SplStakePool, SanctumStakePool, NativeStaking, Marinade, JupiterSwap, // Jupiter Swap - Mint, // GLAM Mint JupiterVote, // Jupiter Vote } -#[derive(AnchorDeserialize, AnchorSerialize, Clone, PartialEq, Debug)] -pub enum IntegrationFeature { - All, -} - -#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)] -pub struct IntegrationAcl { - pub name: IntegrationName, - pub features: Vec, -} - -pub fn check_access(fund: &StateAccount, signer: &Pubkey, permission: Permission) -> Result<()> { - if fund.owner == *signer { +pub fn check_access(state: &StateAccount, signer: &Pubkey, permission: Permission) -> Result<()> { + if state.owner == *signer { return Ok(()); } @@ -79,22 +65,21 @@ pub fn check_access(fund: &StateAccount, signer: &Pubkey, permission: Permission permission ); - if let Some(acls) = fund.delegate_acls() { - for acl in acls { - if acl.pubkey == *signer && acl.permissions.contains(&permission) { - return Ok(()); - } + for acl in state.delegate_acls.clone() { + if acl.pubkey == *signer && acl.permissions.contains(&permission) { + return Ok(()); } } + return Err(AccessError::NotAuthorized.into()); } pub fn check_access_any( - fund: &StateAccount, + state: &StateAccount, signer: &Pubkey, allowed_permissions: Vec, ) -> Result<()> { - if fund.owner == *signer { + if state.owner == *signer { return Ok(()); } @@ -105,44 +90,50 @@ pub fn check_access_any( allowed_permissions ); - if let Some(acls) = fund.delegate_acls() { - for acl in acls { - if acl.pubkey == *signer - && acl - .permissions - .iter() - .any(|p| allowed_permissions.contains(p)) - { - return Ok(()); - } + for acl in state.delegate_acls.clone() { + if acl.pubkey == *signer + && acl + .permissions + .iter() + .any(|p| allowed_permissions.contains(p)) + { + return Ok(()); } } + return Err(AccessError::NotAuthorized.into()); } -pub fn check_integration(fund: &StateAccount, integration: IntegrationName) -> Result<()> { +pub fn check_state_type(state: &StateAccount, accont_type: AccountType) -> Result<()> { #[cfg(not(feature = "mainnet"))] - msg!("Checking integration {:?} is enabled", integration); + msg!("Checking state account type {:?}", accont_type); - if let Some(acls) = fund.integration_acls() { - for acl in acls { - if acl.name == integration { - return Ok(()); - } - } + if state.account_type == accont_type { + Ok(()) + } else { + Err(AccessError::WrongStateType.into()) } +} - return Err(AccessError::IntegrationDisabled.into()); +pub fn check_integration(state: &StateAccount, integration: Integration) -> Result<()> { + #[cfg(not(feature = "mainnet"))] + msg!("Checking integration {:?} is enabled", integration); + + if state.integrations.contains(&integration) { + Ok(()) + } else { + Err(AccessError::IntegrationDisabled.into()) + } } pub fn check_stake_pool_integration( - fund: &StateAccount, + state: &StateAccount, stake_pool_program: &Pubkey, ) -> Result<()> { let integration = if stake_pool_program == &SPL_STAKE_POOL_PROGRAM_ID { - IntegrationName::SplStakePool + Integration::SplStakePool } else { - IntegrationName::SanctumStakePool + Integration::SanctumStakePool }; - check_integration(fund, integration) + check_integration(state, integration) } diff --git a/anchor/programs/glam/src/state/model/model.rs b/anchor/programs/glam/src/state/model/model.rs index 5a8069f3..01af620e 100644 --- a/anchor/programs/glam/src/state/model/model.rs +++ b/anchor/programs/glam/src/state/model/model.rs @@ -1,44 +1,41 @@ use anchor_lang::prelude::*; -use super::super::acl::*; +use crate::state::accounts::*; -// Fund -// -// Implemented: -// - Openfunds Fund Essential + Core +use super::super::acl::*; #[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)] pub struct StateModel { // Core pub id: Option, + pub account_type: Option, pub name: Option, pub uri: Option, - pub metadata_uri: Option, - pub is_enabled: Option, + pub enabled: Option, // Assets - pub assets: Vec, - pub external_vault_accounts: Vec, + pub assets: Option>, + pub external_vault_accounts: Option>, // Relationships - pub mints: Vec, + pub mints: Option>, pub company: Option, pub owner: Option, pub created: Option, // ACLs - pub delegate_acls: Vec, - pub integration_acls: Vec, - pub drift_market_indexes_perp: Vec, - pub drift_market_indexes_spot: Vec, - pub drift_order_types: Vec, - - // Openfunds - pub is_raw_openfunds: bool, + pub delegate_acls: Option>, + pub integrations: Option>, + pub drift_market_indexes_perp: Option>, + pub drift_market_indexes_spot: Option>, + pub drift_order_types: Option>, + + // Metadata + pub metadata: Option, pub raw_openfunds: Option, } -// Subset of the Openfunds v2 modelled by Glam +// Subset of the Openfunds v2 modeled by Glam #[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)] pub struct FundOpenfundsModel { pub fund_domicile_alpha_2: Option, @@ -59,12 +56,6 @@ pub struct FundOpenfundsModel { pub ucits_version: Option, } -#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)] -pub struct CreatedModel { - pub key: [u8; 8], // seed for computing state PDA - pub owner: Option, -} - // Share Class // // Implemented: @@ -79,22 +70,22 @@ pub struct ShareClassModel { pub uri: Option, // metadata uri // Glam - pub fund_id: Option, + pub state_pubkey: Option, pub asset: Option, - pub image_uri: Option, - - // Openfund - pub is_raw_openfunds: bool, - pub raw_openfunds: Option, + pub image_uri: Option, // TODO: remove? // Acls - pub allowlist: Vec, - pub blocklist: Vec, + pub allowlist: Option>, + pub blocklist: Option>, // Policies - pub lock_up_period_in_seconds: i32, + pub lock_up_period_in_seconds: Option, pub permanent_delegate: Option, - pub default_account_state_frozen: bool, + pub default_account_state_frozen: Option, + + // Metadata + pub is_raw_openfunds: Option, + pub raw_openfunds: Option, } #[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug, Default)] diff --git a/anchor/programs/glam/src/state/model/openfunds.rs b/anchor/programs/glam/src/state/model/openfunds.rs index d6d3e456..9b68d115 100644 --- a/anchor/programs/glam/src/state/model/openfunds.rs +++ b/anchor/programs/glam/src/state/model/openfunds.rs @@ -62,10 +62,9 @@ impl From for Vec { }); } // Derived fields - let is_raw_openfunds = model.is_raw_openfunds; - if !is_raw_openfunds { - //TODO: add Glam extension fields - } + // TODO: impl + // let is_raw_openfunds = model.is_raw_openfunds; + // if !is_raw_openfunds {} res } } @@ -76,11 +75,14 @@ impl From<&ShareClassModel> for Vec { fn from(model: &ShareClassModel) -> Self { let mut res = vec![]; // Derived fields - let is_raw_openfunds = model.is_raw_openfunds; + let is_raw_openfunds = model.is_raw_openfunds.unwrap_or(false); let model = model.clone(); if !is_raw_openfunds { let v: Vec<(Option, ShareClassFieldName)> = vec![ - (pubkey2string(model.fund_id), ShareClassFieldName::FundId), + ( + pubkey2string(model.state_pubkey), + ShareClassFieldName::FundId, + ), (model.image_uri, ShareClassFieldName::ImageUri), ]; v.iter().for_each(|(value, field)| { diff --git a/anchor/programs/glam/src/state/openfunds/share_class.rs b/anchor/programs/glam/src/state/openfunds/share_class.rs index b45cf8d1..29fb2d2e 100644 --- a/anchor/programs/glam/src/state/openfunds/share_class.rs +++ b/anchor/programs/glam/src/state/openfunds/share_class.rs @@ -4,31 +4,36 @@ use anchor_lang::prelude::*; #[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug, strum::Display)] pub enum ShareClassFieldName { + // // Essential + // ISIN, // impl ShareClassCurrency, // impl + + // // Core - AllInFeeApplied, - AllInFeeDate, - AllInFeeIncludesTransactionCosts, - AllInFeeMaximum, + // + // AllInFeeApplied, + // AllInFeeDate, + // AllInFeeIncludesTransactionCosts, + // AllInFeeMaximum, AppliedSubscriptionFeeInFavourOfDistributor, // impl AppliedSubscriptionFeeInFavourOfDistributorReferenceDate, // impl - Benchmark, - CountryLegalRegistration, - CountryMarketingDistribution, - CurrencyHedgeShareClass, + // Benchmark, + // CountryLegalRegistration, + // CountryMarketingDistribution, + // CurrencyHedgeShareClass, CurrencyOfMinimalSubscription, // impl - DistributionDeclarationFrequency, + // DistributionDeclarationFrequency, FullShareClassName, // impl - HasAllInFee, - HasOngoingCharges, + // HasAllInFee, + // HasOngoingCharges, HasPerformanceFee, // impl HasSubscriptionFeeInFavourOfDistributor, // impl InvestmentStatus, // impl - IsETF, - IsRDRCompliant, - IsTrailerFeeClean, + // IsETF, + // IsRDRCompliant, + // IsTrailerFeeClean, ManagementFeeApplied, // impl ManagementFeeAppliedReferenceDate, // impl ManagementFeeMaximum, // impl @@ -40,58 +45,61 @@ pub enum ShareClassFieldName { MinimalSubsequentSubscriptionInAmount, // impl MinimalSubsequentSubscriptionInShares, // impl MinimumSubscriptionFeeInFavourOfDistributor, // impl - OngoingCharges, - OngoingChargesDate, - PerformanceFeeApplied, - PerformanceFeeAppliedReferenceDate, - PerformanceFeeInProspectus, - PerformanceFeeInProspectusReferenceDate, - RecordDateForSRRI, + // OngoingCharges, + // OngoingChargesDate, + // PerformanceFeeApplied, + // PerformanceFeeAppliedReferenceDate, + // PerformanceFeeInProspectus, + // PerformanceFeeInProspectusReferenceDate, + // RecordDateForSRRI, ShareClassDistributionPolicy, // impl ShareClassExtension, // impl ShareClassLaunchDate, // impl ShareClassLifecycle, // impl SRRI, // impl - TERExcludingPerformanceFee, - TERExcludingPerformanceFeeDate, - TERIncludingPerformanceFee, - TERIncludingPerformanceFeeDate, + // TERExcludingPerformanceFee, + // TERExcludingPerformanceFeeDate, + // TERIncludingPerformanceFee, + // TERIncludingPerformanceFeeDate, + + // // Additional - TransferAgentName, - BICOfTransferAgent, - DomicileOfTransferAgent, - FormOfShare, - HasDurationHedge, - TypeOfEqualization, - IsMultiseries, - SeriesIssuance, - SeriesFrequency, - DoesFundIssueSidePocket, - HasRedemptionGates, - TypeOfAlternativeFundStructureVehicle, - BloombergCode, - FIGICode, - AbbreviatedShareClassName, - ValuationFrequency, - NAVPublicationTime, - IsShareClassEligibleForUCITS, - InvestmentStatusDate, + // + // TransferAgentName, + // BICOfTransferAgent, + // DomicileOfTransferAgent, + // FormOfShare, + // HasDurationHedge, + // TypeOfEqualization, + // IsMultiseries, + // SeriesIssuance, + // SeriesFrequency, + // DoesFundIssueSidePocket, + // HasRedemptionGates, + // TypeOfAlternativeFundStructureVehicle, + // BloombergCode, + // FIGICode, + // AbbreviatedShareClassName, + // ValuationFrequency, + // NAVPublicationTime, + // IsShareClassEligibleForUCITS, + // InvestmentStatusDate, LaunchPrice, // impl LaunchPriceCurrency, // impl LaunchPriceDate, // impl - EFAMAMainEFCCategory, - EFAMAEFCClassificationType, - EFAMAActiveEFCClassification, - EFAMAEFCInvestmentTheme, - PricingMethodology, - SinglePricingType, - SwingFactor, - StandardMinimumRemainingAmount, - StandardMinimumRemainingShares, - CurrencyOfMinimumRemainingAmount, - StandardMinimumRemainingCategory, - HurdleRate, - HighWaterMark, + // EFAMAMainEFCCategory, + // EFAMAEFCClassificationType, + // EFAMAActiveEFCClassification, + // EFAMAEFCInvestmentTheme, + // PricingMethodology, + // SinglePricingType, + // SwingFactor, + // StandardMinimumRemainingAmount, + // StandardMinimumRemainingShares, + // CurrencyOfMinimumRemainingAmount, + // StandardMinimumRemainingCategory, + // HurdleRate, + // HighWaterMark, HasAppliedSubscriptionFeeInFavourOfFund, // impl AppliedSubscriptionFeeInFavourOfFund, // impl AppliedSubscriptionFeeInFavourOfFundReferenceDate, // impl @@ -100,84 +108,87 @@ pub enum ShareClassFieldName { AppliedRedemptionFeeInFavourOfFund, // impl AppliedRedemptionFeeInFavourOfFundReferenceDate, // impl MaximumRedemptionFeeInFavourOfFund, // impl - EquivalentTrailerFeeCleanISIN, - HasSeparateDistributionFee, - DistributionFee, - DistributionFeeMaximum, - IASector, + // EquivalentTrailerFeeCleanISIN, + // HasSeparateDistributionFee, + // DistributionFee, + // DistributionFeeMaximum, + // IASector, + + // // Full - AbsorbingFundFullShareClassName, - AbsorbingFundShareClassISIN, - AdministrationFeeMaximum, - AnnualDistributionAtFiscalYearEnd, - AnnualDistributionYieldAtFiscalYearEnd, + // + // AbsorbingFundFullShareClassName, + // AbsorbingFundShareClassISIN, + // AdministrationFeeMaximum, + // AnnualDistributionAtFiscalYearEnd, + // AnnualDistributionYieldAtFiscalYearEnd, AppliedRedemptionFeeInFavourOfDistributor, // impl AppliedRedemptionFeeInFavourOfDistributorReferenceDate, // impl - BankDetailsSSIForPaymentsProvision, - BankDetailsLevelApplication, - BenchmarkBloombergTicker, - CalculationDateOffsetForRedemption, - CalculationDateOffsetForSubscription, - CalendarOrBusinessDaysForCutOffDateOffsetForRedemption, - CalendarOrBusinessDaysForCutOffDateOffsetForSubscription, - CalendarOrBusinessDaysForPrePaymentDaysForSubscription, - CalendarOrBusinessDaysForSettlementPeriodForRedemption, - CalendarOrBusinessDaysForSettlementPeriodForSubscription, - CalendarOrBusinessDaysForTransactions, - CFICode, - ContingentDeferredSalesChargeExitFee, - ContingentDeferredSalesChargeUpfrontFee, - CountryISOCodeAlpha2, - CountryISOCodeAlpha3, - CountryName, - CurrenciesOfMulticurrencyShareClass, + // BankDetailsSSIForPaymentsProvision, + // BankDetailsLevelApplication, + // BenchmarkBloombergTicker, + // CalculationDateOffsetForRedemption, + // CalculationDateOffsetForSubscription, + // CalendarOrBusinessDaysForCutOffDateOffsetForRedemption, + // CalendarOrBusinessDaysForCutOffDateOffsetForSubscription, + // CalendarOrBusinessDaysForPrePaymentDaysForSubscription, + // CalendarOrBusinessDaysForSettlementPeriodForRedemption, + // CalendarOrBusinessDaysForSettlementPeriodForSubscription, + // CalendarOrBusinessDaysForTransactions, + // CFICode, + // ContingentDeferredSalesChargeExitFee, + // ContingentDeferredSalesChargeUpfrontFee, + // CountryISOCodeAlpha2, + // CountryISOCodeAlpha3, + // CountryName, + // CurrenciesOfMulticurrencyShareClass, CurrencyOfMinimalOrMaximumRedemption, // impl - CustodianFeeApplied, - CustodianFeeAppliedReferenceDate, - CustodianFeeMaximum, + // CustodianFeeApplied, + // CustodianFeeAppliedReferenceDate, + // CustodianFeeMaximum, CutOffDateOffsetForRedemption, // impl CutOffDateOffsetForSubscription, // impl CutOffTimeForRedemption, // impl CutOffTimeForSubscription, // impl - CutOffTimeForSwitchIn, - CutOffTimeForSwitchOut, - DealingDaysOfMultipleRedemptionTradeCycles, - DealingDaysOfMultipleSubscriptionTradeCycles, - DisseminationRecipient, - DistributionFeeReferenceDate, - DoesShareClassApplyMandatoryConversion, - DoesShareClassApplyPartialDealingDays, - DoesShareClassApplyPartialPaymentDays, - DormantEndDate, - DormantStartDate, - ExDividendDateCalendar, - ExitCostDescription, - HasContingentDeferredSalesChargeFee, - HasDilutionLevyAppliedByFund, - HasEqualizationMethodForDistribution, - HasEqualizationMethodForPerformanceFee, - HasForcedRedemption, - HasForwardPricing, - HasHighWaterMark, + // CutOffTimeForSwitchIn, + // CutOffTimeForSwitchOut, + // DealingDaysOfMultipleRedemptionTradeCycles, + // DealingDaysOfMultipleSubscriptionTradeCycles, + // DisseminationRecipient, + // DistributionFeeReferenceDate, + // DoesShareClassApplyMandatoryConversion, + // DoesShareClassApplyPartialDealingDays, + // DoesShareClassApplyPartialPaymentDays, + // DormantEndDate, + // DormantStartDate, + // ExDividendDateCalendar, + // ExitCostDescription, + // HasContingentDeferredSalesChargeFee, + // HasDilutionLevyAppliedByFund, + // HasEqualizationMethodForDistribution, + // HasEqualizationMethodForPerformanceFee, + // HasForcedRedemption, + // HasForwardPricing, + // HasHighWaterMark, HasLockUpForRedemption, // impl - HasPreNoticeForSwitchIn, - HasPreNoticeForSwitchOut, - HasPrePaymentForSubscription, + // HasPreNoticeForSwitchIn, + // HasPreNoticeForSwitchOut, + // HasPrePaymentForSubscription, HasRedemptionFeeInFavourOfDistributor, // impl - HasTripartiteReport, - InvestmentStatusDescription, - IrregularRedemptionDealingDays, - IrregularSubscriptionDealingDays, - IsMulticurrencyShareClass, - IsRestrictedToSeparateFeeArrangement, - IsStructuredFinanceProduct, + // HasTripartiteReport, + // InvestmentStatusDescription, + // IrregularRedemptionDealingDays, + // IrregularSubscriptionDealingDays, + // IsMulticurrencyShareClass, + // IsRestrictedToSeparateFeeArrangement, + // IsStructuredFinanceProduct, IsValidISIN, // impl - LiquidationStartDate, + // LiquidationStartDate, LockUpComment, // impl LockUpPeriodInDays, // impl ManagementFeeMinimum, // impl - MandatoryShareConversionDescriptionDetails, - MarketsRelevantToFundTradingCalendar, + // MandatoryShareConversionDescriptionDetails, + // MarketsRelevantToFundTradingCalendar, MaximalNumberOfPossibleDecimalsAmount, // impl MaximalNumberOfPossibleDecimalsNAV, // impl MaximalNumberOfPossibleDecimalsShares, // impl @@ -186,7 +197,7 @@ pub enum ShareClassFieldName { MaximumRedemptionFeeInFavourOfDistributor, // impl MaximumSubsequentRedemptionInAmount, // impl MaximumSubsequentRedemptionInShares, // impl - MergerRatio, + // MergerRatio, MinimalInitialRedemptionInAmount, // impl MinimalInitialRedemptionInShares, // impl MinimalRedemptionCategory, // impl @@ -195,51 +206,54 @@ pub enum ShareClassFieldName { MinimumRedemptionFeeInFavourOfDistributor, // impl MinimumRedemptionFeeInFavourOfFund, // impl MinimumSubscriptionFeeInFavourOfFund, // impl - MonthlyRedemptionDealingDays, - MonthlySubscriptionDealingDays, - NasdaqFundNetworkNFNIdentifier, - NoTradingDate, - NumberOfPossibleRedemptionsWithinPeriod, - NumberOfPossibleSubscriptionsWithinPeriod, - PartialDealingDaysDateAndTime, - PartialPaymentDaysDateAndTime, - PaymentDateCalendar, + // MonthlyRedemptionDealingDays, + // MonthlySubscriptionDealingDays, + // NasdaqFundNetworkNFNIdentifier, + // NoTradingDate, + // NumberOfPossibleRedemptionsWithinPeriod, + // NumberOfPossibleSubscriptionsWithinPeriod, + // PartialDealingDaysDateAndTime, + // PartialPaymentDaysDateAndTime, + // PaymentDateCalendar, PerformanceFeeMinimum, // impl - PreNoticeCutOffForRedemption, - PreNoticeCutOffForSubscription, - PrePaymentCutOffTimeForSubscription, - PrePaymentDaysForSubscription, - RecordDateCalendar, - RedemptionTradeCyclePeriod, + // PreNoticeCutOffForRedemption, + // PreNoticeCutOffForSubscription, + // PrePaymentCutOffTimeForSubscription, + // PrePaymentDaysForSubscription, + // RecordDateCalendar, + // RedemptionTradeCyclePeriod, RoundingMethodForPrices, // impl RoundingMethodForRedemptionInAmount, // impl RoundingMethodForRedemptionInShares, // impl RoundingMethodForSubscriptionInAmount, // impl RoundingMethodForSubscriptionInShares, // impl - SettlementPeriodForRedemption, - SettlementPeriodForSubscription, - SettlementPeriodForSwitchIn, - SettlementPeriodForSwitchOut, + // SettlementPeriodForRedemption, + // SettlementPeriodForSubscription, + // SettlementPeriodForSwitchIn, + // SettlementPeriodForSwitchOut, ShareClassDividendType, // impl - SingleRegisterAccountRestrictions, - SubscriptionPeriodEndDate, - SubscriptionPeriodStartDate, - SubscriptionTradeCyclePeriod, - SwitchInNoticePeriod, - SwitchOutNoticePeriod, - TerminationDate, - TimeZoneForCutOff, - TimeZoneForCutOffUsingTZDatabase, - ValuationFrequencyDetail, - ValuationReduction, - WeeklyRedemptionDealingDays, - WeeklySubscriptionDealingDays, - YearlyRedemptionDealingDays, - YearlySubscriptionDealingDays, + // SingleRegisterAccountRestrictions, + // SubscriptionPeriodEndDate, + // SubscriptionPeriodStartDate, + // SubscriptionTradeCyclePeriod, + // SwitchInNoticePeriod, + // SwitchOutNoticePeriod, + // TerminationDate, + // TimeZoneForCutOff, + // TimeZoneForCutOffUsingTZDatabase, + // ValuationFrequencyDetail, + // ValuationReduction, + // WeeklyRedemptionDealingDays, + // WeeklySubscriptionDealingDays, + // YearlyRedemptionDealingDays, + // YearlySubscriptionDealingDays, // Full | Country CUSIP, // impl Valor, // impl + + // // Glam Extensions + // FundId, // impl ImageUri, // impl } diff --git a/anchor/src/client/base.ts b/anchor/src/client/base.ts index ce084978..decb6a16 100644 --- a/anchor/src/client/base.ts +++ b/anchor/src/client/base.ts @@ -39,7 +39,7 @@ import { import { ClusterNetwork, GlamClientConfig } from "../clientConfig"; import { StateAccount, - MetadataAccount, + OpenfundsMetadataAccount, StateModel, ShareClassModel, } from "../models"; @@ -263,15 +263,18 @@ export class BaseClient { tx: VersionedTransaction | Transaction, signerOverride?: Keypair, ): Promise { - // Use dedicated connection for sending transactions if available - const txConnection = new Connection( - process.env?.NEXT_PUBLIC_TX_RPC || - process.env.TX_RPC || - this.provider.connection.rpcEndpoint, - { - commitment: "confirmed", - }, - ); + // Mainnet only: use dedicated connection for sending transactions if available + const txConnection = + this.cluster === ClusterNetwork.Mainnet + ? new Connection( + process.env?.NEXT_PUBLIC_TX_RPC || + process.env.TX_RPC || + this.provider.connection.rpcEndpoint, + { + commitment: "confirmed", + }, + ) + : this.provider.connection; // This is just a convenient method so that in tests we can send legacy // txs, for example transfer SOL, create ATA, etc. @@ -372,7 +375,7 @@ export class BaseClient { const owner = stateModel.owner?.pubkey || this.getSigner(); const [pda, _bump] = PublicKey.findProgramAddressSync( [ - anchor.utils.bytes.utf8.encode("fund"), + anchor.utils.bytes.utf8.encode("state"), owner.toBuffer(), Uint8Array.from(createdKey), ], @@ -383,7 +386,7 @@ export class BaseClient { getVaultPda(statePda: PublicKey): PublicKey { const [pda, _bump] = PublicKey.findProgramAddressSync( - [Buffer.from("treasury"), statePda.toBuffer()], + [Buffer.from("vault"), statePda.toBuffer()], this.program.programId, ); return pda; @@ -476,7 +479,7 @@ export class BaseClient { getOpenfundsPda(statePda: PublicKey): PublicKey { const [pda, _] = PublicKey.findProgramAddressSync( - [Buffer.from("openfunds"), statePda.toBuffer()], + [Buffer.from("metadata"), statePda.toBuffer()], this.program.programId, ); return pda; @@ -485,7 +488,7 @@ export class BaseClient { getShareClassPda(statePda: PublicKey, mintIdx: number = 0): PublicKey { const [pda, _] = PublicKey.findProgramAddressSync( [ - Buffer.from("share"), + Buffer.from("mint"), Uint8Array.from([mintIdx % 256]), statePda.toBuffer(), ], @@ -501,26 +504,23 @@ export class BaseClient { getName(stateModel: Partial) { const name = stateModel.name || - stateModel.rawOpenfunds?.legalFundNameIncludingUmbrella || - (stateModel.mints && stateModel.mints[0]?.name); + (stateModel.mints && stateModel.mints[0]?.name) || + stateModel.rawOpenfunds?.legalFundNameIncludingUmbrella; if (!name) { throw new Error("Name not be inferred from state model"); } return name; } - // @ts-ignore public async fetchStateAccount(statePda: PublicKey): Promise { - // stateAccount is a type alias of fundAccount - return await this.program.account.fundAccount.fetch(statePda); + return await this.program.account.stateAccount.fetch(statePda); } - public async fetchMetadataAccount( + public async fetchOpenfundsMetadataAccount( state: PublicKey, - ): Promise { + ): Promise { const openfunds = this.getOpenfundsPda(state); - // metadataAccount is a type alias of fundMetadataAccount - return await this.program.account.fundMetadataAccount.fetch(openfunds); + return await this.program.account.openfundsMetadataAccount.fetch(openfunds); } public async fetchShareClassAccount( @@ -616,14 +616,15 @@ export class BaseClient { */ public async fetchState(statePda: PublicKey): Promise { const stateAccount = await this.fetchStateAccount(statePda); - const metadataAccount = await this.fetchMetadataAccount(statePda); + const openfundsMetadataAccount = + await this.fetchOpenfundsMetadataAccount(statePda); if (stateAccount.mints.length > 0) { const firstShareClass = await this.fetchShareClassAccount(statePda, 0); return StateModel.fromOnchainAccounts( statePda, stateAccount, - metadataAccount, + openfundsMetadataAccount, firstShareClass, this.program.programId, ); @@ -632,19 +633,19 @@ export class BaseClient { return StateModel.fromOnchainAccounts( statePda, stateAccount, - metadataAccount, + openfundsMetadataAccount, undefined, this.program.programId, ); } public async fetchAllGlamStates(): Promise { - const stateAccounts = await this.program.account.fundAccount.all(); - const openfundsAccounts = - await this.program.account.fundMetadataAccount.all(); + const stateAccounts = await this.program.account.stateAccount.all(); + const openfundsMetadataAccounts = + await this.program.account.openfundsMetadataAccount.all(); - let openfundsCache = new Map(); - openfundsAccounts.forEach((of) => { + let openfundsCache = new Map(); + openfundsMetadataAccounts.forEach((of) => { openfundsCache.set(of.publicKey.toBase58(), of.account); }); @@ -665,12 +666,12 @@ export class BaseClient { mintCache.set(mintAddresses[j].toBase58(), mintInfo); }); - return stateAccounts.map((f) => + return stateAccounts.map((s) => StateModel.fromOnchainAccounts( - f.publicKey, - f.account, - openfundsCache.get(f.account.metadata.toBase58()), - mintCache.get(f.account.mints[0] ? f.account.mints[0].toBase58() : ""), + s.publicKey, + s.account, + openfundsCache.get(s.account.metadata?.pubkey.toBase58() || ""), + mintCache.get(s.account.mints[0]?.toBase58() || ""), this.program.programId, ), ); diff --git a/anchor/src/client/drift.ts b/anchor/src/client/drift.ts index 3b91594c..6371746c 100644 --- a/anchor/src/client/drift.ts +++ b/anchor/src/client/drift.ts @@ -552,6 +552,7 @@ export class DriftClient { const driftState = await getDriftStateAccountPublicKey(this.DRIFT_PROGRAM); const tx = await this.base.program.methods + // @ts-ignore .driftPlaceOrders([orderParams]) .accountsPartial({ state: statePda, @@ -590,6 +591,7 @@ export class DriftClient { ); const tx = await this.base.program.methods + // @ts-ignore .driftCancelOrders(marketType, marketIndex, direction) .accountsPartial({ state: glamState, diff --git a/anchor/src/client/investor.ts b/anchor/src/client/investor.ts index 9590bed4..70d91052 100644 --- a/anchor/src/client/investor.ts +++ b/anchor/src/client/investor.ts @@ -82,8 +82,8 @@ export class InvestorClient { const signer = txOptions.signer || this.base.getSigner(); // share class token to receive - const shareClass = this.base.getShareClassPda(statePda, shareClassId); - const signerShareAta = this.base.getShareClassAta(signer, shareClass); + const shareClassMint = this.base.getShareClassPda(statePda, shareClassId); + const signerShareAta = this.base.getShareClassAta(signer, shareClassMint); // asset token to transfer const assetMeta = this.base.getAssetMeta(asset.toBase58()); @@ -107,7 +107,7 @@ export class InvestorClient { if (!stateModel) { stateModel = await this.base.fetchState(statePda); } - let remainingAccounts = stateModel.assets.flatMap((asset) => { + let remainingAccounts = (stateModel.assets || []).flatMap((asset) => { const assetMeta = this.base.getAssetMeta(asset.toBase58()); const vaultAta = this.base.getVaultAta( statePda, @@ -128,7 +128,7 @@ export class InvestorClient { }); remainingAccounts = remainingAccounts.concat( - stateModel.externalVaultAccounts.map((address) => ({ + (stateModel.externalVaultAccounts || []).map((address) => ({ pubkey: address, isSigner: false, isWritable: false, @@ -156,7 +156,7 @@ export class InvestorClient { signer, signerShareAta, signer, - shareClass, + shareClassMint, TOKEN_2022_PROGRAM_ID, ), ]; @@ -186,12 +186,11 @@ export class InvestorClient { } } - // @ts-ignore const tx = await this.base.program.methods .subscribe(0, amount, skipState) .accounts({ state: statePda, - shareClass, + shareClassMint, asset, vaultAta, signerAssetAta, diff --git a/anchor/src/client/shareclass.ts b/anchor/src/client/shareclass.ts index 9a7bf698..0fc39b49 100644 --- a/anchor/src/client/shareclass.ts +++ b/anchor/src/client/shareclass.ts @@ -56,12 +56,10 @@ export class ShareClassClient { const openfunds = this.base.getOpenfundsPda(state); const shareClassMint = this.base.getShareClassPda(state, shareClassId); - // @ts-ignore Type instantiation is excessively deep and possibly infinite. return await this.base.program.methods .closeShareClass(shareClassId) .accounts({ state, - metadata: openfunds, shareClassMint, }) .instruction(); @@ -80,7 +78,6 @@ export class ShareClassClient { .closeShareClass(shareClassId) .accounts({ state, - metadata: openfunds, shareClassMint, }) .rpc(); @@ -161,7 +158,7 @@ export class ShareClassClient { * @param shareClassId * @param recipient Recipient's wallet address * @param amount Amount of shares to mint - * @param forceThaw If true, force thaw token account before minting + * @param forceThaw If true, force unfreezing token account before minting * @param txOptions * @returns Transaction signature */ diff --git a/anchor/src/client/state.ts b/anchor/src/client/state.ts index 037d1065..9d220f48 100644 --- a/anchor/src/client/state.ts +++ b/anchor/src/client/state.ts @@ -21,6 +21,10 @@ import { ManagerModel, ShareClassModel, ShareClassOpenfundsModel, + CreatedModel, + Metadata, + StateIdlModel, + StateModelType, } from "../models"; import { WSOL } from "../constants"; @@ -41,31 +45,30 @@ export class StateClient { const mints = stateModel.mints; stateModel.mints = []; - if (mints.length > 1) { + if (mints && mints.length > 1) { throw new Error("Multiple mints not supported"); } // No share class, only need to initialize the fund - if (mints.length === 0) { - // @ts-ignore + if (mints && mints.length === 0) { const txSig = await this.base.program.methods .initializeState(stateModel) .accountsPartial({ state: statePda, vault, - metadata: openfunds, + openfundsMetadata: openfunds, }) .rpc(); return [txSig, statePda]; } - if (singleTx) { + if (mints && mints.length > 0 && singleTx) { const initStateIx = await this.base.program.methods .initializeState(stateModel) .accountsPartial({ state: statePda, vault, - metadata: openfunds, + openfundsMetadata: openfunds, }) .instruction(); @@ -75,36 +78,33 @@ export class StateClient { .accounts({ state: statePda, shareClassMint, - metadata: openfunds, }) .preInstructions([initStateIx]) .rpc(); return [txSig, statePda]; } - // @ts-ignore const txSig = await this.base.program.methods .initializeState(stateModel) .accountsPartial({ state: statePda, vault, - metadata: openfunds, + openfundsMetadata: openfunds, }) .rpc(); const addShareClassTxs = await Promise.all( - mints.map(async (shareClass, j: number) => { + (mints || []).map(async (shareClass, j: number) => { const shareClassMint = this.base.getShareClassPda(statePda, j); // FIXME: setting rawOpenfunds to null is a workarond for // Access violation in stack frame 5 at address 0x200005ff8 of size 8 - shareClass.rawOpenfunds = null; + // shareClass.rawOpenfunds = null; return await this.base.program.methods .addShareClass(shareClass) .accounts({ state: statePda, shareClassMint, - metadata: openfunds, }) .preInstructions([ // FIXME: estimate compute units @@ -122,12 +122,22 @@ export class StateClient { updated: Partial, txOptions: TxOptions = {}, ): Promise { - return await this.base.program.methods + const tx = await this.updateStateTx(statePda, updated, txOptions); + return await this.base.sendAndConfirm(tx); + } + + public async updateStateTx( + statePda: PublicKey, + updated: Partial, + txOptions: TxOptions, + ): Promise { + const tx = await this.base.program.methods .updateState(new StateModel(updated)) .accounts({ state: statePda, }) - .rpc(); + .transaction(); + return await this.base.intoVersionedTransaction({ tx, ...txOptions }); } public async closeState( @@ -140,7 +150,6 @@ export class StateClient { .closeState() .accounts({ state: statePda, - metadata: openfunds, }) .preInstructions(txOptions.preInstructions || []) .transaction(); @@ -156,35 +165,33 @@ export class StateClient { const owner = this.base.getSigner(); const defaultDate = new Date().toISOString().split("T")[0]; - // createdKey = hash fund name and get first 8 bytes + partialStateModel.name = this.base.getName(partialStateModel); + + // createdKey = hash state name and get first 8 bytes // useful for computing state account PDA in the future - const createdKey = [ - ...Buffer.from( - anchor.utils.sha256.hash(this.base.getName(partialStateModel)), - ).subarray(0, 8), - ]; - partialStateModel.created = { - key: createdKey, - owner, - }; + partialStateModel.created = new CreatedModel({ + key: [ + ...Buffer.from( + anchor.utils.sha256.hash(partialStateModel.name), + ).subarray(0, 8), + ], + }); partialStateModel.rawOpenfunds = new FundOpenfundsModel( partialStateModel.rawOpenfunds ?? {}, ); partialStateModel.owner = new ManagerModel({ - ...(partialStateModel.owner || {}), + ...partialStateModel.owner, pubkey: owner, }); - partialStateModel.company = new CompanyModel( - partialStateModel.company || {}, - ); + partialStateModel.company = new CompanyModel({ + ...partialStateModel.company, + }); if (partialStateModel.mints?.length == 1) { const shareClass = partialStateModel.mints[0]; - partialStateModel.name = partialStateModel.name || shareClass.name; - partialStateModel.rawOpenfunds.fundCurrency = partialStateModel.rawOpenfunds?.fundCurrency || shareClass.rawOpenfunds?.shareClassCurrency || @@ -196,7 +203,7 @@ export class StateClient { throw new Error("Fund with more than 1 share class is not supported"); } - if (partialStateModel.isEnabled) { + if (partialStateModel.enabled) { partialStateModel.rawOpenfunds.fundLaunchDate = partialStateModel.rawOpenfunds?.fundLaunchDate || defaultDate; } @@ -205,9 +212,11 @@ export class StateClient { const statePda = this.base.getStatePda(partialStateModel); partialStateModel.uri = partialStateModel.uri || `https://gui.glam.systems/products/${statePda}`; - partialStateModel.metadataUri = - partialStateModel.metadataUri || - `https://api.glam.systems/v0/openfunds?fund=${statePda}&format=csv`; + partialStateModel.metadata = new Metadata({ + ...partialStateModel.metadata, + uri: `https://api.glam.systems/v0/openfunds?fund=${statePda}`, + template: { openfunds: {} }, + }); // build openfunds models for each share classes (partialStateModel.mints || []).forEach( @@ -227,14 +236,13 @@ export class StateClient { const sharePda = this.base.getShareClassPda(statePda, i); shareClass.uri = `https://api.glam.systems/metadata/${sharePda}`; - shareClass.fundId = statePda; + shareClass.statePubkey = statePda; shareClass.imageUri = `https://api.glam.systems/v0/sparkle?key=${sharePda}&format=png`; }, ); // convert partial share class models to full share class models partialStateModel.mints = (partialStateModel.mints || []).map( - // @ts-ignore (s) => new ShareClassModel(s), ); @@ -254,7 +262,11 @@ export class StateClient { txOptions: TxOptions = {}, ): Promise { const updated = new StateModel({ - delegateAcls: delegates.map((pubkey) => ({ pubkey, permissions: [] })), + delegateAcls: delegates.map((pubkey) => ({ + pubkey, + permissions: [], + expiresAt: new BN(0), + })), }); return await this.updateState(statePda, updated, txOptions); } @@ -264,8 +276,7 @@ export class StateClient { delegateAcls: DelegateAcl[], txOptions: TxOptions = {}, ): Promise { - const updatedFund = new StateModel({ delegateAcls }); - return await this.updateState(statePda, updatedFund, txOptions); + return await this.updateState(statePda, { delegateAcls }, txOptions); } public async setSubscribeRedeemEnabled( @@ -326,7 +337,6 @@ export class StateClient { tokenAccounts: PublicKey[], txOptions: TxOptions, ): Promise { - // @ts-ignore const tx = await this.base.program.methods .closeTokenAccounts() .accounts({ diff --git a/anchor/src/models.ts b/anchor/src/models.ts index cabc35e9..3e5fe503 100644 --- a/anchor/src/models.ts +++ b/anchor/src/models.ts @@ -3,6 +3,7 @@ import { Glam, GlamIDLJson } from "./glamExports"; import { PublicKey } from "@solana/web3.js"; import { ExtensionType, getExtensionData, Mint } from "@solana/spl-token"; import { TokenMetadata, unpack } from "@solana/spl-token-metadata"; +import { BN } from "@coral-xyz/anchor"; export const GlamIntegrations = GlamIDLJson?.types @@ -18,53 +19,54 @@ export const VaultIntegrations = GlamIntegrations.filter((i) => i !== "Mint"); const GLAM_PROGRAM_ID_DEFAULT = new PublicKey(GlamIDLJson.address); -// FIXME: Anchor is not able to handle enums with too many options -// The culprit of so many broken types suppressed by @ts-ignore is ShareClassFieldName, which -// has 100+ options. - -// @ts-ignore -export type StateAccount = IdlAccounts["fundAccount"]; -export type MetadataAccount = IdlAccounts["fundMetadataAccount"]; +export type StateAccountType = IdlTypes["accountType"]; +export type StateAccount = IdlAccounts["stateAccount"]; +export type OpenfundsMetadataAccount = + IdlAccounts["openfundsMetadataAccount"]; export type StateModelType = IdlTypes["stateModel"]; export class StateIdlModel implements StateModelType { id: PublicKey | null; + accountType: StateAccountType | null; name: string | null; uri: string | null; - metadataUri: string | null; - isEnabled: boolean | null; - assets: PublicKey[]; - externalVaultAccounts: PublicKey[]; - mints: ShareClassModel[]; + enabled: boolean | null; + + assets: PublicKey[] | null; + externalVaultAccounts: PublicKey[] | null; + + mints: ShareClassModel[] | null; company: CompanyModel | null; owner: ManagerModel | null; created: CreatedModel | null; - delegateAcls: DelegateAcl[]; - integrationAcls: IntegrationAcl[]; - driftMarketIndexesPerp: number[]; - driftMarketIndexesSpot: number[]; - driftOrderTypes: number[]; - isRawOpenfunds: boolean; + + delegateAcls: DelegateAcl[] | null; + integrations: Integration[] | null; + driftMarketIndexesPerp: number[] | null; + driftMarketIndexesSpot: number[] | null; + driftOrderTypes: number[] | null; + + metadata: Metadata | null; rawOpenfunds: FundOpenfundsModel | null; constructor(data: Partial) { this.id = data.id ?? null; + this.accountType = data.accountType ?? null; this.name = data.name ?? null; this.uri = data.uri ?? null; - this.metadataUri = data.metadataUri ?? null; - this.isEnabled = data.isEnabled ?? null; - this.assets = data.assets ?? []; - this.externalVaultAccounts = data.externalVaultAccounts ?? []; - this.mints = data.mints ?? []; + this.enabled = data.enabled ?? null; + this.assets = data.assets ?? null; + this.externalVaultAccounts = data.externalVaultAccounts ?? null; + this.mints = data.mints ?? null; this.company = data.company ?? null; this.owner = data.owner ?? null; this.created = data.created ?? null; - this.delegateAcls = data.delegateAcls ?? []; - this.integrationAcls = data.integrationAcls ?? []; - this.driftMarketIndexesPerp = data.driftMarketIndexesPerp ?? []; - this.driftMarketIndexesSpot = data.driftMarketIndexesSpot ?? []; - this.driftOrderTypes = data.driftOrderTypes ?? []; - this.isRawOpenfunds = data.isRawOpenfunds ?? false; + this.delegateAcls = data.delegateAcls ?? null; + this.integrations = data.integrations ?? null; + this.driftMarketIndexesPerp = data.driftMarketIndexesPerp ?? null; + this.driftMarketIndexesSpot = data.driftMarketIndexesSpot ?? null; + this.driftOrderTypes = data.driftOrderTypes ?? null; + this.metadata = data.metadata ?? null; this.rawOpenfunds = data.rawOpenfunds ?? null; } } @@ -88,7 +90,7 @@ export class StateModel extends StateIdlModel { throw new Error("Fund ID not set"); } const [pda, _bump] = PublicKey.findProgramAddressSync( - [Buffer.from("treasury"), this.id.toBuffer()], + [Buffer.from("vault"), this.id.toBuffer()], this.glamProgramId, ); return pda; @@ -99,37 +101,37 @@ export class StateModel extends StateIdlModel { throw new Error("Fund ID not set"); } const [pda, _] = PublicKey.findProgramAddressSync( - [Buffer.from("openfunds"), this.id.toBuffer()], + [Buffer.from("metadata"), this.id.toBuffer()], this.glamProgramId, ); return pda; } get productType() { - if (this.mints.length === 0) { - return "Vault"; - } - if ( - // @ts-ignore - this.integrationAcls.find((acl) => Object.keys(acl.name)[0] === "mint") - ) { - return "Mint"; - } - return "Fund"; + // @ts-ignore + const val = Object.keys(this.accountType)[0]; + return String(val).charAt(0).toUpperCase() + String(val).slice(1); + } + + get launchDate() { + const createdAt = this.created?.createdAt.toNumber() ?? 0; + return this.rawOpenfunds?.fundLaunchDate || createdAt + ? new Date(createdAt * 1000).toISOString().split("T")[0] + : "Unknown"; } get shareClassMints() { - if (this.mints.length > 0 && !this.id) { + if (this.mints && this.mints.length > 0 && !this.id) { // If share classes are set, fund ID should be set as well throw new Error("Fund ID not set"); } - return this.mints.map((_, i) => + return (this.mints || []).map((_, i) => ShareClassModel.mintAddress(this.id!, i, this.glamProgramId), ); } get sparkleKey() { - if (this.mints.length === 0) { + if (!this.mints || this.mints.length === 0) { return this.idStr; } return this.shareClassMints[0].toBase58() || this.idStr; @@ -139,13 +141,13 @@ export class StateModel extends StateIdlModel { * Build a StateModel from onchain accounts * * @param stateAccount provides core fund data - * @param openfundsAccount includes fund rawOpenfunds data and share class rawOpenfunds data + * @param openfundsMetadataAccount includes fund rawOpenfunds data and share class rawOpenfunds data * @param shareClassMint provides share class data */ static fromOnchainAccounts( statePda: PublicKey, stateAccount: StateAccount, - openfundsAccount?: MetadataAccount, + openfundsMetadataAccount?: OpenfundsMetadataAccount, shareClassMint?: Mint, glamProgramId: PublicKey = GLAM_PROGRAM_ID_DEFAULT, ) { @@ -153,8 +155,13 @@ export class StateModel extends StateIdlModel { id: statePda, name: stateAccount.name, uri: stateAccount.uri, + accountType: stateAccount.accountType, + metadata: stateAccount.metadata, + assets: stateAccount.assets, + created: stateAccount.created, + delegateAcls: stateAccount.delegateAcls, + integrations: stateAccount.integrations, owner: new ManagerModel({ pubkey: stateAccount.owner }), - metadataUri: stateAccount.metadataUri, mints: [], }; @@ -167,13 +174,13 @@ export class StateModel extends StateIdlModel { // @ts-ignore stateModel[name] = value; } else { - console.warn(`Fund param ${name} not found in FundIdlModel`); + console.warn(`State param ${name} not found in StateIdlModel`); } }); // Build stateModel.rawOpenfunds from openfunds account const fundOpenfundsFields = {}; - openfundsAccount?.fund.forEach((param) => { + openfundsMetadataAccount?.fund.forEach((param) => { const name = Object.keys(param.name)[0]; const value = param.value; // @ts-ignore @@ -184,7 +191,7 @@ export class StateModel extends StateIdlModel { // Build the array of ShareClassModel stateAccount.mints.forEach((_, i) => { const shareClassIdlModel = {} as any; - shareClassIdlModel["fundId"] = statePda; + shareClassIdlModel["statePubkey"] = statePda; stateAccount.params[i + 1].forEach((param) => { const name = Object.keys(param.name)[0]; @@ -201,9 +208,9 @@ export class StateModel extends StateIdlModel { } }); - if (openfundsAccount) { + if (openfundsMetadataAccount) { const shareClassOpenfundsFields = {}; - openfundsAccount.shareClasses[i].forEach((param) => { + openfundsMetadataAccount.shareClasses[i].forEach((param) => { const name = Object.keys(param.name)[0]; const value = param.value; // @ts-ignore @@ -246,7 +253,6 @@ export class StateModel extends StateIdlModel { stateModel.rawOpenfunds?.legalFundNameIncludingUmbrella || (stateModel.mints && stateModel.mints[0]?.name); - // @ts-ignore return new StateModel(stateModel, glamProgramId); } } @@ -297,31 +303,35 @@ export class ShareClassIdlModel implements ShareClassModelType { symbol: string | null; name: string | null; uri: string | null; - fundId: PublicKey | null; + + statePubkey: PublicKey | null; asset: PublicKey | null; imageUri: string | null; - isRawOpenfunds: boolean; - rawOpenfunds: ShareClassOpenfundsModel | null; - allowlist: PublicKey[]; - blocklist: PublicKey[]; - lockUpPeriodInSeconds: number; + + allowlist: PublicKey[] | null; + blocklist: PublicKey[] | null; + + lockUpPeriodInSeconds: number | null; permanentDelegate: PublicKey | null; - defaultAccountStateFrozen: boolean; + defaultAccountStateFrozen: boolean | null; + + isRawOpenfunds: boolean | null; + rawOpenfunds: ShareClassOpenfundsModel | null; constructor(data: Partial) { this.symbol = data.symbol ?? null; this.name = data.name ?? null; this.uri = data.uri ?? null; - this.fundId = data.fundId ?? null; + this.statePubkey = data.statePubkey ?? null; this.asset = data.asset ?? null; this.imageUri = data.imageUri ?? null; - this.isRawOpenfunds = data.isRawOpenfunds ?? false; + this.isRawOpenfunds = data.isRawOpenfunds ?? null; this.rawOpenfunds = data.rawOpenfunds ?? null; - this.allowlist = data.allowlist ?? []; - this.blocklist = data.blocklist ?? []; - this.lockUpPeriodInSeconds = data.lockUpPeriodInSeconds ?? 0; + this.allowlist = data.allowlist ?? null; + this.blocklist = data.blocklist ?? null; + this.lockUpPeriodInSeconds = data.lockUpPeriodInSeconds ?? null; this.permanentDelegate = data.permanentDelegate ?? null; - this.defaultAccountStateFrozen = data.defaultAccountStateFrozen ?? false; + this.defaultAccountStateFrozen = data.defaultAccountStateFrozen ?? null; } } export class ShareClassModel extends ShareClassIdlModel { @@ -335,7 +345,7 @@ export class ShareClassModel extends ShareClassIdlModel { glamProgramId: PublicKey = GLAM_PROGRAM_ID_DEFAULT, ): PublicKey { const [pda, _] = PublicKey.findProgramAddressSync( - [Buffer.from("share"), Uint8Array.from([idx % 256]), statePda.toBuffer()], + [Buffer.from("mint"), Uint8Array.from([idx % 256]), statePda.toBuffer()], glamProgramId, ); return pda; @@ -433,6 +443,19 @@ export class CompanyModel implements CompanyModelType { } } +export type MetadataType = IdlTypes["metadata"]; +export class Metadata implements MetadataType { + template: IdlTypes["metadataTemplate"]; + pubkey: PublicKey; + uri: string; + + constructor(data: Partial) { + this.template = data.template!; + this.pubkey = data.pubkey ?? new PublicKey(0); + this.uri = data.uri ?? ""; + } +} + export type ManagerModelType = IdlTypes["managerModel"]; export class ManagerModel implements ManagerModelType { portfolioManagerName: string | null; @@ -449,11 +472,13 @@ export class ManagerModel implements ManagerModelType { export type CreatedModelType = IdlTypes["createdModel"]; export class CreatedModel implements CreatedModelType { key: number[]; // Uint8Array; - owner: PublicKey | null; + createdBy: PublicKey; + createdAt: BN; constructor(obj: Partial) { - this.key = obj.key ?? []; - this.owner = obj.owner ?? null; + this.key = obj.key ?? [0, 0, 0, 0, 0, 0, 0, 0]; + this.createdBy = obj.createdBy ?? new PublicKey(0); + this.createdAt = obj.createdAt ?? new BN(0); } } @@ -462,21 +487,13 @@ export type DelegateAclType = IdlTypes["delegateAcl"]; export class DelegateAcl implements DelegateAclType { pubkey: PublicKey; permissions: Permission[]; + expiresAt: BN; constructor(obj: Partial) { this.pubkey = obj.pubkey!; this.permissions = obj.permissions ?? []; + this.expiresAt = obj.expiresAt ?? new BN(0); } } -export type IntegrationName = IdlTypes["integrationName"]; -export type IntegrationAclType = IdlTypes["integrationAcl"]; -export class IntegrationAcl implements IntegrationAclType { - name: IntegrationName; - features: { all: {} }[]; - - constructor(obj: Partial) { - this.name = obj.name!; - this.features = obj.features ?? []; - } -} +export type Integration = IdlTypes["integration"]; diff --git a/anchor/src/react/glam.tsx b/anchor/src/react/glam.tsx index 386dcdfd..b42e584b 100644 --- a/anchor/src/react/glam.tsx +++ b/anchor/src/react/glam.tsx @@ -94,24 +94,24 @@ const glamStatesListAtom = atomWithStorage( // In order to properly deser states, we need to // convert string -> pubkey (and maybe more in future) -const deserializeGlamStateCache = (f: any) => { - if (!f) { +const deserializeGlamStateCache = (s: any) => { + if (!s) { return undefined; } - if (typeof f.pubkey === "string") { - f.address = f.pubkey; - f.pubkey = new PublicKey(f.pubkey); + if (typeof s.pubkey === "string") { + s.address = s.pubkey; + s.pubkey = new PublicKey(s.pubkey); } - return f as GlamStateCache; + return s as GlamStateCache; }; -const toStateCache = (f: StateModel) => { +const toStateCache = (s: StateModel) => { return { - pubkey: f.id, - sparkleKey: f.sparkleKey, - address: f.idStr, - name: f.name, - product: f.productType, + pubkey: s.id, + sparkleKey: s.sparkleKey, + address: s.idStr, + name: s.name, + product: s.productType, } as GlamStateCache; }; @@ -197,14 +197,14 @@ export function GlamProvider({ // Find a list of glam states that the wallet has access to const glamStatesList = [] as GlamStateCache[]; - glamStateModels.forEach((f: StateModel) => { - if (wallet?.publicKey?.equals(f.owner!.pubkey!)) { - const stateCache = toStateCache(f); + glamStateModels.forEach((s: StateModel) => { + if (wallet?.publicKey?.equals(s.owner!.pubkey!)) { + const stateCache = toStateCache(s); glamStatesList.push(stateCache); } else { - f.delegateAcls.forEach((acl: any) => { + (s.delegateAcls || []).forEach((acl) => { if (wallet?.publicKey?.equals(acl.pubkey)) { - glamStatesList.push(toStateCache(f)); + glamStatesList.push(toStateCache(s)); } }); } @@ -238,7 +238,7 @@ export function GlamProvider({ ); const glamState = await glamClient.fetchState(activeGlamState?.pubkey); console.log("delegate acls:", glamState.delegateAcls); - setDelegateAcls(glamState.delegateAcls); + setDelegateAcls(glamState.delegateAcls || []); } }; diff --git a/anchor/target/idl/glam.json b/anchor/target/idl/glam.json index 8b55709b..f20b579c 100644 --- a/anchor/target/idl/glam.json +++ b/anchor/target/idl/glam.json @@ -76,8 +76,30 @@ "writable": true }, { - "name": "metadata", - "writable": true + "name": "openfunds_metadata", + "writable": true, + "optional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "account", + "path": "state" + } + ] + } }, { "name": "signer", @@ -118,7 +140,7 @@ "- Permission::BurnShare", "", "# Integration required", - "- IntegrationName::Mint" + "- Integration::Mint" ], "discriminator": [ 111, @@ -233,7 +255,7 @@ "- Permission::VoteOnProposal", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 20, @@ -258,14 +280,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -348,14 +367,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -407,7 +423,28 @@ }, { "name": "metadata", - "writable": true + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "account", + "path": "state" + } + ] + } }, { "name": "signer", @@ -454,7 +491,28 @@ }, { "name": "metadata", - "writable": true + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "account", + "path": "state" + } + ] + } }, { "name": "vault", @@ -464,14 +522,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -527,14 +582,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -572,7 +624,7 @@ "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 58, @@ -601,14 +653,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -644,7 +693,7 @@ "- Permission::DriftCancelOrders", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 98, @@ -675,14 +724,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -747,7 +793,7 @@ "- Permission::DriftDeleteUser", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 179, @@ -782,14 +828,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -829,7 +872,7 @@ "- Permission::DriftDeposit", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 252, @@ -864,14 +907,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -927,7 +967,7 @@ "- Permission::DriftInitialize", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 21, @@ -962,14 +1002,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1013,7 +1050,7 @@ "- Additional permission Permission::DriftSpotMarket or Permission::DriftPerpMarket is required depending on market type.", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 117, @@ -1044,14 +1081,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1102,7 +1136,7 @@ "- Permission::DriftUpdateUser", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 4, @@ -1129,14 +1163,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1181,7 +1212,7 @@ "- Permission::DriftUpdateUser", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 36, @@ -1208,14 +1239,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1260,7 +1288,7 @@ "- Permission::DriftUpdateUser", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 157, @@ -1287,14 +1315,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1339,7 +1364,7 @@ "- Permission::DriftWithdraw", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 86, @@ -1377,14 +1402,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1441,7 +1463,7 @@ "- Permission::ForceTransferShare", "", "# Integration required", - "- IntegrationName::Mint" + "- Integration::Mint" ], "discriminator": [ 71, @@ -1616,7 +1638,7 @@ "- Permission::StakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 5, @@ -1640,14 +1662,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1706,7 +1725,7 @@ "- Permission::StakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 148, @@ -1730,14 +1749,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1786,7 +1802,7 @@ "- Permission::Stake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 71, @@ -1816,14 +1832,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1911,10 +1924,11 @@ { "kind": "const", "value": [ - 102, - 117, - 110, - 100 + 115, + 116, + 97, + 116, + 101 ] }, { @@ -1929,22 +1943,18 @@ } }, { - "name": "metadata", + "name": "vault", "writable": true, "pda": { "seeds": [ { "kind": "const", "value": [ - 111, - 112, - 101, - 110, - 102, + 118, + 97, 117, - 110, - 100, - 115 + 108, + 116 ] }, { @@ -1955,21 +1965,27 @@ } }, { - "name": "vault", + "name": "signer", + "writable": true, + "signer": true + }, + { + "name": "openfunds_metadata", "writable": true, + "optional": true, "pda": { "seeds": [ { "kind": "const", "value": [ - 116, - 114, + 109, 101, + 116, 97, - 115, - 117, - 114, - 121 + 100, + 97, + 116, + 97 ] }, { @@ -1979,11 +1995,6 @@ ] } }, - { - "name": "signer", - "writable": true, - "signer": true - }, { "name": "system_program", "address": "11111111111111111111111111111111" @@ -2017,7 +2028,7 @@ "- Permission::JupiterSwapLst: input and output assets are both LST.", "", "# Integration required", - "- IntegrationName::JupiterSwap" + "- Integration::JupiterSwap" ], "discriminator": [ 116, @@ -2042,14 +2053,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2248,7 +2256,7 @@ "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::Marinade" + "- Integration::Marinade" ], "discriminator": [ 14, @@ -2278,14 +2286,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2341,7 +2346,7 @@ "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::Marinade" + "- Integration::Marinade" ], "discriminator": [ 117, @@ -2371,14 +2376,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2458,7 +2460,7 @@ "- Permission::Stake", "", "# Integration required", - "- IntegrationName::Marinade" + "- Integration::Marinade" ], "discriminator": [ 64, @@ -2487,14 +2489,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2659,7 +2658,7 @@ "- Permission::Stake", "", "# Integration required", - "- IntegrationName::Marinade" + "- Integration::Marinade" ], "discriminator": [ 69, @@ -2688,14 +2687,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2871,7 +2867,7 @@ "- Permission::LiquidUnstake", "", "# Integration required", - "- IntegrationName::Marinade" + "- Integration::Marinade" ], "discriminator": [ 29, @@ -2900,14 +2896,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2977,7 +2970,7 @@ "- Permission::UnstakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 190, @@ -3001,14 +2994,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3058,7 +3048,7 @@ "- Permission::Stake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 173, @@ -3088,14 +3078,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3146,7 +3133,7 @@ "- Permission::MintShare", "", "# Integration required", - "- IntegrationName::Mint" + "- Integration::Mint" ], "discriminator": [ 145, @@ -3260,7 +3247,7 @@ "- Permission::VoteOnProposal", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 163, @@ -3284,14 +3271,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3339,7 +3323,7 @@ "- Permission::UnstakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 201, @@ -3363,14 +3347,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3463,14 +3444,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3554,7 +3532,7 @@ "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 240, @@ -3584,14 +3562,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3695,7 +3670,7 @@ "- Permission::SetTokenAccountsStates", "", "# Integration required", - "- IntegrationName::Mint" + "- Integration::Mint" ], "discriminator": [ 50, @@ -3752,7 +3727,7 @@ "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 130, @@ -3782,14 +3757,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3848,7 +3820,7 @@ "- Permission::Stake", "", "# Integration required", - "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + "- Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used." ], "discriminator": [ 147, @@ -3877,14 +3849,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -4037,7 +4006,7 @@ "- Permission::Stake", "", "# Integration required", - "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + "- Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used." ], "discriminator": [ 212, @@ -4067,14 +4036,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -4250,7 +4216,7 @@ "- Permission::LiquidUnstake", "", "# Integration required", - "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + "- Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used." ], "discriminator": [ 179, @@ -4279,14 +4245,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -4364,7 +4327,7 @@ "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + "- Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used." ], "discriminator": [ 7, @@ -4394,14 +4357,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -4509,14 +4469,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -4527,7 +4484,7 @@ } }, { - "name": "share_class", + "name": "share_class_mint", "writable": true }, { @@ -4545,7 +4502,7 @@ }, { "kind": "account", - "path": "share_class" + "path": "share_class_mint" } ], "program": { @@ -4680,7 +4637,7 @@ "- Permission::UnstakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 163, @@ -4704,14 +4661,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5017,14 +4971,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5179,7 +5130,7 @@ "- Permission::UnstakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 210, @@ -5203,14 +5154,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5264,7 +5212,7 @@ "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 93, @@ -5294,14 +5242,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5338,7 +5283,7 @@ "- Permission::UnstakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 201, @@ -5362,14 +5307,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5448,14 +5390,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5605,14 +5544,11 @@ { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5756,32 +5692,6 @@ 155 ] }, - { - "name": "FundAccount", - "discriminator": [ - 49, - 104, - 168, - 214, - 134, - 180, - 173, - 154 - ] - }, - { - "name": "FundMetadataAccount", - "discriminator": [ - 214, - 24, - 35, - 92, - 16, - 104, - 166, - 6 - ] - }, { "name": "Governor", "discriminator": [ @@ -5808,6 +5718,19 @@ 169 ] }, + { + "name": "OpenfundsMetadataAccount", + "discriminator": [ + 5, + 89, + 20, + 76, + 255, + 158, + 209, + 219 + ] + }, { "name": "PartialUnstaking", "discriminator": [ @@ -5847,6 +5770,19 @@ 33 ] }, + { + "name": "StateAccount", + "discriminator": [ + 142, + 247, + 54, + 95, + 85, + 133, + 249, + 103 + ] + }, { "name": "Vote", "discriminator": [ @@ -5864,51 +5800,73 @@ "errors": [ { "code": 6000, - "name": "InvalidName", - "msg": "Name too long: max 50 chars" + "name": "InvalidAccountType", + "msg": "Invalid account type" }, { "code": 6001, - "name": "InvalidSymbol", - "msg": "Symbol too long: max 50 chars" + "name": "InvalidName", + "msg": "Name too long: max 64 chars" }, { "code": 6002, - "name": "InvalidUri", - "msg": "Uri too long: max 20" + "name": "InvalidSymbol", + "msg": "Symbol too long: max 32 chars" }, { "code": 6003, + "name": "InvalidUri", + "msg": "Uri too long: max 128 chars" + }, + { + "code": 6004, "name": "InvalidAssetsLen", "msg": "Too many assets: max 100" }, { - "code": 6004, + "code": 6005, "name": "Disabled", "msg": "State account is disabled" }, { - "code": 6005, + "code": 6006, "name": "NoShareClass", "msg": "No share class found" }, { - "code": 6006, + "code": 6007, "name": "ShareClassesNotClosed", "msg": "Glam state account can't be closed. Close share classes first" }, { - "code": 6007, + "code": 6008, "name": "CloseNotEmptyError", "msg": "Error closing state account: not empty" }, { - "code": 6008, + "code": 6009, "name": "WithdrawDenied", "msg": "Withdraw denied. Only vaults allow withdraws (funds and mints don't)" } ], "types": [ + { + "name": "AccountType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Vault" + }, + { + "name": "Mint" + }, + { + "name": "Fund" + } + ] + } + }, { "name": "CompanyField", "type": { @@ -6083,10 +6041,12 @@ } }, { - "name": "owner", - "type": { - "option": "pubkey" - } + "name": "created_by", + "type": "pubkey" + }, + { + "name": "created_at", + "type": "i64" } ] } @@ -6109,6 +6069,10 @@ } } } + }, + { + "name": "expires_at", + "type": "i64" } ] } @@ -6142,30 +6106,12 @@ "type": { "kind": "enum", "variants": [ - { - "name": "TimeCreated" - }, - { - "name": "IsEnabled" - }, - { - "name": "Assets" - }, - { - "name": "AssetsWeights" - }, { "name": "ShareClassAllowlist" }, { "name": "ShareClassBlocklist" }, - { - "name": "DelegateAcls" - }, - { - "name": "IntegrationAcls" - }, { "name": "ExternalVaultAccounts" }, @@ -6318,36 +6264,6 @@ } } ] - }, - { - "name": "VecDelegateAcl", - "fields": [ - { - "name": "val", - "type": { - "vec": { - "defined": { - "name": "DelegateAcl" - } - } - } - } - ] - }, - { - "name": "VecIntegrationAcl", - "fields": [ - { - "name": "val", - "type": { - "vec": { - "defined": { - "name": "IntegrationAcl" - } - } - } - } - ] } ] } @@ -6416,60 +6332,6 @@ ] } }, - { - "name": "FundAccount", - "type": { - "kind": "struct", - "fields": [ - { - "name": "owner", - "type": "pubkey" - }, - { - "name": "vault", - "type": "pubkey" - }, - { - "name": "metadata", - "type": "pubkey" - }, - { - "name": "engine", - "type": "pubkey" - }, - { - "name": "mints", - "type": { - "vec": "pubkey" - } - }, - { - "name": "name", - "type": "string" - }, - { - "name": "uri", - "type": "string" - }, - { - "name": "metadata_uri", - "type": "string" - }, - { - "name": "params", - "type": { - "vec": { - "vec": { - "defined": { - "name": "EngineField" - } - } - } - } - } - ] - } - }, { "name": "FundField", "type": { @@ -6673,62 +6535,6 @@ ] } }, - { - "name": "FundMetadataAccount", - "type": { - "kind": "struct", - "fields": [ - { - "name": "state_pubkey", - "type": "pubkey" - }, - { - "name": "company", - "type": { - "vec": { - "defined": { - "name": "CompanyField" - } - } - } - }, - { - "name": "fund", - "type": { - "vec": { - "defined": { - "name": "FundField" - } - } - } - }, - { - "name": "share_classes", - "type": { - "vec": { - "vec": { - "defined": { - "name": "ShareClassField" - } - } - } - } - }, - { - "name": "fund_managers", - "type": { - "vec": { - "vec": { - "defined": { - "name": "FundManagerField" - } - } - } - } - } - ] - } - }, { "name": "FundOpenfundsModel", "type": { @@ -6914,47 +6720,7 @@ } }, { - "name": "IntegrationAcl", - "type": { - "kind": "struct", - "fields": [ - { - "name": "name", - "type": { - "defined": { - "name": "IntegrationName" - } - } - }, - { - "name": "features", - "type": { - "vec": { - "defined": { - "name": "IntegrationFeature" - } - } - } - } - ] - } - }, - { - "name": "IntegrationFeature", - "type": { - "kind": "enum", - "variants": [ - { - "name": "All" - } - ] - } - }, - { - "name": "IntegrationName", - "docs": [ - "* Integration ACL" - ], + "name": "Integration", "type": { "kind": "enum", "variants": [ @@ -6976,9 +6742,6 @@ { "name": "JupiterSwap" }, - { - "name": "Mint" - }, { "name": "JupiterVote" } @@ -7119,6 +6882,97 @@ ] } }, + { + "name": "Metadata", + "type": { + "kind": "struct", + "fields": [ + { + "name": "template", + "type": { + "defined": { + "name": "MetadataTemplate" + } + } + }, + { + "name": "pubkey", + "type": "pubkey" + }, + { + "name": "uri", + "type": "string" + } + ] + } + }, + { + "name": "MetadataTemplate", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Openfunds" + } + ] + } + }, + { + "name": "OpenfundsMetadataAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "fund_id", + "type": "pubkey" + }, + { + "name": "company", + "type": { + "vec": { + "defined": { + "name": "CompanyField" + } + } + } + }, + { + "name": "fund", + "type": { + "vec": { + "defined": { + "name": "FundField" + } + } + } + }, + { + "name": "share_classes", + "type": { + "vec": { + "vec": { + "defined": { + "name": "ShareClassField" + } + } + } + } + }, + { + "name": "fund_managers", + "type": { + "vec": { + "vec": { + "defined": { + "name": "FundManagerField" + } + } + } + } + } + ] + } + }, { "name": "OrderParams", "type": { @@ -7613,91 +7467,49 @@ "name": "ShareClassCurrency" }, { - "name": "AllInFeeApplied" + "name": "AppliedSubscriptionFeeInFavourOfDistributor" }, { - "name": "AllInFeeDate" + "name": "AppliedSubscriptionFeeInFavourOfDistributorReferenceDate" }, { - "name": "AllInFeeIncludesTransactionCosts" + "name": "CurrencyOfMinimalSubscription" }, { - "name": "AllInFeeMaximum" + "name": "FullShareClassName" }, { - "name": "AppliedSubscriptionFeeInFavourOfDistributor" + "name": "HasPerformanceFee" }, { - "name": "AppliedSubscriptionFeeInFavourOfDistributorReferenceDate" + "name": "HasSubscriptionFeeInFavourOfDistributor" }, { - "name": "Benchmark" + "name": "InvestmentStatus" }, { - "name": "CountryLegalRegistration" + "name": "ManagementFeeApplied" }, { - "name": "CountryMarketingDistribution" + "name": "ManagementFeeAppliedReferenceDate" }, { - "name": "CurrencyHedgeShareClass" + "name": "ManagementFeeMaximum" }, { - "name": "CurrencyOfMinimalSubscription" + "name": "MaximumSubscriptionFeeInFavourOfDistributor" }, { - "name": "DistributionDeclarationFrequency" + "name": "MinimalInitialSubscriptionCategory" }, { - "name": "FullShareClassName" + "name": "MinimalInitialSubscriptionInAmount" }, { - "name": "HasAllInFee" + "name": "MinimalInitialSubscriptionInShares" }, { - "name": "HasOngoingCharges" - }, - { - "name": "HasPerformanceFee" - }, - { - "name": "HasSubscriptionFeeInFavourOfDistributor" - }, - { - "name": "InvestmentStatus" - }, - { - "name": "IsETF" - }, - { - "name": "IsRDRCompliant" - }, - { - "name": "IsTrailerFeeClean" - }, - { - "name": "ManagementFeeApplied" - }, - { - "name": "ManagementFeeAppliedReferenceDate" - }, - { - "name": "ManagementFeeMaximum" - }, - { - "name": "MaximumSubscriptionFeeInFavourOfDistributor" - }, - { - "name": "MinimalInitialSubscriptionCategory" - }, - { - "name": "MinimalInitialSubscriptionInAmount" - }, - { - "name": "MinimalInitialSubscriptionInShares" - }, - { - "name": "MinimalSubsequentSubscriptionCategory" + "name": "MinimalSubsequentSubscriptionCategory" }, { "name": "MinimalSubsequentSubscriptionInAmount" @@ -7708,27 +7520,6 @@ { "name": "MinimumSubscriptionFeeInFavourOfDistributor" }, - { - "name": "OngoingCharges" - }, - { - "name": "OngoingChargesDate" - }, - { - "name": "PerformanceFeeApplied" - }, - { - "name": "PerformanceFeeAppliedReferenceDate" - }, - { - "name": "PerformanceFeeInProspectus" - }, - { - "name": "PerformanceFeeInProspectusReferenceDate" - }, - { - "name": "RecordDateForSRRI" - }, { "name": "ShareClassDistributionPolicy" }, @@ -7744,75 +7535,6 @@ { "name": "SRRI" }, - { - "name": "TERExcludingPerformanceFee" - }, - { - "name": "TERExcludingPerformanceFeeDate" - }, - { - "name": "TERIncludingPerformanceFee" - }, - { - "name": "TERIncludingPerformanceFeeDate" - }, - { - "name": "TransferAgentName" - }, - { - "name": "BICOfTransferAgent" - }, - { - "name": "DomicileOfTransferAgent" - }, - { - "name": "FormOfShare" - }, - { - "name": "HasDurationHedge" - }, - { - "name": "TypeOfEqualization" - }, - { - "name": "IsMultiseries" - }, - { - "name": "SeriesIssuance" - }, - { - "name": "SeriesFrequency" - }, - { - "name": "DoesFundIssueSidePocket" - }, - { - "name": "HasRedemptionGates" - }, - { - "name": "TypeOfAlternativeFundStructureVehicle" - }, - { - "name": "BloombergCode" - }, - { - "name": "FIGICode" - }, - { - "name": "AbbreviatedShareClassName" - }, - { - "name": "ValuationFrequency" - }, - { - "name": "NAVPublicationTime" - }, - { - "name": "IsShareClassEligibleForUCITS" - }, - { - "name": "InvestmentStatusDate" - }, { "name": "LaunchPrice" }, @@ -7822,45 +7544,6 @@ { "name": "LaunchPriceDate" }, - { - "name": "EFAMAMainEFCCategory" - }, - { - "name": "EFAMAEFCClassificationType" - }, - { - "name": "EFAMAActiveEFCClassification" - }, - { - "name": "EFAMAEFCInvestmentTheme" - }, - { - "name": "PricingMethodology" - }, - { - "name": "SinglePricingType" - }, - { - "name": "SwingFactor" - }, - { - "name": "StandardMinimumRemainingAmount" - }, - { - "name": "StandardMinimumRemainingShares" - }, - { - "name": "CurrencyOfMinimumRemainingAmount" - }, - { - "name": "StandardMinimumRemainingCategory" - }, - { - "name": "HurdleRate" - }, - { - "name": "HighWaterMark" - }, { "name": "HasAppliedSubscriptionFeeInFavourOfFund" }, @@ -7885,108 +7568,15 @@ { "name": "MaximumRedemptionFeeInFavourOfFund" }, - { - "name": "EquivalentTrailerFeeCleanISIN" - }, - { - "name": "HasSeparateDistributionFee" - }, - { - "name": "DistributionFee" - }, - { - "name": "DistributionFeeMaximum" - }, - { - "name": "IASector" - }, - { - "name": "AbsorbingFundFullShareClassName" - }, - { - "name": "AbsorbingFundShareClassISIN" - }, - { - "name": "AdministrationFeeMaximum" - }, - { - "name": "AnnualDistributionAtFiscalYearEnd" - }, - { - "name": "AnnualDistributionYieldAtFiscalYearEnd" - }, { "name": "AppliedRedemptionFeeInFavourOfDistributor" }, { "name": "AppliedRedemptionFeeInFavourOfDistributorReferenceDate" }, - { - "name": "BankDetailsSSIForPaymentsProvision" - }, - { - "name": "BankDetailsLevelApplication" - }, - { - "name": "BenchmarkBloombergTicker" - }, - { - "name": "CalculationDateOffsetForRedemption" - }, - { - "name": "CalculationDateOffsetForSubscription" - }, - { - "name": "CalendarOrBusinessDaysForCutOffDateOffsetForRedemption" - }, - { - "name": "CalendarOrBusinessDaysForCutOffDateOffsetForSubscription" - }, - { - "name": "CalendarOrBusinessDaysForPrePaymentDaysForSubscription" - }, - { - "name": "CalendarOrBusinessDaysForSettlementPeriodForRedemption" - }, - { - "name": "CalendarOrBusinessDaysForSettlementPeriodForSubscription" - }, - { - "name": "CalendarOrBusinessDaysForTransactions" - }, - { - "name": "CFICode" - }, - { - "name": "ContingentDeferredSalesChargeExitFee" - }, - { - "name": "ContingentDeferredSalesChargeUpfrontFee" - }, - { - "name": "CountryISOCodeAlpha2" - }, - { - "name": "CountryISOCodeAlpha3" - }, - { - "name": "CountryName" - }, - { - "name": "CurrenciesOfMulticurrencyShareClass" - }, { "name": "CurrencyOfMinimalOrMaximumRedemption" }, - { - "name": "CustodianFeeApplied" - }, - { - "name": "CustodianFeeAppliedReferenceDate" - }, - { - "name": "CustodianFeeMaximum" - }, { "name": "CutOffDateOffsetForRedemption" }, @@ -7999,108 +7589,15 @@ { "name": "CutOffTimeForSubscription" }, - { - "name": "CutOffTimeForSwitchIn" - }, - { - "name": "CutOffTimeForSwitchOut" - }, - { - "name": "DealingDaysOfMultipleRedemptionTradeCycles" - }, - { - "name": "DealingDaysOfMultipleSubscriptionTradeCycles" - }, - { - "name": "DisseminationRecipient" - }, - { - "name": "DistributionFeeReferenceDate" - }, - { - "name": "DoesShareClassApplyMandatoryConversion" - }, - { - "name": "DoesShareClassApplyPartialDealingDays" - }, - { - "name": "DoesShareClassApplyPartialPaymentDays" - }, - { - "name": "DormantEndDate" - }, - { - "name": "DormantStartDate" - }, - { - "name": "ExDividendDateCalendar" - }, - { - "name": "ExitCostDescription" - }, - { - "name": "HasContingentDeferredSalesChargeFee" - }, - { - "name": "HasDilutionLevyAppliedByFund" - }, - { - "name": "HasEqualizationMethodForDistribution" - }, - { - "name": "HasEqualizationMethodForPerformanceFee" - }, - { - "name": "HasForcedRedemption" - }, - { - "name": "HasForwardPricing" - }, - { - "name": "HasHighWaterMark" - }, { "name": "HasLockUpForRedemption" }, - { - "name": "HasPreNoticeForSwitchIn" - }, - { - "name": "HasPreNoticeForSwitchOut" - }, - { - "name": "HasPrePaymentForSubscription" - }, { "name": "HasRedemptionFeeInFavourOfDistributor" }, - { - "name": "HasTripartiteReport" - }, - { - "name": "InvestmentStatusDescription" - }, - { - "name": "IrregularRedemptionDealingDays" - }, - { - "name": "IrregularSubscriptionDealingDays" - }, - { - "name": "IsMulticurrencyShareClass" - }, - { - "name": "IsRestrictedToSeparateFeeArrangement" - }, - { - "name": "IsStructuredFinanceProduct" - }, { "name": "IsValidISIN" }, - { - "name": "LiquidationStartDate" - }, { "name": "LockUpComment" }, @@ -8110,12 +7607,6 @@ { "name": "ManagementFeeMinimum" }, - { - "name": "MandatoryShareConversionDescriptionDetails" - }, - { - "name": "MarketsRelevantToFundTradingCalendar" - }, { "name": "MaximalNumberOfPossibleDecimalsAmount" }, @@ -8140,9 +7631,6 @@ { "name": "MaximumSubsequentRedemptionInShares" }, - { - "name": "MergerRatio" - }, { "name": "MinimalInitialRedemptionInAmount" }, @@ -8167,54 +7655,9 @@ { "name": "MinimumSubscriptionFeeInFavourOfFund" }, - { - "name": "MonthlyRedemptionDealingDays" - }, - { - "name": "MonthlySubscriptionDealingDays" - }, - { - "name": "NasdaqFundNetworkNFNIdentifier" - }, - { - "name": "NoTradingDate" - }, - { - "name": "NumberOfPossibleRedemptionsWithinPeriod" - }, - { - "name": "NumberOfPossibleSubscriptionsWithinPeriod" - }, - { - "name": "PartialDealingDaysDateAndTime" - }, - { - "name": "PartialPaymentDaysDateAndTime" - }, - { - "name": "PaymentDateCalendar" - }, { "name": "PerformanceFeeMinimum" }, - { - "name": "PreNoticeCutOffForRedemption" - }, - { - "name": "PreNoticeCutOffForSubscription" - }, - { - "name": "PrePaymentCutOffTimeForSubscription" - }, - { - "name": "PrePaymentDaysForSubscription" - }, - { - "name": "RecordDateCalendar" - }, - { - "name": "RedemptionTradeCyclePeriod" - }, { "name": "RoundingMethodForPrices" }, @@ -8230,66 +7673,9 @@ { "name": "RoundingMethodForSubscriptionInShares" }, - { - "name": "SettlementPeriodForRedemption" - }, - { - "name": "SettlementPeriodForSubscription" - }, - { - "name": "SettlementPeriodForSwitchIn" - }, - { - "name": "SettlementPeriodForSwitchOut" - }, { "name": "ShareClassDividendType" }, - { - "name": "SingleRegisterAccountRestrictions" - }, - { - "name": "SubscriptionPeriodEndDate" - }, - { - "name": "SubscriptionPeriodStartDate" - }, - { - "name": "SubscriptionTradeCyclePeriod" - }, - { - "name": "SwitchInNoticePeriod" - }, - { - "name": "SwitchOutNoticePeriod" - }, - { - "name": "TerminationDate" - }, - { - "name": "TimeZoneForCutOff" - }, - { - "name": "TimeZoneForCutOffUsingTZDatabase" - }, - { - "name": "ValuationFrequencyDetail" - }, - { - "name": "ValuationReduction" - }, - { - "name": "WeeklyRedemptionDealingDays" - }, - { - "name": "WeeklySubscriptionDealingDays" - }, - { - "name": "YearlyRedemptionDealingDays" - }, - { - "name": "YearlySubscriptionDealingDays" - }, { "name": "CUSIP" }, @@ -8329,7 +7715,7 @@ } }, { - "name": "fund_id", + "name": "state_pubkey", "type": { "option": "pubkey" } @@ -8347,44 +7733,54 @@ } }, { - "name": "is_raw_openfunds", - "type": "bool" + "name": "allowlist", + "type": { + "option": { + "vec": "pubkey" + } + } }, { - "name": "raw_openfunds", + "name": "blocklist", "type": { "option": { - "defined": { - "name": "ShareClassOpenfundsModel" - } + "vec": "pubkey" } } }, { - "name": "allowlist", + "name": "lock_up_period_in_seconds", "type": { - "vec": "pubkey" + "option": "i32" } }, { - "name": "blocklist", + "name": "permanent_delegate", "type": { - "vec": "pubkey" + "option": "pubkey" } }, { - "name": "lock_up_period_in_seconds", - "type": "i32" + "name": "default_account_state_frozen", + "type": { + "option": "bool" + } }, { - "name": "permanent_delegate", + "name": "is_raw_openfunds", "type": { - "option": "pubkey" + "option": "bool" } }, { - "name": "default_account_state_frozen", - "type": "bool" + "name": "raw_openfunds", + "type": { + "option": { + "defined": { + "name": "ShareClassOpenfundsModel" + } + } + } } ] } @@ -8565,6 +7961,108 @@ ] } }, + { + "name": "StateAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "account_type", + "type": { + "defined": { + "name": "AccountType" + } + } + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "vault", + "type": "pubkey" + }, + { + "name": "enabled", + "type": "bool" + }, + { + "name": "created", + "type": { + "defined": { + "name": "CreatedModel" + } + } + }, + { + "name": "engine", + "type": "pubkey" + }, + { + "name": "mints", + "type": { + "vec": "pubkey" + } + }, + { + "name": "metadata", + "type": { + "option": { + "defined": { + "name": "Metadata" + } + } + } + }, + { + "name": "name", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "assets", + "type": { + "vec": "pubkey" + } + }, + { + "name": "delegate_acls", + "type": { + "vec": { + "defined": { + "name": "DelegateAcl" + } + } + } + }, + { + "name": "integrations", + "type": { + "vec": { + "defined": { + "name": "Integration" + } + } + } + }, + { + "name": "params", + "type": { + "vec": { + "vec": { + "defined": { + "name": "EngineField" + } + } + } + } + } + ] + } + }, { "name": "StateModel", "type": { @@ -8577,25 +8075,29 @@ } }, { - "name": "name", + "name": "account_type", "type": { - "option": "string" + "option": { + "defined": { + "name": "AccountType" + } + } } }, { - "name": "uri", + "name": "name", "type": { "option": "string" } }, { - "name": "metadata_uri", + "name": "uri", "type": { "option": "string" } }, { - "name": "is_enabled", + "name": "enabled", "type": { "option": "bool" } @@ -8603,21 +8105,27 @@ { "name": "assets", "type": { - "vec": "pubkey" + "option": { + "vec": "pubkey" + } } }, { "name": "external_vault_accounts", "type": { - "vec": "pubkey" + "option": { + "vec": "pubkey" + } } }, { "name": "mints", "type": { - "vec": { - "defined": { - "name": "ShareClassModel" + "option": { + "vec": { + "defined": { + "name": "ShareClassModel" + } } } } @@ -8655,19 +8163,23 @@ { "name": "delegate_acls", "type": { - "vec": { - "defined": { - "name": "DelegateAcl" + "option": { + "vec": { + "defined": { + "name": "DelegateAcl" + } } } } }, { - "name": "integration_acls", + "name": "integrations", "type": { - "vec": { - "defined": { - "name": "IntegrationAcl" + "option": { + "vec": { + "defined": { + "name": "Integration" + } } } } @@ -8675,24 +8187,36 @@ { "name": "drift_market_indexes_perp", "type": { - "vec": "u32" + "option": { + "vec": "u32" + } } }, { "name": "drift_market_indexes_spot", "type": { - "vec": "u32" + "option": { + "vec": "u32" + } } }, { "name": "drift_order_types", "type": { - "vec": "u32" + "option": { + "vec": "u32" + } } }, { - "name": "is_raw_openfunds", - "type": "bool" + "name": "metadata", + "type": { + "option": { + "defined": { + "name": "Metadata" + } + } + } }, { "name": "raw_openfunds", @@ -8776,22 +8300,22 @@ { "name": "SEED_METADATA", "type": "string", - "value": "\"openfunds\"" + "value": "\"metadata\"" }, { "name": "SEED_MINT", "type": "string", - "value": "\"share\"" + "value": "\"mint\"" }, { "name": "SEED_STATE", "type": "string", - "value": "\"fund\"" + "value": "\"state\"" }, { "name": "SEED_VAULT", "type": "string", - "value": "\"treasury\"" + "value": "\"vault\"" } ] } \ No newline at end of file diff --git a/anchor/target/types/glam.ts b/anchor/target/types/glam.ts index 593e096d..dc66da9d 100644 --- a/anchor/target/types/glam.ts +++ b/anchor/target/types/glam.ts @@ -82,8 +82,30 @@ export type Glam = { "writable": true }, { - "name": "metadata", - "writable": true + "name": "openfundsMetadata", + "writable": true, + "optional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "account", + "path": "state" + } + ] + } }, { "name": "signer", @@ -124,7 +146,7 @@ export type Glam = { "- Permission::BurnShare", "", "# Integration required", - "- IntegrationName::Mint" + "- Integration::Mint" ], "discriminator": [ 111, @@ -239,7 +261,7 @@ export type Glam = { "- Permission::VoteOnProposal", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 20, @@ -264,14 +286,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -354,14 +373,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -413,7 +429,28 @@ export type Glam = { }, { "name": "metadata", - "writable": true + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "account", + "path": "state" + } + ] + } }, { "name": "signer", @@ -460,7 +497,28 @@ export type Glam = { }, { "name": "metadata", - "writable": true + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "account", + "path": "state" + } + ] + } }, { "name": "vault", @@ -470,14 +528,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -533,14 +588,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -578,7 +630,7 @@ export type Glam = { "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 58, @@ -607,14 +659,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -650,7 +699,7 @@ export type Glam = { "- Permission::DriftCancelOrders", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 98, @@ -681,14 +730,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -753,7 +799,7 @@ export type Glam = { "- Permission::DriftDeleteUser", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 179, @@ -788,14 +834,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -835,7 +878,7 @@ export type Glam = { "- Permission::DriftDeposit", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 252, @@ -870,14 +913,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -933,7 +973,7 @@ export type Glam = { "- Permission::DriftInitialize", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 21, @@ -968,14 +1008,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1019,7 +1056,7 @@ export type Glam = { "- Additional permission Permission::DriftSpotMarket or Permission::DriftPerpMarket is required depending on market type.", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 117, @@ -1050,14 +1087,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1108,7 +1142,7 @@ export type Glam = { "- Permission::DriftUpdateUser", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 4, @@ -1135,14 +1169,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1187,7 +1218,7 @@ export type Glam = { "- Permission::DriftUpdateUser", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 36, @@ -1214,14 +1245,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1266,7 +1294,7 @@ export type Glam = { "- Permission::DriftUpdateUser", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 157, @@ -1293,14 +1321,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1345,7 +1370,7 @@ export type Glam = { "- Permission::DriftWithdraw", "", "# Integration required", - "- IntegrationName::Drift" + "- Integration::Drift" ], "discriminator": [ 86, @@ -1383,14 +1408,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1447,7 +1469,7 @@ export type Glam = { "- Permission::ForceTransferShare", "", "# Integration required", - "- IntegrationName::Mint" + "- Integration::Mint" ], "discriminator": [ 71, @@ -1622,7 +1644,7 @@ export type Glam = { "- Permission::StakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 5, @@ -1646,14 +1668,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1712,7 +1731,7 @@ export type Glam = { "- Permission::StakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 148, @@ -1736,14 +1755,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1792,7 +1808,7 @@ export type Glam = { "- Permission::Stake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 71, @@ -1822,14 +1838,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -1917,10 +1930,11 @@ export type Glam = { { "kind": "const", "value": [ - 102, - 117, - 110, - 100 + 115, + 116, + 97, + 116, + 101 ] }, { @@ -1935,22 +1949,18 @@ export type Glam = { } }, { - "name": "metadata", + "name": "vault", "writable": true, "pda": { "seeds": [ { "kind": "const", "value": [ - 111, - 112, - 101, - 110, - 102, + 118, + 97, 117, - 110, - 100, - 115 + 108, + 116 ] }, { @@ -1961,21 +1971,27 @@ export type Glam = { } }, { - "name": "vault", + "name": "signer", + "writable": true, + "signer": true + }, + { + "name": "openfundsMetadata", "writable": true, + "optional": true, "pda": { "seeds": [ { "kind": "const", "value": [ - 116, - 114, + 109, 101, + 116, 97, - 115, - 117, - 114, - 121 + 100, + 97, + 116, + 97 ] }, { @@ -1985,11 +2001,6 @@ export type Glam = { ] } }, - { - "name": "signer", - "writable": true, - "signer": true - }, { "name": "systemProgram", "address": "11111111111111111111111111111111" @@ -2023,7 +2034,7 @@ export type Glam = { "- Permission::JupiterSwapLst: input and output assets are both LST.", "", "# Integration required", - "- IntegrationName::JupiterSwap" + "- Integration::JupiterSwap" ], "discriminator": [ 116, @@ -2048,14 +2059,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2254,7 +2262,7 @@ export type Glam = { "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::Marinade" + "- Integration::Marinade" ], "discriminator": [ 14, @@ -2284,14 +2292,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2347,7 +2352,7 @@ export type Glam = { "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::Marinade" + "- Integration::Marinade" ], "discriminator": [ 117, @@ -2377,14 +2382,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2464,7 +2466,7 @@ export type Glam = { "- Permission::Stake", "", "# Integration required", - "- IntegrationName::Marinade" + "- Integration::Marinade" ], "discriminator": [ 64, @@ -2493,14 +2495,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2665,7 +2664,7 @@ export type Glam = { "- Permission::Stake", "", "# Integration required", - "- IntegrationName::Marinade" + "- Integration::Marinade" ], "discriminator": [ 69, @@ -2694,14 +2693,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2877,7 +2873,7 @@ export type Glam = { "- Permission::LiquidUnstake", "", "# Integration required", - "- IntegrationName::Marinade" + "- Integration::Marinade" ], "discriminator": [ 29, @@ -2906,14 +2902,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -2983,7 +2976,7 @@ export type Glam = { "- Permission::UnstakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 190, @@ -3007,14 +3000,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3064,7 +3054,7 @@ export type Glam = { "- Permission::Stake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 173, @@ -3094,14 +3084,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3152,7 +3139,7 @@ export type Glam = { "- Permission::MintShare", "", "# Integration required", - "- IntegrationName::Mint" + "- Integration::Mint" ], "discriminator": [ 145, @@ -3266,7 +3253,7 @@ export type Glam = { "- Permission::VoteOnProposal", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 163, @@ -3290,14 +3277,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3345,7 +3329,7 @@ export type Glam = { "- Permission::UnstakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 201, @@ -3369,14 +3353,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3469,14 +3450,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3560,7 +3538,7 @@ export type Glam = { "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 240, @@ -3590,14 +3568,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3701,7 +3676,7 @@ export type Glam = { "- Permission::SetTokenAccountsStates", "", "# Integration required", - "- IntegrationName::Mint" + "- Integration::Mint" ], "discriminator": [ 50, @@ -3758,7 +3733,7 @@ export type Glam = { "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 130, @@ -3788,14 +3763,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -3854,7 +3826,7 @@ export type Glam = { "- Permission::Stake", "", "# Integration required", - "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + "- Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used." ], "discriminator": [ 147, @@ -3883,14 +3855,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -4043,7 +4012,7 @@ export type Glam = { "- Permission::Stake", "", "# Integration required", - "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + "- Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used." ], "discriminator": [ 212, @@ -4073,14 +4042,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -4256,7 +4222,7 @@ export type Glam = { "- Permission::LiquidUnstake", "", "# Integration required", - "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + "- Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used." ], "discriminator": [ 179, @@ -4285,14 +4251,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -4370,7 +4333,7 @@ export type Glam = { "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::SplStakePool or IntegrationName::SanctumStakePool, depending on the stake pool program used." + "- Integration::SplStakePool or Integration::SanctumStakePool, depending on the stake pool program used." ], "discriminator": [ 7, @@ -4400,14 +4363,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -4515,14 +4475,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -4533,7 +4490,7 @@ export type Glam = { } }, { - "name": "shareClass", + "name": "shareClassMint", "writable": true }, { @@ -4551,7 +4508,7 @@ export type Glam = { }, { "kind": "account", - "path": "shareClass" + "path": "shareClassMint" } ], "program": { @@ -4686,7 +4643,7 @@ export type Glam = { "- Permission::UnstakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 163, @@ -4710,14 +4667,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5023,14 +4977,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5185,7 +5136,7 @@ export type Glam = { "- Permission::UnstakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 210, @@ -5209,14 +5160,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5270,7 +5218,7 @@ export type Glam = { "- Permission::Unstake", "", "# Integration required", - "- IntegrationName::NativeStaking" + "- Integration::NativeStaking" ], "discriminator": [ 93, @@ -5300,14 +5248,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5344,7 +5289,7 @@ export type Glam = { "- Permission::UnstakeJup", "", "# Integration required", - "- IntegrationName::JupiterVote" + "- Integration::JupiterVote" ], "discriminator": [ 201, @@ -5368,14 +5313,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5454,14 +5396,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5611,14 +5550,11 @@ export type Glam = { { "kind": "const", "value": [ - 116, - 114, - 101, + 118, 97, - 115, 117, - 114, - 121 + 108, + 116 ] }, { @@ -5762,32 +5698,6 @@ export type Glam = { 155 ] }, - { - "name": "fundAccount", - "discriminator": [ - 49, - 104, - 168, - 214, - 134, - 180, - 173, - 154 - ] - }, - { - "name": "fundMetadataAccount", - "discriminator": [ - 214, - 24, - 35, - 92, - 16, - 104, - 166, - 6 - ] - }, { "name": "governor", "discriminator": [ @@ -5814,6 +5724,19 @@ export type Glam = { 169 ] }, + { + "name": "openfundsMetadataAccount", + "discriminator": [ + 5, + 89, + 20, + 76, + 255, + 158, + 209, + 219 + ] + }, { "name": "partialUnstaking", "discriminator": [ @@ -5853,6 +5776,19 @@ export type Glam = { 33 ] }, + { + "name": "stateAccount", + "discriminator": [ + 142, + 247, + 54, + 95, + 85, + 133, + 249, + 103 + ] + }, { "name": "vote", "discriminator": [ @@ -5870,51 +5806,73 @@ export type Glam = { "errors": [ { "code": 6000, - "name": "invalidName", - "msg": "Name too long: max 50 chars" + "name": "invalidAccountType", + "msg": "Invalid account type" }, { "code": 6001, - "name": "invalidSymbol", - "msg": "Symbol too long: max 50 chars" + "name": "invalidName", + "msg": "Name too long: max 64 chars" }, { "code": 6002, - "name": "invalidUri", - "msg": "Uri too long: max 20" + "name": "invalidSymbol", + "msg": "Symbol too long: max 32 chars" }, { "code": 6003, + "name": "invalidUri", + "msg": "Uri too long: max 128 chars" + }, + { + "code": 6004, "name": "invalidAssetsLen", "msg": "Too many assets: max 100" }, { - "code": 6004, + "code": 6005, "name": "disabled", "msg": "State account is disabled" }, { - "code": 6005, + "code": 6006, "name": "noShareClass", "msg": "No share class found" }, { - "code": 6006, + "code": 6007, "name": "shareClassesNotClosed", "msg": "Glam state account can't be closed. Close share classes first" }, { - "code": 6007, + "code": 6008, "name": "closeNotEmptyError", "msg": "Error closing state account: not empty" }, { - "code": 6008, + "code": 6009, "name": "withdrawDenied", "msg": "Withdraw denied. Only vaults allow withdraws (funds and mints don't)" } ], "types": [ + { + "name": "accountType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "vault" + }, + { + "name": "mint" + }, + { + "name": "fund" + } + ] + } + }, { "name": "companyField", "type": { @@ -6089,10 +6047,12 @@ export type Glam = { } }, { - "name": "owner", - "type": { - "option": "pubkey" - } + "name": "createdBy", + "type": "pubkey" + }, + { + "name": "createdAt", + "type": "i64" } ] } @@ -6115,6 +6075,10 @@ export type Glam = { } } } + }, + { + "name": "expiresAt", + "type": "i64" } ] } @@ -6148,30 +6112,12 @@ export type Glam = { "type": { "kind": "enum", "variants": [ - { - "name": "timeCreated" - }, - { - "name": "isEnabled" - }, - { - "name": "assets" - }, - { - "name": "assetsWeights" - }, { "name": "shareClassAllowlist" }, { "name": "shareClassBlocklist" }, - { - "name": "delegateAcls" - }, - { - "name": "integrationAcls" - }, { "name": "externalVaultAccounts" }, @@ -6324,36 +6270,6 @@ export type Glam = { } } ] - }, - { - "name": "vecDelegateAcl", - "fields": [ - { - "name": "val", - "type": { - "vec": { - "defined": { - "name": "delegateAcl" - } - } - } - } - ] - }, - { - "name": "vecIntegrationAcl", - "fields": [ - { - "name": "val", - "type": { - "vec": { - "defined": { - "name": "integrationAcl" - } - } - } - } - ] } ] } @@ -6422,60 +6338,6 @@ export type Glam = { ] } }, - { - "name": "fundAccount", - "type": { - "kind": "struct", - "fields": [ - { - "name": "owner", - "type": "pubkey" - }, - { - "name": "vault", - "type": "pubkey" - }, - { - "name": "metadata", - "type": "pubkey" - }, - { - "name": "engine", - "type": "pubkey" - }, - { - "name": "mints", - "type": { - "vec": "pubkey" - } - }, - { - "name": "name", - "type": "string" - }, - { - "name": "uri", - "type": "string" - }, - { - "name": "metadataUri", - "type": "string" - }, - { - "name": "params", - "type": { - "vec": { - "vec": { - "defined": { - "name": "engineField" - } - } - } - } - } - ] - } - }, { "name": "fundField", "type": { @@ -6679,62 +6541,6 @@ export type Glam = { ] } }, - { - "name": "fundMetadataAccount", - "type": { - "kind": "struct", - "fields": [ - { - "name": "statePubkey", - "type": "pubkey" - }, - { - "name": "company", - "type": { - "vec": { - "defined": { - "name": "companyField" - } - } - } - }, - { - "name": "fund", - "type": { - "vec": { - "defined": { - "name": "fundField" - } - } - } - }, - { - "name": "shareClasses", - "type": { - "vec": { - "vec": { - "defined": { - "name": "shareClassField" - } - } - } - } - }, - { - "name": "fundManagers", - "type": { - "vec": { - "vec": { - "defined": { - "name": "fundManagerField" - } - } - } - } - } - ] - } - }, { "name": "fundOpenfundsModel", "type": { @@ -6920,47 +6726,7 @@ export type Glam = { } }, { - "name": "integrationAcl", - "type": { - "kind": "struct", - "fields": [ - { - "name": "name", - "type": { - "defined": { - "name": "integrationName" - } - } - }, - { - "name": "features", - "type": { - "vec": { - "defined": { - "name": "integrationFeature" - } - } - } - } - ] - } - }, - { - "name": "integrationFeature", - "type": { - "kind": "enum", - "variants": [ - { - "name": "all" - } - ] - } - }, - { - "name": "integrationName", - "docs": [ - "* Integration ACL" - ], + "name": "integration", "type": { "kind": "enum", "variants": [ @@ -6982,9 +6748,6 @@ export type Glam = { { "name": "jupiterSwap" }, - { - "name": "mint" - }, { "name": "jupiterVote" } @@ -7125,6 +6888,97 @@ export type Glam = { ] } }, + { + "name": "metadata", + "type": { + "kind": "struct", + "fields": [ + { + "name": "template", + "type": { + "defined": { + "name": "metadataTemplate" + } + } + }, + { + "name": "pubkey", + "type": "pubkey" + }, + { + "name": "uri", + "type": "string" + } + ] + } + }, + { + "name": "metadataTemplate", + "type": { + "kind": "enum", + "variants": [ + { + "name": "openfunds" + } + ] + } + }, + { + "name": "openfundsMetadataAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "fundId", + "type": "pubkey" + }, + { + "name": "company", + "type": { + "vec": { + "defined": { + "name": "companyField" + } + } + } + }, + { + "name": "fund", + "type": { + "vec": { + "defined": { + "name": "fundField" + } + } + } + }, + { + "name": "shareClasses", + "type": { + "vec": { + "vec": { + "defined": { + "name": "shareClassField" + } + } + } + } + }, + { + "name": "fundManagers", + "type": { + "vec": { + "vec": { + "defined": { + "name": "fundManagerField" + } + } + } + } + } + ] + } + }, { "name": "orderParams", "type": { @@ -7619,91 +7473,49 @@ export type Glam = { "name": "shareClassCurrency" }, { - "name": "allInFeeApplied" + "name": "appliedSubscriptionFeeInFavourOfDistributor" }, { - "name": "allInFeeDate" + "name": "appliedSubscriptionFeeInFavourOfDistributorReferenceDate" }, { - "name": "allInFeeIncludesTransactionCosts" + "name": "currencyOfMinimalSubscription" }, { - "name": "allInFeeMaximum" + "name": "fullShareClassName" }, { - "name": "appliedSubscriptionFeeInFavourOfDistributor" + "name": "hasPerformanceFee" }, { - "name": "appliedSubscriptionFeeInFavourOfDistributorReferenceDate" + "name": "hasSubscriptionFeeInFavourOfDistributor" }, { - "name": "benchmark" + "name": "investmentStatus" }, { - "name": "countryLegalRegistration" + "name": "managementFeeApplied" }, { - "name": "countryMarketingDistribution" + "name": "managementFeeAppliedReferenceDate" }, { - "name": "currencyHedgeShareClass" + "name": "managementFeeMaximum" }, { - "name": "currencyOfMinimalSubscription" + "name": "maximumSubscriptionFeeInFavourOfDistributor" }, { - "name": "distributionDeclarationFrequency" + "name": "minimalInitialSubscriptionCategory" }, { - "name": "fullShareClassName" + "name": "minimalInitialSubscriptionInAmount" }, { - "name": "hasAllInFee" + "name": "minimalInitialSubscriptionInShares" }, { - "name": "hasOngoingCharges" - }, - { - "name": "hasPerformanceFee" - }, - { - "name": "hasSubscriptionFeeInFavourOfDistributor" - }, - { - "name": "investmentStatus" - }, - { - "name": "isEtf" - }, - { - "name": "isRdrCompliant" - }, - { - "name": "isTrailerFeeClean" - }, - { - "name": "managementFeeApplied" - }, - { - "name": "managementFeeAppliedReferenceDate" - }, - { - "name": "managementFeeMaximum" - }, - { - "name": "maximumSubscriptionFeeInFavourOfDistributor" - }, - { - "name": "minimalInitialSubscriptionCategory" - }, - { - "name": "minimalInitialSubscriptionInAmount" - }, - { - "name": "minimalInitialSubscriptionInShares" - }, - { - "name": "minimalSubsequentSubscriptionCategory" + "name": "minimalSubsequentSubscriptionCategory" }, { "name": "minimalSubsequentSubscriptionInAmount" @@ -7714,27 +7526,6 @@ export type Glam = { { "name": "minimumSubscriptionFeeInFavourOfDistributor" }, - { - "name": "ongoingCharges" - }, - { - "name": "ongoingChargesDate" - }, - { - "name": "performanceFeeApplied" - }, - { - "name": "performanceFeeAppliedReferenceDate" - }, - { - "name": "performanceFeeInProspectus" - }, - { - "name": "performanceFeeInProspectusReferenceDate" - }, - { - "name": "recordDateForSrri" - }, { "name": "shareClassDistributionPolicy" }, @@ -7750,75 +7541,6 @@ export type Glam = { { "name": "srri" }, - { - "name": "terExcludingPerformanceFee" - }, - { - "name": "terExcludingPerformanceFeeDate" - }, - { - "name": "terIncludingPerformanceFee" - }, - { - "name": "terIncludingPerformanceFeeDate" - }, - { - "name": "transferAgentName" - }, - { - "name": "bicOfTransferAgent" - }, - { - "name": "domicileOfTransferAgent" - }, - { - "name": "formOfShare" - }, - { - "name": "hasDurationHedge" - }, - { - "name": "typeOfEqualization" - }, - { - "name": "isMultiseries" - }, - { - "name": "seriesIssuance" - }, - { - "name": "seriesFrequency" - }, - { - "name": "doesFundIssueSidePocket" - }, - { - "name": "hasRedemptionGates" - }, - { - "name": "typeOfAlternativeFundStructureVehicle" - }, - { - "name": "bloombergCode" - }, - { - "name": "figiCode" - }, - { - "name": "abbreviatedShareClassName" - }, - { - "name": "valuationFrequency" - }, - { - "name": "navPublicationTime" - }, - { - "name": "isShareClassEligibleForUcits" - }, - { - "name": "investmentStatusDate" - }, { "name": "launchPrice" }, @@ -7828,45 +7550,6 @@ export type Glam = { { "name": "launchPriceDate" }, - { - "name": "efamaMainEfcCategory" - }, - { - "name": "efamaefcClassificationType" - }, - { - "name": "efamaActiveEfcClassification" - }, - { - "name": "efamaefcInvestmentTheme" - }, - { - "name": "pricingMethodology" - }, - { - "name": "singlePricingType" - }, - { - "name": "swingFactor" - }, - { - "name": "standardMinimumRemainingAmount" - }, - { - "name": "standardMinimumRemainingShares" - }, - { - "name": "currencyOfMinimumRemainingAmount" - }, - { - "name": "standardMinimumRemainingCategory" - }, - { - "name": "hurdleRate" - }, - { - "name": "highWaterMark" - }, { "name": "hasAppliedSubscriptionFeeInFavourOfFund" }, @@ -7891,108 +7574,15 @@ export type Glam = { { "name": "maximumRedemptionFeeInFavourOfFund" }, - { - "name": "equivalentTrailerFeeCleanIsin" - }, - { - "name": "hasSeparateDistributionFee" - }, - { - "name": "distributionFee" - }, - { - "name": "distributionFeeMaximum" - }, - { - "name": "iaSector" - }, - { - "name": "absorbingFundFullShareClassName" - }, - { - "name": "absorbingFundShareClassIsin" - }, - { - "name": "administrationFeeMaximum" - }, - { - "name": "annualDistributionAtFiscalYearEnd" - }, - { - "name": "annualDistributionYieldAtFiscalYearEnd" - }, { "name": "appliedRedemptionFeeInFavourOfDistributor" }, { "name": "appliedRedemptionFeeInFavourOfDistributorReferenceDate" }, - { - "name": "bankDetailsSsiForPaymentsProvision" - }, - { - "name": "bankDetailsLevelApplication" - }, - { - "name": "benchmarkBloombergTicker" - }, - { - "name": "calculationDateOffsetForRedemption" - }, - { - "name": "calculationDateOffsetForSubscription" - }, - { - "name": "calendarOrBusinessDaysForCutOffDateOffsetForRedemption" - }, - { - "name": "calendarOrBusinessDaysForCutOffDateOffsetForSubscription" - }, - { - "name": "calendarOrBusinessDaysForPrePaymentDaysForSubscription" - }, - { - "name": "calendarOrBusinessDaysForSettlementPeriodForRedemption" - }, - { - "name": "calendarOrBusinessDaysForSettlementPeriodForSubscription" - }, - { - "name": "calendarOrBusinessDaysForTransactions" - }, - { - "name": "cfiCode" - }, - { - "name": "contingentDeferredSalesChargeExitFee" - }, - { - "name": "contingentDeferredSalesChargeUpfrontFee" - }, - { - "name": "countryIsoCodeAlpha2" - }, - { - "name": "countryIsoCodeAlpha3" - }, - { - "name": "countryName" - }, - { - "name": "currenciesOfMulticurrencyShareClass" - }, { "name": "currencyOfMinimalOrMaximumRedemption" }, - { - "name": "custodianFeeApplied" - }, - { - "name": "custodianFeeAppliedReferenceDate" - }, - { - "name": "custodianFeeMaximum" - }, { "name": "cutOffDateOffsetForRedemption" }, @@ -8005,108 +7595,15 @@ export type Glam = { { "name": "cutOffTimeForSubscription" }, - { - "name": "cutOffTimeForSwitchIn" - }, - { - "name": "cutOffTimeForSwitchOut" - }, - { - "name": "dealingDaysOfMultipleRedemptionTradeCycles" - }, - { - "name": "dealingDaysOfMultipleSubscriptionTradeCycles" - }, - { - "name": "disseminationRecipient" - }, - { - "name": "distributionFeeReferenceDate" - }, - { - "name": "doesShareClassApplyMandatoryConversion" - }, - { - "name": "doesShareClassApplyPartialDealingDays" - }, - { - "name": "doesShareClassApplyPartialPaymentDays" - }, - { - "name": "dormantEndDate" - }, - { - "name": "dormantStartDate" - }, - { - "name": "exDividendDateCalendar" - }, - { - "name": "exitCostDescription" - }, - { - "name": "hasContingentDeferredSalesChargeFee" - }, - { - "name": "hasDilutionLevyAppliedByFund" - }, - { - "name": "hasEqualizationMethodForDistribution" - }, - { - "name": "hasEqualizationMethodForPerformanceFee" - }, - { - "name": "hasForcedRedemption" - }, - { - "name": "hasForwardPricing" - }, - { - "name": "hasHighWaterMark" - }, { "name": "hasLockUpForRedemption" }, - { - "name": "hasPreNoticeForSwitchIn" - }, - { - "name": "hasPreNoticeForSwitchOut" - }, - { - "name": "hasPrePaymentForSubscription" - }, { "name": "hasRedemptionFeeInFavourOfDistributor" }, - { - "name": "hasTripartiteReport" - }, - { - "name": "investmentStatusDescription" - }, - { - "name": "irregularRedemptionDealingDays" - }, - { - "name": "irregularSubscriptionDealingDays" - }, - { - "name": "isMulticurrencyShareClass" - }, - { - "name": "isRestrictedToSeparateFeeArrangement" - }, - { - "name": "isStructuredFinanceProduct" - }, { "name": "isValidIsin" }, - { - "name": "liquidationStartDate" - }, { "name": "lockUpComment" }, @@ -8116,12 +7613,6 @@ export type Glam = { { "name": "managementFeeMinimum" }, - { - "name": "mandatoryShareConversionDescriptionDetails" - }, - { - "name": "marketsRelevantToFundTradingCalendar" - }, { "name": "maximalNumberOfPossibleDecimalsAmount" }, @@ -8146,9 +7637,6 @@ export type Glam = { { "name": "maximumSubsequentRedemptionInShares" }, - { - "name": "mergerRatio" - }, { "name": "minimalInitialRedemptionInAmount" }, @@ -8173,54 +7661,9 @@ export type Glam = { { "name": "minimumSubscriptionFeeInFavourOfFund" }, - { - "name": "monthlyRedemptionDealingDays" - }, - { - "name": "monthlySubscriptionDealingDays" - }, - { - "name": "nasdaqFundNetworkNfnIdentifier" - }, - { - "name": "noTradingDate" - }, - { - "name": "numberOfPossibleRedemptionsWithinPeriod" - }, - { - "name": "numberOfPossibleSubscriptionsWithinPeriod" - }, - { - "name": "partialDealingDaysDateAndTime" - }, - { - "name": "partialPaymentDaysDateAndTime" - }, - { - "name": "paymentDateCalendar" - }, { "name": "performanceFeeMinimum" }, - { - "name": "preNoticeCutOffForRedemption" - }, - { - "name": "preNoticeCutOffForSubscription" - }, - { - "name": "prePaymentCutOffTimeForSubscription" - }, - { - "name": "prePaymentDaysForSubscription" - }, - { - "name": "recordDateCalendar" - }, - { - "name": "redemptionTradeCyclePeriod" - }, { "name": "roundingMethodForPrices" }, @@ -8236,66 +7679,9 @@ export type Glam = { { "name": "roundingMethodForSubscriptionInShares" }, - { - "name": "settlementPeriodForRedemption" - }, - { - "name": "settlementPeriodForSubscription" - }, - { - "name": "settlementPeriodForSwitchIn" - }, - { - "name": "settlementPeriodForSwitchOut" - }, { "name": "shareClassDividendType" }, - { - "name": "singleRegisterAccountRestrictions" - }, - { - "name": "subscriptionPeriodEndDate" - }, - { - "name": "subscriptionPeriodStartDate" - }, - { - "name": "subscriptionTradeCyclePeriod" - }, - { - "name": "switchInNoticePeriod" - }, - { - "name": "switchOutNoticePeriod" - }, - { - "name": "terminationDate" - }, - { - "name": "timeZoneForCutOff" - }, - { - "name": "timeZoneForCutOffUsingTzDatabase" - }, - { - "name": "valuationFrequencyDetail" - }, - { - "name": "valuationReduction" - }, - { - "name": "weeklyRedemptionDealingDays" - }, - { - "name": "weeklySubscriptionDealingDays" - }, - { - "name": "yearlyRedemptionDealingDays" - }, - { - "name": "yearlySubscriptionDealingDays" - }, { "name": "cusip" }, @@ -8335,7 +7721,7 @@ export type Glam = { } }, { - "name": "fundId", + "name": "statePubkey", "type": { "option": "pubkey" } @@ -8353,44 +7739,54 @@ export type Glam = { } }, { - "name": "isRawOpenfunds", - "type": "bool" + "name": "allowlist", + "type": { + "option": { + "vec": "pubkey" + } + } }, { - "name": "rawOpenfunds", + "name": "blocklist", "type": { "option": { - "defined": { - "name": "shareClassOpenfundsModel" - } + "vec": "pubkey" } } }, { - "name": "allowlist", + "name": "lockUpPeriodInSeconds", "type": { - "vec": "pubkey" + "option": "i32" } }, { - "name": "blocklist", + "name": "permanentDelegate", "type": { - "vec": "pubkey" + "option": "pubkey" } }, { - "name": "lockUpPeriodInSeconds", - "type": "i32" + "name": "defaultAccountStateFrozen", + "type": { + "option": "bool" + } }, { - "name": "permanentDelegate", + "name": "isRawOpenfunds", "type": { - "option": "pubkey" + "option": "bool" } }, { - "name": "defaultAccountStateFrozen", - "type": "bool" + "name": "rawOpenfunds", + "type": { + "option": { + "defined": { + "name": "shareClassOpenfundsModel" + } + } + } } ] } @@ -8571,6 +7967,108 @@ export type Glam = { ] } }, + { + "name": "stateAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "accountType", + "type": { + "defined": { + "name": "accountType" + } + } + }, + { + "name": "owner", + "type": "pubkey" + }, + { + "name": "vault", + "type": "pubkey" + }, + { + "name": "enabled", + "type": "bool" + }, + { + "name": "created", + "type": { + "defined": { + "name": "createdModel" + } + } + }, + { + "name": "engine", + "type": "pubkey" + }, + { + "name": "mints", + "type": { + "vec": "pubkey" + } + }, + { + "name": "metadata", + "type": { + "option": { + "defined": { + "name": "metadata" + } + } + } + }, + { + "name": "name", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "assets", + "type": { + "vec": "pubkey" + } + }, + { + "name": "delegateAcls", + "type": { + "vec": { + "defined": { + "name": "delegateAcl" + } + } + } + }, + { + "name": "integrations", + "type": { + "vec": { + "defined": { + "name": "integration" + } + } + } + }, + { + "name": "params", + "type": { + "vec": { + "vec": { + "defined": { + "name": "engineField" + } + } + } + } + } + ] + } + }, { "name": "stateModel", "type": { @@ -8583,25 +8081,29 @@ export type Glam = { } }, { - "name": "name", + "name": "accountType", "type": { - "option": "string" + "option": { + "defined": { + "name": "accountType" + } + } } }, { - "name": "uri", + "name": "name", "type": { "option": "string" } }, { - "name": "metadataUri", + "name": "uri", "type": { "option": "string" } }, { - "name": "isEnabled", + "name": "enabled", "type": { "option": "bool" } @@ -8609,21 +8111,27 @@ export type Glam = { { "name": "assets", "type": { - "vec": "pubkey" + "option": { + "vec": "pubkey" + } } }, { "name": "externalVaultAccounts", "type": { - "vec": "pubkey" + "option": { + "vec": "pubkey" + } } }, { "name": "mints", "type": { - "vec": { - "defined": { - "name": "shareClassModel" + "option": { + "vec": { + "defined": { + "name": "shareClassModel" + } } } } @@ -8661,19 +8169,23 @@ export type Glam = { { "name": "delegateAcls", "type": { - "vec": { - "defined": { - "name": "delegateAcl" + "option": { + "vec": { + "defined": { + "name": "delegateAcl" + } } } } }, { - "name": "integrationAcls", + "name": "integrations", "type": { - "vec": { - "defined": { - "name": "integrationAcl" + "option": { + "vec": { + "defined": { + "name": "integration" + } } } } @@ -8681,24 +8193,36 @@ export type Glam = { { "name": "driftMarketIndexesPerp", "type": { - "vec": "u32" + "option": { + "vec": "u32" + } } }, { "name": "driftMarketIndexesSpot", "type": { - "vec": "u32" + "option": { + "vec": "u32" + } } }, { "name": "driftOrderTypes", "type": { - "vec": "u32" + "option": { + "vec": "u32" + } } }, { - "name": "isRawOpenfunds", - "type": "bool" + "name": "metadata", + "type": { + "option": { + "defined": { + "name": "metadata" + } + } + } }, { "name": "rawOpenfunds", @@ -8782,22 +8306,22 @@ export type Glam = { { "name": "seedMetadata", "type": "string", - "value": "\"openfunds\"" + "value": "\"metadata\"" }, { "name": "seedMint", "type": "string", - "value": "\"share\"" + "value": "\"mint\"" }, { "name": "seedState", "type": "string", - "value": "\"fund\"" + "value": "\"state\"" }, { "name": "seedVault", "type": "string", - "value": "\"treasury\"" + "value": "\"vault\"" } ] }; diff --git a/anchor/tests/glam_crud.spec.ts b/anchor/tests/glam_crud.spec.ts index 150f3457..8c9673ed 100644 --- a/anchor/tests/glam_crud.spec.ts +++ b/anchor/tests/glam_crud.spec.ts @@ -24,43 +24,44 @@ const key2 = Keypair.fromSeed(str2seed("acl_test_key2")); describe("glam_crud", () => { const glamClient = new GlamClient(); const glamClientCustomWallet = new GlamClient({ wallet: new Wallet(key1) }); - let statePda: PublicKey; + let statePda: PublicKey, vaultPda: PublicKey; it("Initialize glam state", async () => { - const shareClassAllowlist = [glamClient.getSigner()]; - const shareClassBlocklist = []; + const shareClassAllowlist = [key1.publicKey]; + const shareClassBlocklist = [key2.publicKey]; const stateForTest = { ...stateModelForTest }; stateForTest.mints![0].allowlist = shareClassAllowlist; + stateForTest.mints![0].blocklist = shareClassBlocklist; - const stateData = await createGlamStateForTest(glamClient, stateForTest); - statePda = stateData.statePda; + const res = await createGlamStateForTest(glamClient, stateForTest); + statePda = res.statePda; + vaultPda = res.vaultPda; const stateModel = await glamClient.fetchState(statePda); - expect(stateModel.mints.length).toEqual(1); - expect(stateModel.mints[0].allowlist).toEqual(shareClassAllowlist); - expect(stateModel.mints[0].blocklist).toEqual(shareClassBlocklist); + expect(stateModel.mints?.length).toEqual(1); + expect(stateModel.mints![0].allowlist).toEqual(shareClassAllowlist); + expect(stateModel.mints![0].blocklist).toEqual(shareClassBlocklist); }); it("Update name in state", async () => { - const updatedState = { - name: "Updated name in state", - } as Partial; + const name = "Updated name in state"; try { - const txSig = await glamClient.state.updateState(statePda, updatedState); + const txSig = await glamClient.state.updateState(statePda, { name }); console.log("Update name txSig", txSig); } catch (e) { console.error(e); throw e; } const state = await glamClient.fetchStateAccount(statePda); - expect(state.name).toEqual(updatedState.name); + expect(state.name).toEqual(name); }); it("Update share class allowlist", async () => { const shareClassModel = new ShareClassModel({ allowlist: [key1.publicKey, key2.publicKey], + blocklist: [], }); try { const txSig = await glamClient.program.methods @@ -76,14 +77,16 @@ describe("glam_crud", () => { throw e; } const stateModel = await glamClient.fetchState(statePda); - expect(stateModel.mints[0].allowlist).toEqual(shareClassModel.allowlist); + expect(stateModel.mints![0].allowlist).toEqual(shareClassModel.allowlist); + expect(stateModel.mints![0].blocklist).toEqual(shareClassModel.blocklist); }); it("Update assets allowlist", async () => { // The test glam state has 2 assets, WSOL and MSOL. Update to USDC. - let updatedState = { assets: [USDC] }; try { - const txSig = await glamClient.state.updateState(statePda, updatedState); + const txSig = await glamClient.state.updateState(statePda, { + assets: [USDC], + }); console.log("Update assets (USDC) txSig", txSig); } catch (e) { console.error(e); @@ -93,9 +96,10 @@ describe("glam_crud", () => { expect(stateModel.assets).toEqual([USDC]); // Update assets back to WSOL and MSOL - updatedState = { assets: [WSOL, MSOL] }; try { - const txSig = await glamClient.state.updateState(statePda, updatedState); + const txSig = await glamClient.state.updateState(statePda, { + assets: [WSOL, MSOL], + }); console.log("Update assets (WSOL and MSOL) txSig", txSig); } catch (e) { console.error(e); @@ -108,53 +112,58 @@ describe("glam_crud", () => { it("[integration-acl] add and update", async () => { // 0 by default let stateModel = await glamClient.fetchState(statePda); - expect(stateModel.integrationAcls.length).toEqual(0); + expect(stateModel.integrations?.length).toEqual(0); - // 1 acl - let updatedState = { - integrationAcls: [{ name: { drift: {} }, features: [] }], - } as Partial; + // 1 integration try { - const txSig = await glamClient.state.updateState(statePda, updatedState); - console.log("Update integration acl txSig", txSig); + const txSig = await glamClient.state.updateState(statePda, { + integrations: [{ drift: {} }], + }); + console.log("Update integrations (drift) txSig", txSig); } catch (e) { console.error(e); throw e; } stateModel = await glamClient.fetchState(statePda); - expect(stateModel.integrationAcls.length).toEqual(1); - expect(stateModel.integrationAcls).toEqual(updatedState.integrationAcls); + expect(stateModel.integrations?.length).toEqual(1); + expect(stateModel.integrations).toEqual([{ drift: {} }]); // 5 acls - updatedState = { - integrationAcls: [ - { name: { drift: {} }, features: [] }, - { name: { jupiterSwap: {} }, features: [] }, - { name: { marinade: {} }, features: [] }, - { name: { splStakePool: {} }, features: [] }, - { name: { sanctumStakePool: {} }, features: [] }, + const updated = { + integrations: [ + { drift: {} }, + { jupiterSwap: {} }, + { marinade: {} }, + { splStakePool: {} }, + { sanctumStakePool: {} }, ], }; try { - const txSig = await glamClient.state.updateState(statePda, updatedState); - console.log("Update integration acl txSig", txSig); + const txSig = await glamClient.state.updateState(statePda, updated); + console.log( + "Update integrations (drift, jupiterSwap, marinade, splStakePool, sanctumStakePool) txSig", + txSig, + ); } catch (e) { console.error(e); throw e; } stateModel = await glamClient.fetchState(statePda); - expect(stateModel.integrationAcls.length).toEqual(5); - expect(stateModel.integrationAcls).toEqual(updatedState.integrationAcls); + expect(stateModel.integrations).toEqual(updated.integrations); }); it("[delegate-acl] upsert", async () => { - // empty acls + // empty delegate acls let stateModel = await glamClient.fetchState(statePda); - expect(stateModel.delegateAcls.length).toEqual(0); + expect(stateModel.delegateAcls?.length).toEqual(0); - // grant key1 wSolWrap permission + // grant key1 wSolWrap permission, no expiration const delegateAcls = [ - { pubkey: key1.publicKey, permissions: [{ wSolWrap: {} }] }, + { + pubkey: key1.publicKey, + permissions: [{ wSolWrap: {} }], + expiresAt: new BN(0), + }, ]; try { const txSig = await glamClient.state.upsertDelegateAcls( @@ -168,15 +177,18 @@ describe("glam_crud", () => { } stateModel = await glamClient.fetchState(statePda); expect(stateModel.delegateAcls?.length).toEqual(1); - expect(stateModel.delegateAcls[0].pubkey).toEqual(key1.publicKey); - expect(stateModel.delegateAcls[0].permissions).toEqual([{ wSolWrap: {} }]); + expect(stateModel.delegateAcls![0].pubkey).toEqual(key1.publicKey); + expect(stateModel.delegateAcls![0].permissions).toEqual([{ wSolWrap: {} }]); + expect(stateModel.delegateAcls![0].expiresAt).toEqual(new BN(0)); // grant key1 wSolWrap and wSolUnwrap permission + const expiresAt = new BN(Date.now() / 1000 + 60); try { const txSig = await glamClient.state.upsertDelegateAcls(statePda, [ { pubkey: key1.publicKey, permissions: [{ wSolWrap: {} }, { wSolUnwrap: {} }], + expiresAt, }, ]); console.log("Update delegate acl txSig", txSig); @@ -186,17 +198,22 @@ describe("glam_crud", () => { } stateModel = await glamClient.fetchState(statePda); expect(stateModel.delegateAcls?.length).toEqual(1); - expect(stateModel.delegateAcls[0].pubkey).toEqual(key1.publicKey); - expect(stateModel.delegateAcls[0].permissions).toEqual([ + expect(stateModel.delegateAcls![0].pubkey).toEqual(key1.publicKey); + expect(stateModel.delegateAcls![0].permissions).toEqual([ { wSolWrap: {} }, { wSolUnwrap: {} }, ]); + expect(stateModel.delegateAcls![0].expiresAt).toEqual(expiresAt); }); it("[delegate-acl] delete", async () => { // add key2 permissions const delegateAcls = [ - { pubkey: key2.publicKey, permissions: [{ stake: {} }] }, + { + pubkey: key2.publicKey, + permissions: [{ stake: {} }], + expiresAt: new BN(0), + }, ]; try { const txSig = await glamClient.state.upsertDelegateAcls( @@ -210,8 +227,6 @@ describe("glam_crud", () => { } let stateModel = await glamClient.fetchState(statePda); expect(stateModel.delegateAcls?.length).toEqual(2); - expect(stateModel.delegateAcls[1].pubkey).toEqual(key2.publicKey); - expect(stateModel.delegateAcls[1].permissions).toEqual([{ stake: {} }]); // remove key1 and key2 permissions try { @@ -229,8 +244,8 @@ describe("glam_crud", () => { }); it("[delegate-acl] test authorization", async () => { - // transfer 1 SOL to treasury - // transfer 0.1 SOL to key1 as it needs to pay for treasury wsol ata creation + // transfer 1 SOL to vault + // transfer 0.1 SOL to key1 as it needs to pay for vault wsol ata creation const tranferTx = new Transaction().add( SystemProgram.transfer({ fromPubkey: glamClient.getSigner(), @@ -246,13 +261,17 @@ describe("glam_crud", () => { await glamClient.sendAndConfirm(tranferTx); // grant key1 wSolWrap permission - let updatedState = { + let updated = { delegateAcls: [ - { pubkey: key1.publicKey, permissions: [{ wSolWrap: {} }] }, + { + pubkey: key1.publicKey, + permissions: [{ wSolWrap: {} }], + expiresAt: new BN(0), + }, ], }; try { - const txSig = await glamClient.state.updateState(statePda, updatedState); + const txSig = await glamClient.state.updateState(statePda, updated); console.log("Update delegate acl txSig", txSig); } catch (e) { console.error(e); @@ -260,19 +279,10 @@ describe("glam_crud", () => { } let StateModel = await glamClient.fetchState(statePda); expect(StateModel.delegateAcls?.length).toEqual(1); - expect(StateModel.delegateAcls[0].pubkey).toEqual(key1.publicKey); + expect(StateModel.delegateAcls![0].pubkey).toEqual(key1.publicKey); // key1 now has wSolWrap permission, use key1 to wrap some SOL - try { - const tx = await glamClientCustomWallet.wsol.wrap( - statePda, - new BN(30_000_000), - ); - console.log("Wrap:", tx); - } catch (e) { - console.log("Error", e); - throw e; - } + await glamClientCustomWallet.wsol.wrap(statePda, new BN(30_000_000)); // key1 doesn't have wSolUnwrap permission, unwrap should fail try { @@ -284,95 +294,85 @@ describe("glam_crud", () => { } }, 15_000); - it("Update state unauthorized", async () => { - const updatedState = new StateModel({ name: "Updated state name" }); + it("[drift-market-allowlists] upsert", async () => { + let stateModel = await glamClient.fetchState(statePda); + expect(stateModel.driftMarketIndexesPerp).toBeNull(); + try { - const txId = await glamClient.program.methods - .updateState(updatedState) - .accounts({ - state: statePda, - signer: key1.publicKey, - }) - .signers([key1]) - .rpc(); - expect(txId).toBeUndefined(); + const txSig = await glamClient.state.updateState(statePda, { + driftMarketIndexesPerp: [0, 1], + driftMarketIndexesSpot: [2, 3, 4], + driftOrderTypes: [5, 6], + }); + console.log("Update driftMarketIndexesPerp txSig", txSig); } catch (e) { - expect(e.error.errorMessage).toEqual("Signer is not authorized"); + console.error(e); + throw e; } - }); + stateModel = await glamClient.fetchState(statePda); + expect(stateModel.driftMarketIndexesPerp).toEqual([0, 1]); + expect(stateModel.driftMarketIndexesSpot).toEqual([2, 3, 4]); + expect(stateModel.driftOrderTypes).toEqual([5, 6]); + }, 15_000); - it("Update owner", async () => { - const defaultOwner = glamClient.getSigner(); - const newOwner = Keypair.fromSeed(str2seed("new-owner")); + it("[ownership] Update state unauthorized", async () => { + try { + const txSig = await glamClientCustomWallet.state.updateState(statePda, { + name: "Updated state name", + }); + expect(txSig).toBeUndefined(); + } catch (e) { + expect((e as GlamError).message).toEqual("Signer is not authorized."); + } + }, 15_000); - const updatedState = new StateModel({ + it("[ownership] Update owner", async () => { + const updated = { owner: { portfolioManagerName: "New Owner", - pubkey: newOwner.publicKey, + pubkey: key1.publicKey, kind: { wallet: {} }, }, - }); + }; try { - const txId = await glamClient.program.methods - .updateState(updatedState) - .accounts({ - state: statePda, - signer: defaultOwner, - }) - .rpc(); - - console.log("Owner updated from default to new", txId); + const txSig = await glamClient.state.updateState(statePda, updated); + console.log("Owner updated from default to new", txSig); } catch (e) { console.error(e); throw e; } let glamState = await glamClient.fetchStateAccount(statePda); - expect(glamState.owner.toString()).toEqual(newOwner.publicKey.toString()); - - const updatedState2 = new StateModel({ - owner: { - portfolioManagerName: "Default Manager", - pubkey: defaultOwner, - kind: { wallet: {} }, - }, - }); + expect(glamState.owner).toEqual(key1.publicKey); - // default manager can NOT update back + // previous owner CAN NOT update try { - const txId = await glamClient.program.methods - .updateState(updatedState2) - .accounts({ - state: statePda, - signer: defaultOwner, - }) - .rpc(); - expect(txId).toBeUndefined(); + const txSig = await glamClient.state.updateState(statePda, { + name: "Updated state name", + }); + expect(txSig).toBeUndefined(); } catch (e) { - expect(e.message).toContain("not authorized"); + expect((e as GlamError).message).toEqual("Signer is not authorized."); } // new manager CAN update back try { - const txId = await glamClient.program.methods - .updateState(updatedState2) - .accounts({ - state: statePda, - signer: newOwner.publicKey, - }) - .signers([newOwner]) - .rpc(); + const txId = await glamClientCustomWallet.state.updateState(statePda, { + owner: { + portfolioManagerName: "Default Owner", + pubkey: glamClient.getSigner(), + kind: { wallet: {} }, + }, + }); console.log("Owner updated from new to default", txId); } catch (e) { console.error(e); throw e; } glamState = await glamClient.fetchStateAccount(statePda); - expect(glamState.owner).toEqual(defaultOwner); + expect(glamState.owner).toEqual(glamClient.getSigner()); }); it("Close token accounts", async () => { - const treasury = glamClient.getVaultPda(statePda); - // Create empty token accounts const transaction = new Transaction(); for (const mint of [WSOL, MSOL]) { @@ -380,15 +380,15 @@ describe("glam_crud", () => { createAssociatedTokenAccountIdempotentInstruction( glamClient.getSigner(), glamClient.getVaultAta(statePda, mint), - treasury, + vaultPda, mint, ), ); } const txSig = await glamClient.sendAndConfirm(transaction); - console.log("Creating ata for treasury:", txSig); + console.log("Created wSOL and mSOL ATAs for vault:", txSig); - let tokenAccounts = await glamClient.getTokenAccountsByOwner(treasury); + let tokenAccounts = await glamClient.getTokenAccountsByOwner(vaultPda); expect(tokenAccounts.length).toEqual(2); // Close token accounts @@ -402,7 +402,7 @@ describe("glam_crud", () => { console.error(e); throw e; } - tokenAccounts = await glamClient.getTokenAccountsByOwner(treasury); + tokenAccounts = await glamClient.getTokenAccountsByOwner(vaultPda); expect(tokenAccounts.length).toEqual(0); }); @@ -411,8 +411,8 @@ describe("glam_crud", () => { expect(glamState).not.toBeNull(); try { - const txId = await glamClient.state.closeState(statePda); - expect(txId).toBeUndefined(); + const txSig = await glamClient.state.closeState(statePda); + expect(txSig).toBeUndefined(); } catch (e) { expect(e.message).toContain( "Glam state account can't be closed. Close share classes first", @@ -420,16 +420,16 @@ describe("glam_crud", () => { } try { - const txId = await glamClient.shareClass.closeShareClass(statePda); - console.log("Close share class txId:", txId); + const txSig = await glamClient.shareClass.closeShareClass(statePda); + console.log("Close share class txId:", txSig); } catch (e) { console.error(e); throw e; } try { - const txId = await glamClient.state.closeState(statePda); - console.log("Close state account txId:", txId); + const txSig = await glamClient.state.closeState(statePda); + console.log("Close state account txId:", txSig); } catch (e) { console.error(e); throw e; diff --git a/anchor/tests/glam_drift.spec.ts b/anchor/tests/glam_drift.spec.ts index d23c53df..f2af4252 100644 --- a/anchor/tests/glam_drift.spec.ts +++ b/anchor/tests/glam_drift.spec.ts @@ -115,7 +115,7 @@ describe("glam_drift", () => { // Enable drift integration const updated = { - integrationAcls: [{ name: { drift: {} }, features: [] }], + integrations: [{ drift: {} }], }; try { const txSig = await glamClient.state.updateState(statePda, updated); diff --git a/anchor/tests/glam_investor.spec.ts b/anchor/tests/glam_investor.spec.ts index de474e7c..35cc4ff9 100644 --- a/anchor/tests/glam_investor.spec.ts +++ b/anchor/tests/glam_investor.spec.ts @@ -21,7 +21,12 @@ import { createAssociatedTokenAccountIdempotentInstruction, } from "@solana/spl-token"; -import { stateModelForTest, createGlamStateForTest, str2seed } from "./setup"; +import { + stateModelForTest, + createGlamStateForTest, + str2seed, + airdrop, +} from "./setup"; import { GlamClient, WSOL } from "../src"; describe("glam_investor", () => { @@ -57,7 +62,7 @@ describe("glam_investor", () => { ...stateModelForTest, name: "Glam Investment", assets: [usdc.publicKey, btc.publicKey, ethOrWsol], - integrationAcls: [{ name: { marinade: {} }, features: [] }], + integrations: [{ marinade: {} }], // overwrite share class acls: alice and manager are allowed to subscribe, // bob and eve will be blocked. mints: [ @@ -75,7 +80,6 @@ describe("glam_investor", () => { const connection = glamClient.provider.connection; const commitment = "confirmed"; - const program = glamClient.program; const vaultUsdcAta = getAssociatedTokenAddressSync( usdc.publicKey, @@ -152,11 +156,7 @@ describe("glam_investor", () => { // create ATAs for each user for (const user of userKeypairs) { // send 1 SOL to each user - const airdrop = await connection.requestAirdrop( - user.publicKey, - 1_000_000_000, - ); - await connection.confirmTransaction(airdrop); + await airdrop(connection, user.publicKey, 1_000_000_000); const userATA = await createAssociatedTokenAccount( connection, @@ -209,13 +209,11 @@ describe("glam_investor", () => { it("Fund created", async () => { try { - // - // create fund - // const stateData = await createGlamStateForTest(glamClient, stateModel); const stateAccount = await glamClient.fetchStateAccount( stateData.statePda, ); + expect(statePda).toEqual(stateData.statePda); expect(stateAccount.mints[0]).toEqual(sharePda); } catch (e) { console.error(e); @@ -282,12 +280,12 @@ describe("glam_investor", () => { TOKEN_2022_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID, ); - const txId = await program.methods + const txId = await glamClient.program.methods .subscribe(0, new BN(1 * 10 ** 8), true) .accountsPartial({ state: statePda, vault: vaultPda, - shareClass: invalidShareClass, + shareClassMint: invalidShareClass, signerShareAta: shareAta, asset: btc.publicKey, vaultAta: vaultEthAta, @@ -307,7 +305,6 @@ describe("glam_investor", () => { ]) .rpc({ commitment }); } catch (e) { - // console.error(e); expect(e.message).toContain("A seeds constraint was violated"); expect(e.message).toContain("Error Code: ConstraintSeeds"); } diff --git a/anchor/tests/glam_jupiter.spec.ts b/anchor/tests/glam_jupiter.spec.ts index 39d87324..44fcc70b 100644 --- a/anchor/tests/glam_jupiter.spec.ts +++ b/anchor/tests/glam_jupiter.spec.ts @@ -25,15 +25,12 @@ describe("glam_jupiter", () => { it("Initialize fund", async () => { const stateData = await createGlamStateForTest(glamClient, { ...stateModelForTest, - integrationAcls: [ - { name: { jupiterSwap: {} }, features: [] }, - { name: { jupiterVote: {} }, features: [] }, - ], + integrations: [{ jupiterSwap: {} }, { jupiterVote: {} }], }); statePda = stateData.statePda; const state = await glamClient.fetchState(statePda); - expect(state.mints.length).toEqual(1); + expect(state.mints?.length).toEqual(1); await airdrop( glamClient.provider.connection, diff --git a/anchor/tests/glam_marinade.spec.ts b/anchor/tests/glam_marinade.spec.ts index 12e9f3ea..51e41692 100644 --- a/anchor/tests/glam_marinade.spec.ts +++ b/anchor/tests/glam_marinade.spec.ts @@ -13,10 +13,7 @@ describe("glam_marinade", () => { statePda = stateData.statePda; const txSig = await glamClient.state.updateState(statePda, { - integrationAcls: [ - { name: { marinade: {} }, features: [] }, - { name: { nativeStaking: {} }, features: [] }, - ], + integrations: [{ marinade: {} }, { nativeStaking: {} }], }); console.log("Marinade integration enabled:", txSig); diff --git a/anchor/tests/glam_share_class.spec.ts b/anchor/tests/glam_mint.spec.ts similarity index 78% rename from anchor/tests/glam_share_class.spec.ts rename to anchor/tests/glam_mint.spec.ts index 0f66e1f4..40ad1834 100644 --- a/anchor/tests/glam_share_class.spec.ts +++ b/anchor/tests/glam_mint.spec.ts @@ -12,18 +12,19 @@ import { const key1 = Keypair.fromSeed(str2seed("acl_test_key1")); const key2 = Keypair.fromSeed(str2seed("acl_test_key2")); -describe("glam_share_class", () => { +describe("glam_mint", () => { const glamClient = new GlamClient(); let statePda: PublicKey; it("Initialize mint with default account state frozen", async () => { const stateForTest = { ...stateModelForTest, - integrationAcls: [{ name: { mint: {} }, features: [] }], // must have mint integration + accountType: { mint: {} }, mints: [ { ...stateModelForTest.mints![0], - allowlist: [glamClient.getSigner()], + allowlist: [key1.publicKey], + blocklist: [key2.publicKey], defaultAccountStateFrozen: true, permanentDelegate: new PublicKey(0), // set permanent delegate to share class itself }, @@ -34,37 +35,37 @@ describe("glam_share_class", () => { statePda = stateData.statePda; const stateModel = await glamClient.fetchState(statePda); - expect(stateModel.mints.length).toEqual(1); - expect(stateModel.mints[0].allowlist).toEqual([glamClient.getSigner()]); - expect(stateModel.mints[0].blocklist).toEqual([]); + expect(stateModel.mints?.length).toEqual(1); + expect(stateModel.mints![0].allowlist).toEqual([key1.publicKey]); + expect(stateModel.mints![0].blocklist).toEqual([key2.publicKey]); }); it("Mint share class fail due to default state frozen", async () => { try { - const txId = await glamClient.shareClass.mintShare( + const txSig = await glamClient.shareClass.mintShare( statePda, 0, key1.publicKey, new BN(1_000_000_000), ); - expect(txId).toBeUndefined(); + expect(txSig).toBeUndefined(); } catch (e) { expect(e.logs).toContain("Program log: Error: Account is frozen"); } }); - it("Mint again and force thawing token account", async () => { + it("Unfreeze token account and mint", async () => { const amount = new BN(1_000_000_000); const recipient = key1.publicKey; try { - const txId = await glamClient.shareClass.mintShare( + const txSig = await glamClient.shareClass.mintShare( statePda, 0, recipient, amount, true, ); - console.log("mintShare txId", txId); + console.log("mintShare txSig", txSig); } catch (e) { console.error(e); throw e; @@ -85,26 +86,26 @@ describe("glam_share_class", () => { const shareClassMint = glamClient.getShareClassPda(statePda, 0); const ata = glamClient.getShareClassAta(key1.publicKey, shareClassMint); - // Token account is not frozen before the tx + // Before: token account is not frozen let accountInfo = await glamClient.provider.connection.getAccountInfo(ata); let tokenAccount = unpackAccount(ata, accountInfo, TOKEN_2022_PROGRAM_ID); expect(tokenAccount.isFrozen).toEqual(false); // Freeeze token account try { - const txId = await glamClient.shareClass.setTokenAccountsStates( + const txSig = await glamClient.shareClass.setTokenAccountsStates( statePda, 0, [ata], true, ); - console.log("setTokenAccountsStates txId", txId); + console.log("setTokenAccountsStates txSig", txSig); } catch (e) { console.error(e); throw e; } - // Token account is frozen before the tx + // After: token account is frozen accountInfo = await glamClient.provider.connection.getAccountInfo(ata); tokenAccount = unpackAccount(ata, accountInfo, TOKEN_2022_PROGRAM_ID); expect(tokenAccount.isFrozen).toEqual(true); @@ -119,7 +120,7 @@ describe("glam_share_class", () => { const amount = new BN(500_000_000); try { - const txId = await glamClient.shareClass.forceTransferShare( + const txSig = await glamClient.shareClass.forceTransferShare( statePda, 0, amount, @@ -127,7 +128,7 @@ describe("glam_share_class", () => { to, true, ); - console.log("forceTransferShare txId", txId); + console.log("forceTransferShare txSig", txSig); } catch (e) { console.error(e); throw e; @@ -154,13 +155,13 @@ describe("glam_share_class", () => { const from = key1.publicKey; const amount = new BN(500_000_000); - const txId = await glamClient.shareClass.burnShare( + const txSig = await glamClient.shareClass.burnShare( statePda, 0, amount, from, ); - console.log("burnShare txId", txId); + console.log("burnShare txSig", txSig); const shareClassMint = glamClient.getShareClassPda(statePda, 0); const fromAta = glamClient.getShareClassAta(from, shareClassMint); @@ -175,23 +176,23 @@ describe("glam_share_class", () => { it("Subscribe and redeem disabled", async () => { try { - const txId = await glamClient.state.setSubscribeRedeemEnabled( + const txSig = await glamClient.state.setSubscribeRedeemEnabled( statePda, false, ); - console.log("setSubscribeRedeemEnabled txId", txId); + console.log("setSubscribeRedeemEnabled txSig", txSig); } catch (e) { console.error(e); throw e; } try { - const txId = await glamClient.investor.subscribe( + const txSig = await glamClient.investor.subscribe( statePda, WSOL, new BN(10 ** 8), ); - console.log("subscribe:", txId); + console.log("subscribe:", txSig); } catch (e) { expect((e as GlamError).message).toEqual( "Fund is disabled for subscription and redemption.", diff --git a/anchor/tests/glam_openfunds.spec.ts b/anchor/tests/glam_openfunds.spec.ts index 2fbb420c..11ef4d48 100644 --- a/anchor/tests/glam_openfunds.spec.ts +++ b/anchor/tests/glam_openfunds.spec.ts @@ -37,8 +37,12 @@ describe("glam_openfunds", () => { }, ], // Glam - isEnabled: true, + accountType: { fund: {} }, + enabled: true, assets: [USDC, WBTC, WSOL], + metadata: { + template: { openfunds: {} }, + }, // Openfunds (Fund) rawOpenfunds: { fundDomicileAlpha2: "XS", @@ -71,18 +75,16 @@ describe("glam_openfunds", () => { mints: [ { // Glam Token - name: "Glam Investment Fund BTC-SOL", + name: "Glam Investment Fund BTC-SOL (b)", symbol: "GBS", uri: "", - fundId: new PublicKey(0), + statePubkey: null, asset: USDC, imageUri: "", // Glam permanentDelegate: new PublicKey(0), lockUpPeriodInSeconds: 40 * 24 * 60 * 60, defaultAccountStateFrozen: false, - allowlist: [], - blocklist: [], isRawOpenfunds: true, // Openfunds Share Class rawOpenfunds: { @@ -105,7 +107,8 @@ describe("glam_openfunds", () => { }, ], // Glam - isEnabled: true, + accountType: { fund: {} }, + enabled: true, assets: [USDC, WBTC, WSOL], // Openfunds (Fund) rawOpenfunds: { diff --git a/anchor/tests/glam_sol_msol.spec.ts b/anchor/tests/glam_sol_msol.spec.ts deleted file mode 100644 index 6b5e5ec4..00000000 --- a/anchor/tests/glam_sol_msol.spec.ts +++ /dev/null @@ -1,508 +0,0 @@ -import { BN, Wallet } from "@coral-xyz/anchor"; -import { Keypair, PublicKey } from "@solana/web3.js"; -import { - getAssociatedTokenAddressSync, - TOKEN_PROGRAM_ID, - TOKEN_2022_PROGRAM_ID, - getMint, - getAccount, -} from "@solana/spl-token"; - -import { - stateModelForTest, - quoteResponseForTest, - swapInstructionsForTest, - sleep, - airdrop, - createGlamStateForTest, -} from "./setup"; -import { StateModel, GlamClient, MSOL, WSOL } from "../src"; - -describe("glam_sol_msol", () => { - const glamClient = new GlamClient(); - - const userKeypairs = [ - Keypair.generate(), // alice - Keypair.generate(), // bob - Keypair.generate(), // eve - ]; - const alice = userKeypairs[0]; - const bob = userKeypairs[1]; - const eve = userKeypairs[2]; - const glamClientAlice = new GlamClient({ wallet: new Wallet(alice) }); - const glamClientBob = new GlamClient({ wallet: new Wallet(bob) }); - const glamClientEve = new GlamClient({ wallet: new Wallet(eve) }); - - const stateForTest = { - ...stateModelForTest, - name: "Glam SOL-mSOL", - assets: [WSOL, MSOL], - integrationAcls: [ - { name: { marinade: {} }, features: [] }, - { name: { jupiterSwap: {} }, features: [] }, - { name: { nativeStaking: {} }, features: [] }, - ], - } as Partial; - - const statePda = glamClient.getStatePda(stateForTest); - const vaultPda = glamClient.getVaultPda(statePda); - const sharePda = glamClient.getShareClassPda(statePda, 0); - - const connection = glamClient.provider.connection; - const commitment = "confirmed"; - - let defaultVote; - - beforeAll(async () => { - try { - await Promise.all( - userKeypairs.map(async (user) => { - // send 10 SOL to each user - await airdrop(connection, user.publicKey, 10_000_000_000); - }), - ); - - // - // create fund - // - const fundData = await createGlamStateForTest(glamClient, stateForTest); - - // default vote account - const voteAccountStatus = await connection.getVoteAccounts(); - const vote = voteAccountStatus.current.sort( - (a, b) => b.activatedStake - a.activatedStake, - )[0].votePubkey; - defaultVote = new PublicKey(vote); - } catch (e) { - console.error(e); - throw e; - } - }, /* timeout */ 15_000); - - it("Fund created", async () => { - try { - const state = await glamClient.fetchStateAccount(statePda); - expect(state.name).toEqual(stateForTest.name); - } catch (e) { - console.error(e); - } - }); - - it("Alice subscribes to fund with 1 SOL", async () => { - const amount = new BN(1_000_000_000); - try { - const txId = await glamClientAlice.investor.subscribe( - statePda, - WSOL, - amount, - ); - console.log("tx:", txId); - } catch (e) { - console.error(e); - throw e; - } - - const shares = await getMint( - connection, - sharePda, - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(shares.supply) / 1e9).toFixed(2)).toEqual("1.00"); - const sharesAta = await getAccount( - connection, - glamClientAlice.getShareClassAta(alice.publicKey, sharePda), - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(sharesAta.amount) / 1e9).toFixed(2)).toEqual("1.00"); - }); - - it("Bob subscribes to fund with 2 SOL", async () => { - const amount = new BN(2_000_000_000); - try { - const txId = await glamClientBob.investor.subscribe( - statePda, - WSOL, - amount, - ); - console.log("tx:", txId); - } catch (e) { - console.error(e); - throw e; - } - - const shares = await getMint( - connection, - sharePda, - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(shares.supply) / 1e9).toFixed(2)).toEqual("3.00"); - const sharesAta = await getAccount( - connection, - glamClientBob.getShareClassAta(bob.publicKey, sharePda), - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(sharesAta.amount) / 1e9).toFixed(2)).toEqual("2.00"); - }); - - it("Alice subscribes to fund with 3 SOL", async () => { - const amount = new BN(3_000_000_000); - try { - const txId = await glamClientAlice.investor.subscribe( - statePda, - WSOL, - amount, - ); - console.log("tx:", txId); - } catch (e) { - console.error(e); - throw e; - } - - const shares = await getMint( - connection, - sharePda, - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(shares.supply) / 1e9).toFixed(2)).toEqual("6.00"); - const sharesAta = await getAccount( - connection, - glamClientAlice.getShareClassAta(alice.publicKey, sharePda), - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(sharesAta.amount) / 1e9).toFixed(2)).toEqual("4.00"); - }); - - it("Alice redeems 2 shares", async () => { - const amount = new BN(2_000_000_000); - try { - const txId = await glamClientAlice.investor.redeem( - statePda, - amount, - true, - ); - console.log("tx:", txId); - } catch (e) { - console.error(e); - throw e; - } - - const shares = await getMint( - connection, - sharePda, - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(shares.supply) / 1e9).toFixed(2)).toEqual("4.00"); - const sharesAta = await getAccount( - connection, - glamClientAlice.getShareClassAta(alice.publicKey, sharePda), - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(sharesAta.amount) / 1e9).toFixed(2)).toEqual("2.00"); - const wsolAta = await getAccount( - connection, - getAssociatedTokenAddressSync(WSOL, alice.publicKey), - commitment, - TOKEN_PROGRAM_ID, - ); - expect((Number(wsolAta.amount) / 1e9).toFixed(2)).toEqual("2.00"); - }); - - /* - * Modify share price - */ - - it("Alice subscribes to fund with 3 SOL", async () => { - /* The fund has 4 SOL for 4 shares (share price is 1 SOL). - We airdrop 2 SOL. - The fund has 6 SOL for 4 shares (share price is 1.5 SOL). - With 3 SOL, Alice gets 2 shares. - The fund has 9 SOL for 6 shares. */ - await airdrop(connection, vaultPda, 2_000_000_000); - - const amount = new BN(3_000_000_000); - try { - const txId = await glamClientAlice.investor.subscribe( - statePda, - WSOL, - amount, - ); - console.log("tx:", txId); - } catch (e) { - console.error(e); - throw e; - } - - const shares = await getMint( - connection, - sharePda, - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(shares.supply) / 1e9).toFixed(2)).toEqual("6.00"); - const sharesAta = await getAccount( - connection, - glamClientAlice.getShareClassAta(alice.publicKey, sharePda), - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(sharesAta.amount) / 1e9).toFixed(2)).toEqual("4.00"); - }); - - it("Alice redeems 2 shares", async () => { - /* The fund has 9 SOL for 6 shares (share price is 1.5 SOL). - We airdrop 3 SOL. - The fund has 12 SOL for 6 shares (share price is 2 SOL). - With 1 share, Alice gets 2 SOL. - The fund has 10 SOL for 5 shares. */ - await airdrop(connection, vaultPda, 3_000_000_000); - - const amount = new BN(1_000_000_000); - try { - const txId = await glamClientAlice.investor.redeem( - statePda, - amount, - true, - ); - console.log("tx:", txId); - } catch (e) { - console.error(e); - throw e; - } - - const shares = await getMint( - connection, - sharePda, - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(shares.supply) / 1e9).toFixed(2)).toEqual("5.00"); - const sharesAta = await getAccount( - connection, - glamClientAlice.getShareClassAta(alice.publicKey, sharePda), - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(sharesAta.amount) / 1e9).toFixed(2)).toEqual("3.00"); - const wsolAta = await getAccount( - connection, - getAssociatedTokenAddressSync(WSOL, alice.publicKey), - commitment, - TOKEN_PROGRAM_ID, - ); - expect((Number(wsolAta.amount) / 1e9).toFixed(2)).toEqual("2.00"); - }); - - /* - * mSOL - */ - - it("Manager swaps some SOL", async () => { - /* The fund has 10 SOL for 5 shares. - We swap .05 SOL for .41795954 mSOL. - mSOL price is 1.186356194 SOL. - The value of the fund is almost unchanged. */ - - const signer = glamClient.getSigner(); - const inputSignerAta = glamClient.getAta(WSOL); - const outputSignerAta = glamClient.getAta(MSOL); - - const quoteResponse = quoteResponseForTest; - const swapInstructions = swapInstructionsForTest( - signer, - inputSignerAta, - outputSignerAta, - ); - - const amount = 50_000_000; - try { - const txId = await glamClient.jupiter.swap( - statePda, - undefined, - quoteResponse, - swapInstructions, - ); - console.log("swap e2e txId", txId); - } catch (e) { - console.error(e); - throw e; - } - }); - - it("Alice subscribes to fund with 3 SOL", async () => { - /* The fund has ~10 SOL for 5 shares. - - After subscribing with 3 SOL, the fund has ~13 SOL for ~6.5 shares. - Alice receives ~1.5 shares */ - - const amount = new BN(3_000_000_000); - try { - const txId = await glamClientAlice.investor.subscribe( - statePda, - WSOL, - amount, - ); - console.log("tx:", txId); - } catch (e) { - console.error(e); - throw e; - } - - const shares = await getMint( - connection, - sharePda, - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(shares.supply) / 1e9).toFixed(2)).toEqual("6.50"); - const sharesAta = await getAccount( - connection, - glamClientAlice.getShareClassAta(alice.publicKey, sharePda), - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(sharesAta.amount) / 1e9).toFixed(2)).toEqual("4.50"); - }); - - it("Alice redeems 1 share", async () => { - /* The fund has ~13 SOL (wSOL & mSOL) for ~6.5 shares. - After in-kind redeeming 1 share, alice gets <2 wSOL. */ - - const amount = new BN(1_000_000_000); - try { - const txId = await glamClientAlice.investor.redeem( - statePda, - amount, - true, - ); - console.log("tx:", txId); - } catch (e) { - console.error(e); - throw e; - } - - const shares = await getMint( - connection, - sharePda, - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(shares.supply) / 1e9).toFixed(2)).toEqual("5.50"); - const sharesAta = await getAccount( - connection, - glamClientAlice.getShareClassAta(alice.publicKey, sharePda), - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(sharesAta.amount) / 1e9).toFixed(2)).toEqual("3.50"); - - // Alice gets less than 2 wSOL and some mSOL - const wsolAta = await getAccount( - connection, - getAssociatedTokenAddressSync(WSOL, alice.publicKey), - commitment, - TOKEN_PROGRAM_ID, - ); - expect((Number(wsolAta.amount) / 1e9).toFixed(2)).toEqual("1.99"); - const msolAta = await getAccount( - connection, - getAssociatedTokenAddressSync(MSOL, alice.publicKey), - commitment, - TOKEN_PROGRAM_ID, - ); - expect((Number(msolAta.amount) / 1e9).toFixed(3)).toEqual("0.006"); - }); - - it("Manager orders marinade delayed stake and delegates stake", async () => { - try { - let txSig = await glamClient.marinade.delayedUnstake( - statePda, - new BN(30_000_000), - ); - console.log("delayedUnstake txSig", txSig); - - txSig = await glamClient.staking.initializeAndDelegateStake( - statePda, - defaultVote, - new BN(2_000_000_000), - ); - console.log("initializeAndDelegateStake txSig", txSig); - } catch (e) { - console.error(e); - throw e; - } - }); - - it("Eve subscribes to fund with 1 SOL twice at different epochs", async () => { - /* The fund has ~11 SOL for 5.5 shares. */ - try { - const txId = await glamClientEve.investor.subscribe( - statePda, - WSOL, - new BN(1_000_000_000), - ); - console.log("eve subscribe #0 tx:", txId); - } catch (e) { - console.error(e); - throw e; - } - - let shares = await getMint( - connection, - sharePda, - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(shares.supply) / 1e9).toFixed(2)).toEqual("6.00"); - const sharesAtaEpoch0 = await getAccount( - connection, - glamClientAlice.getShareClassAta(eve.publicKey, sharePda), - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(sharesAtaEpoch0.amount) / 1e9).toFixed(2)).toEqual("0.50"); - - await sleep(15_000); // wait for epoch change - - /* The fund has ~12 SOL for 6.0 shares. */ - try { - const txId = await glamClientEve.investor.subscribe( - statePda, - WSOL, - new BN(1_000_000_000), - ); - console.log("eve subscribe #1 tx:", txId); - } catch (e) { - console.error(e); - throw e; - } - - shares = await getMint( - connection, - sharePda, - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(shares.supply) / 1e9).toFixed(2)).toEqual("6.50"); - const sharesAtaEpoch1 = await getAccount( - connection, - glamClientAlice.getShareClassAta(eve.publicKey, sharePda), - commitment, - TOKEN_2022_PROGRAM_ID, - ); - expect((Number(sharesAtaEpoch1.amount) / 1e9).toFixed(2)).toEqual("1.00"); - - // shares received at epoch 1 should be less than shares received at epoch 0 - // because the fund has more SOL at epoch 1 due to staking yield - expect(sharesAtaEpoch1.amount - sharesAtaEpoch0.amount).toBeLessThan( - sharesAtaEpoch0.amount, - ); - }, 30_000); -}); diff --git a/anchor/tests/glam_staking.spec.ts b/anchor/tests/glam_staking.spec.ts index 61678ba7..533072b0 100644 --- a/anchor/tests/glam_staking.spec.ts +++ b/anchor/tests/glam_staking.spec.ts @@ -35,10 +35,10 @@ describe("glam_staking", () => { statePda = stateData.statePda; const txSig = await glamClient.state.updateState(statePda, { - integrationAcls: [ - { name: { nativeStaking: {} }, features: [] }, - { name: { splStakePool: {} }, features: [] }, - { name: { sanctumStakePool: {} }, features: [] }, + integrations: [ + { nativeStaking: {} }, + { splStakePool: {} }, + { sanctumStakePool: {} }, ], }); diff --git a/anchor/tests/glam_vault.spec.ts b/anchor/tests/glam_vault.spec.ts deleted file mode 100644 index 9666cc3a..00000000 --- a/anchor/tests/glam_vault.spec.ts +++ /dev/null @@ -1,468 +0,0 @@ -import { - Keypair, - PublicKey, - SystemProgram, - Transaction, -} from "@solana/web3.js"; -import { BN, Wallet } from "@coral-xyz/anchor"; - -import { createGlamStateForTest, stateModelForTest, str2seed } from "./setup"; -import { StateModel, GlamClient, MSOL, USDC, WSOL } from "../src"; -import { - createMint, - createAssociatedTokenAccount, - mintTo, - TOKEN_PROGRAM_ID, - getAccount, - createAssociatedTokenAccountIdempotentInstruction, -} from "@solana/spl-token"; - -const key1 = Keypair.fromSeed(str2seed("acl_test_key1")); -const key2 = Keypair.fromSeed(str2seed("acl_test_key2")); - -describe("glam_vault", () => { - const glamClient = new GlamClient(); - const glamClientCustomWallet = new GlamClient({ wallet: new Wallet(key1) }); - let statePda: PublicKey; - - it("Initialize vault", async () => { - let stateForTest = { ...stateModelForTest }; - stateForTest.mints = []; - const stateData = await createGlamStateForTest(glamClient, stateForTest); - - statePda = stateData.statePda; - - const state = await glamClient.fetchState(statePda); - expect(state.mints.length).toEqual(0); - }); - - it("Update vault name", async () => { - const updated = { name: "Updated vault name" }; - try { - const txSig = await glamClient.state.updateState(statePda, updated); - console.log("Update vault name txSig", txSig); - } catch (e) { - console.error(e); - throw e; - } - const state = await glamClient.fetchStateAccount(statePda); - expect(state.name).toEqual(updated.name); - }); - - it("Update vault asset allowlist", async () => { - // The test glam state has 2 assets, WSOL and MSOL. Update to USDC. - let updatedState = { assets: [USDC] }; - try { - const txSig = await glamClient.state.updateState(statePda, updatedState); - console.log("Update vault assets (USDC) txSig", txSig); - } catch (e) { - console.error(e); - throw e; - } - let stateModel = (await glamClient.fetchState(statePda)) as StateModel; - expect(stateModel.assets).toEqual([USDC]); - - // Update assets back to WSOL and MSOL - updatedState = { assets: [WSOL, MSOL] }; - try { - const txSig = await glamClient.state.updateState(statePda, updatedState); - console.log("Update vault assets (WSOL and MSOL) txSig", txSig); - } catch (e) { - console.error(e); - throw e; - } - stateModel = (await glamClient.fetchState(statePda)) as StateModel; - expect(stateModel.assets).toEqual([WSOL, MSOL]); - }); - - it("[integration-acl] add and update", async () => { - const updatedState1 = { - integrationAcls: [{ name: { drift: {} }, features: [] }], - }; - try { - const txSig = await glamClient.state.updateState(statePda, updatedState1); - console.log("Update integration acl txSig", txSig); - } catch (e) { - console.error(e); - throw e; - } - const stateModel1 = await glamClient.fetchState(statePda); - expect(stateModel1.integrationAcls.length).toEqual(1); - expect(stateModel1.integrationAcls).toEqual(updatedState1?.integrationAcls); - - const updatedState2 = { - integrationAcls: [ - { name: { drift: {} }, features: [] }, - { name: { jupiterSwap: {} }, features: [] }, - { name: { marinade: {} }, features: [] }, - { name: { splStakePool: {} }, features: [] }, - { name: { sanctumStakePool: {} }, features: [] }, - ], - }; - try { - const txSig = await glamClient.state.updateState(statePda, updatedState2); - console.log("Update integration acl txSig", txSig); - } catch (e) { - console.error(e); - throw e; - } - }); - - it("[delegate-acl] upsert", async () => { - // empty acls - let stateModel = await glamClient.fetchState(statePda); - expect(stateModel.delegateAcls.length).toEqual(0); - - // grant key1 wSolWrap permission - const delegateAcls = [ - { pubkey: key1.publicKey, permissions: [{ wSolWrap: {} }] }, - ]; - try { - const txSig = await glamClient.state.upsertDelegateAcls( - statePda, - delegateAcls, - ); - console.log("Update delegate acl txSig", txSig); - } catch (e) { - console.error(e); - throw e; - } - stateModel = await glamClient.fetchState(statePda); - expect(stateModel.delegateAcls?.length).toEqual(1); - expect(stateModel.delegateAcls[0].pubkey).toEqual(key1.publicKey); - expect(stateModel.delegateAcls[0].permissions).toEqual([{ wSolWrap: {} }]); - - // grant key1 wSolWrap and wSolUnwrap permission - try { - const txSig = await glamClient.state.upsertDelegateAcls(statePda, [ - { - pubkey: key1.publicKey, - permissions: [{ wSolWrap: {} }, { wSolUnwrap: {} }], - }, - ]); - console.log("Update delegate acl txSig", txSig); - } catch (e) { - console.error(e); - throw e; - } - stateModel = await glamClient.fetchState(statePda); - expect(stateModel.delegateAcls?.length).toEqual(1); - expect(stateModel.delegateAcls[0].pubkey).toEqual(key1.publicKey); - expect(stateModel.delegateAcls[0].permissions).toEqual([ - { wSolWrap: {} }, - { wSolUnwrap: {} }, - ]); - }); - - it("[delegate-acl] delete", async () => { - // add key2 permissions - const delegateAcls = [ - { pubkey: key2.publicKey, permissions: [{ stake: {} }] }, - ]; - try { - const txSig = await glamClient.state.upsertDelegateAcls( - statePda, - delegateAcls, - ); - console.log("Update delegate acl txSig", txSig); - } catch (e) { - console.error(e); - throw e; - } - let stateModel = await glamClient.fetchState(statePda); - expect(stateModel.delegateAcls?.length).toEqual(2); - expect(stateModel.delegateAcls[1].pubkey).toEqual(key2.publicKey); - expect(stateModel.delegateAcls[1].permissions).toEqual([{ stake: {} }]); - - // remove key1 and key2 permissions - try { - const txSig = await glamClient.state.deleteDelegateAcls(statePda, [ - key1.publicKey, - key2.publicKey, - ]); - console.log("Delete delegate acl txSig", txSig); - } catch (e) { - console.error(e); - throw e; - } - stateModel = await glamClient.fetchState(statePda); - expect(stateModel.delegateAcls?.length).toEqual(0); - }); - - it("[delegate-acl] test authorization", async () => { - // transfer 1 SOL to treasury - const tranferTx = new Transaction().add( - SystemProgram.transfer({ - fromPubkey: glamClient.getSigner(), - toPubkey: glamClient.getVaultPda(statePda), - lamports: 1_000_000_000, - }), - ); - await glamClient.sendAndConfirm(tranferTx); - - // 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.getSigner(), - toPubkey: key1.publicKey, - lamports: 100_000_000, - }), - ); - await glamClient.sendAndConfirm(tranferTx2); - - // grant key1 wSolWrap permission - let updatedState = { - delegateAcls: [ - { pubkey: key1.publicKey, permissions: [{ wSolWrap: {} }] }, - ], - }; - try { - const txSig = await glamClient.state.updateState(statePda, updatedState); - console.log("Update delegate acl txSig", txSig); - } catch (e) { - console.error(e); - throw e; - } - let stateModel = await glamClient.fetchState(statePda); - expect(stateModel.delegateAcls?.length).toEqual(1); - expect(stateModel.delegateAcls[0].pubkey).toEqual(key1.publicKey); - - // key1 now has wSolWrap permission, use key1 to wrap some SOL - try { - const tx = await glamClientCustomWallet.wsol.wrap( - statePda, - new BN(30_000_000), - ); - console.log("Wrap:", tx); - } catch (e) { - console.log("Error", e); - throw e; - } - - // key1 doesn't have wSolUnwrap permission, unwrap should fail - try { - const txId = await glamClientCustomWallet.wsol.unwrap(statePda); - console.log("Unwrap:", txId); - expect(txId).toBeUndefined(); - } catch (e) { - const expectedError = e.programLogs.some((log) => - log.includes("Signer is not authorized"), - ); - expect(expectedError).toBeTruthy(); - } - }, 15_000); - - it("Update state unauthorized", async () => { - const updatedState = { name: "Updated name" }; - try { - const txId = await glamClient.program.methods - .updateState(new StateModel(updatedState)) - .accounts({ - state: statePda, - signer: key1.publicKey, - }) - .signers([key1]) - .rpc(); - expect(txId).toBeUndefined(); - } catch (e) { - expect(e.error.errorMessage).toEqual("Signer is not authorized"); - } - }); - - it("Update manager", async () => { - const manager = glamClient.getSigner(); - const newOwner = Keypair.fromSeed(str2seed("new-owner")); - - const updated = { - owner: { - portfolioManagerName: "New Owner", - pubkey: newOwner.publicKey, - kind: { wallet: {} }, - }, - }; - try { - await glamClient.state.updateState(statePda, updated); - } catch (e) { - console.error(e); - throw e; - } - let state = await glamClient.fetchStateAccount(statePda); - expect(state.owner.toString()).toEqual(newOwner.publicKey.toString()); - - const updatedState2 = new StateModel({ - owner: { - portfolioManagerName: "Old Owner", - pubkey: manager, - kind: { wallet: {} }, - }, - }); - - // old manager can NOT update back - try { - const txId = await glamClient.program.methods - .updateState(updatedState2) - .accounts({ - state: statePda, - signer: manager, - }) - .rpc(); - expect(txId).toBeUndefined(); - } catch (e) { - expect(e.message).toContain("not authorized"); - } - - // new manager CAN update back - try { - const txId = await glamClient.program.methods - .updateState(updatedState2) - .accounts({ - state: statePda, - signer: newOwner.publicKey, - }) - .signers([newOwner]) - .rpc(); - } catch (e) { - console.error(e); - throw e; - } - state = await glamClient.fetchStateAccount(statePda); - expect(state.owner.toString()).toEqual(manager.toString()); - }); - - it("Close token accounts", async () => { - const treasury = glamClient.getVaultPda(statePda); - - // Create empty token accounts - const transaction = new Transaction(); - for (const mint of [WSOL, MSOL]) { - transaction.add( - createAssociatedTokenAccountIdempotentInstruction( - glamClient.getSigner(), - glamClient.getVaultAta(statePda, mint), - treasury, - mint, - ), - ); - } - const txSig = await glamClient.sendAndConfirm(transaction); - console.log("Creating ata for treasury:", txSig); - - let tokenAccounts = await glamClient.getTokenAccountsByOwner(treasury); - expect(tokenAccounts.length).toEqual(2); - - // Close token accounts - try { - const txSig = await glamClient.state.closeTokenAccounts( - statePda, - tokenAccounts.map((ta) => ta.pubkey), - ); - console.log("Close token accounts:", txSig); - } catch (e) { - console.error(e); - throw e; - } - tokenAccounts = await glamClient.getTokenAccountsByOwner(treasury); - expect(tokenAccounts.length).toEqual(0); - }); - - it("Deposit and withdraw", async () => { - const connection = glamClient.provider.connection; - const commitment = "confirmed"; - const manager = glamClient.getWallet(); - - const mintKeypair = Keypair.fromSeed(str2seed("usdc")); - const mint = mintKeypair.publicKey; - - const amount = 100_000_000; - - await createMint( - connection, - manager.payer, - manager.publicKey, - null, - 6, - mintKeypair, - { commitment }, - TOKEN_PROGRAM_ID, - ); - - const managerAta = await createAssociatedTokenAccount( - connection, - manager.payer, - mint, - manager.publicKey, - {}, - TOKEN_PROGRAM_ID, - ); - - await mintTo( - connection, - manager.payer, - mint, - managerAta, - manager.payer, - amount * 10, - [], - { commitment }, - TOKEN_PROGRAM_ID, - ); - - // Create empty token accounts - try { - const txSig = await glamClient.state.deposit(statePda, mint, amount); - console.log("Deposit:", txSig); - } catch (e) { - console.error(e); - throw e; - } - - let tokenAccount = await getAccount( - connection, - managerAta, - undefined, - TOKEN_PROGRAM_ID, - ); - expect(tokenAccount.amount.toString()).toEqual((amount * 9).toString()); - - // Transfer out - try { - const txSig = await glamClient.state.withdraw(statePda, mint, amount); - console.log("Withdraw:", txSig); - } catch (e) { - console.error(e); - throw e; - } - - tokenAccount = await getAccount( - connection, - managerAta, - undefined, - TOKEN_PROGRAM_ID, - ); - expect(tokenAccount.amount.toString()).toEqual((amount * 10).toString()); - }); - - it("Close vault", async () => { - const state = await glamClient.fetchStateAccount(statePda); - expect(state).not.toBeNull(); - - try { - const txId = await glamClient.state.closeState(statePda); - console.log("Close state account txId:", txId); - } catch (e) { - console.error(e); - throw e; - } - - // The following accounts should no longer exist - const vault = glamClient.getVaultPda(statePda); - const openfunds = glamClient.getOpenfundsPda(statePda); - const ret = await Promise.all( - [statePda, vault, openfunds].map( - async (address) => - await glamClient.provider.connection.getAccountInfo(address), - ), - ); - expect(ret).toEqual([null, null, null]); - }); -}); diff --git a/anchor/tests/setup.ts b/anchor/tests/setup.ts index 4da20895..7bc79d74 100644 --- a/anchor/tests/setup.ts +++ b/anchor/tests/setup.ts @@ -47,7 +47,6 @@ export const stateModelForTest = { name: "Glam Fund SOL-mSOL", symbol: "GBS", uri: "", - fundId: new PublicKey(0), asset: USDC, imageUri: "", isRawOpenfunds: true, @@ -78,12 +77,13 @@ export const stateModelForTest = { }, ], // Glam - isEnabled: true, + accountType: { vault: {} }, + enabled: true, assets: [WSOL, MSOL], // Openfunds (Fund) rawOpenfunds: { fundDomicileAlpha2: "XS", - legalFundNameIncludingUmbrella: "Glam Fund SOL-mSOL", + // legalFundNameIncludingUmbrella: "Glam Fund SOL-mSOL", fundLaunchDate: new Date().toISOString().split("T")[0], investmentObjective: "demo", fundCurrency: "SOL", diff --git a/cli/src/main.ts b/cli/src/main.ts index bbb0f702..5c373c1a 100644 --- a/cli/src/main.ts +++ b/cli/src/main.ts @@ -1,7 +1,7 @@ import * as anchor from "@coral-xyz/anchor"; import { StateModel, - IntegrationName, + Integration, WSOL, getPriorityFeeEstimate, GlamClient, @@ -105,9 +105,9 @@ program console.log( state.productType, "\t", - state.id.toBase58(), + state.idStr, "\t", - state.rawOpenfunds.fundLaunchDate, + state.launchDate, "\t", state.name, ); @@ -155,10 +155,14 @@ program const glamState = JSON.parse(data); // Convert pubkey strings to PublicKey objects - for (let i = 0; i < glamState?.mints?.length || 0; ++i) { - glamState.mints[i].asset = new PublicKey(glamState.mints[i].asset); - } + glamState.mints?.forEach((mint) => { + mint.asset = new PublicKey(mint.asset); + mint.permanentDelegate = mint.permanentDelegate + ? new PublicKey(mint.permanentDelegate) + : null; + }); glamState.assets = glamState.assets.map((a) => new PublicKey(a)); + glamState.accountType = { [glamState.accountType]: {} }; try { const [txSig, statePda] = await glamClient.state.createState(glamState); @@ -209,7 +213,6 @@ program .accounts({ state: statePda, shareClassMint: glamClient.getShareClassPda(statePda, 0), - metadata: glamClient.getOpenfundsPda(statePda), }) .instruction(); preInstructions.push(closeShareClassIx); @@ -219,7 +222,6 @@ program .closeState() .accounts({ state: statePda, - metadata: glamClient.getOpenfundsPda(statePda), }) .preInstructions(preInstructions); @@ -347,6 +349,7 @@ delegate permissions: permissions.map((p) => ({ [p]: {}, })), + expiresAt: new anchor.BN(0), }, ]); console.log("txSig:", txSig); @@ -399,14 +402,14 @@ integration } const stateModel = await glamClient.fetchState(statePda); - const cnt = stateModel.integrationAcls.length; + const cnt = stateModel.integrations.length; console.log( `${stateModel.name} (${statePda.toBase58()}) has ${cnt} integration${ cnt > 1 ? "s" : "" } enabled`, ); - for (let [i, acl] of stateModel.integrationAcls.entries()) { - console.log(`[${i}] ${Object.keys(acl.name)[0]}`); + for (let [i, integ] of stateModel.integrations.entries()) { + console.log(`[${i}] ${Object.keys(integ)[0]}`); } }); @@ -442,8 +445,8 @@ integration } const stateModel = await glamClient.fetchState(statePda); - const acl = stateModel.integrationAcls.find( - (integ) => Object.keys(integ.name)[0] === integration, + const acl = stateModel.integrations.find( + (integ) => Object.keys(integ)[0] === integration, ); if (acl) { console.log( @@ -453,10 +456,8 @@ integration } const updated = new StateModel({ - integrationAcls: [ - ...stateModel.integrationAcls, - { name: { [integration]: {} } as IntegrationName, features: [] }, - ], + // @ts-ignore + integrations: [...stateModel.integrations, { [integration]: {} }], }); try { @@ -494,8 +495,8 @@ integration const stateModel = await glamClient.fetchState(statePda); const updated = new StateModel({ - integrationAcls: stateModel.integrationAcls.filter( - (integ) => Object.keys(integ.name)[0] !== integration, + integrations: stateModel.integrations.filter( + (integ) => Object.keys(integ)[0] !== integration, ), }); diff --git a/cli/templates/fund.json b/cli/templates/fund.json index 294e32e3..43677bbd 100644 --- a/cli/templates/fund.json +++ b/cli/templates/fund.json @@ -1,6 +1,7 @@ { + "accountType": "fund", "name": "GLAM Fund XYZ", - "isEnabled": true, + "enabled": true, "assets": [ "So11111111111111111111111111111111111111112", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" @@ -29,7 +30,6 @@ "allowlist": [], "blocklist": [], "lockUpTime": 0, - "permanentDelegate": "11111111111111111111111111111111", "defaultAccountStateFrozen": false, "fullShareClassName": "GLAM Token XYZ", "isin": "", diff --git a/cli/templates/mint.json b/cli/templates/mint.json index 36f6e085..c6f9d572 100644 --- a/cli/templates/mint.json +++ b/cli/templates/mint.json @@ -1,6 +1,7 @@ { + "accountType": "mint", "name": "GLAM Mint XYZ", - "isEnabled": true, + "enabled": true, "assets": [ "So11111111111111111111111111111111111111112", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" @@ -20,7 +21,6 @@ "emailAddressOfManCo": "hello@glam.systems", "fundWebsiteOfManCo": "https://glam.systems" }, - "owner": { "portfolioManagerName": "glam.sol" }, "mints": [ { "name": "GLAM Token XYZ", diff --git a/cli/templates/vault.json b/cli/templates/vault.json index b3c5e092..9708f111 100644 --- a/cli/templates/vault.json +++ b/cli/templates/vault.json @@ -1,6 +1,7 @@ { + "accountType": "vault", "name": "GLAM Vault XYZ", - "isEnabled": true, + "enabled": true, "assets": [ "So11111111111111111111111111111111111111112", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" diff --git a/playground/src/app/(mint)/mint/context/page.tsx b/playground/src/app/(mint)/mint/context/page.tsx index 0aa091d3..cf67c350 100644 --- a/playground/src/app/(mint)/mint/context/page.tsx +++ b/playground/src/app/(mint)/mint/context/page.tsx @@ -55,8 +55,10 @@ export default function MintContextPage() { fundDomicileAlpha2: fund.rawOpenfunds?.fundDomicileAlpha2, }, shareClass: { - iSIN: fund.mints[0].rawOpenfunds?.isin, - shareClassCurrency: fund.mints[0].rawOpenfunds?.shareClassCurrency, + iSIN: fund.mints ? fund.mints[0].rawOpenfunds?.isin : null, + shareClassCurrency: fund.mints + ? fund.mints[0].rawOpenfunds?.shareClassCurrency + : null, }, }); diff --git a/playground/src/app/(mint)/mint/create/createMintForm.tsx b/playground/src/app/(mint)/mint/create/createMintForm.tsx index ed347e85..2020a2ac 100644 --- a/playground/src/app/(mint)/mint/create/createMintForm.tsx +++ b/playground/src/app/(mint)/mint/create/createMintForm.tsx @@ -123,27 +123,21 @@ export default function MultiStepForm() { }); const glamState = { + accountType: { mint: {} }, name: basicInfoFormData.name, - isEnabled: true, + enabled: true, rawOpenfunds: { fundDomicileAlpha2: openfundsData.fund.fundDomicileAlpha2, - } as Partial, - // @ts-ignore - integrationAcls: [{ name: { mint: {} }, features: [] }], + }, company: { fundGroupName: openfundsData.company.fundGroupName, - } as Partial, + }, owner: { pubkey: glamClient.getSigner(), kind: { wallet: {} }, - } as Partial, + }, mints: [ { - uri: "", - fundId: null, - imageUri: "", - allowlist: [], - blocklist: [], name: basicInfoFormData.name, symbol: basicInfoFormData.symbol, asset: new PublicKey(0), @@ -153,16 +147,17 @@ export default function MultiStepForm() { : new PublicKey(0), defaultAccountStateFrozen: policyFormData.defaultAccountStateFrozen, isRawOpenfunds: true, - rawOpenfunds: { - isin: openfundsData.shareClass.iSIN, - shareClassCurrency: openfundsData.shareClass.shareClassCurrency, - } as Partial, + // rawOpenfunds: { + // isin: openfundsData.shareClass.iSIN, + // shareClassCurrency: openfundsData.shareClass.shareClassCurrency, + // }, }, ], - } as Partial; + }; try { const [txSig, statePda] = await glamClient.state.createState( + // @ts-ignore glamState, true, ); diff --git a/playground/src/app/(playground)/playground/create/page.tsx b/playground/src/app/(playground)/playground/create/page.tsx index 0c1ff872..6bbbf1c6 100644 --- a/playground/src/app/(playground)/playground/create/page.tsx +++ b/playground/src/app/(playground)/playground/create/page.tsx @@ -60,7 +60,7 @@ export default function Create() { mints: [ { uri: "", - fundId: null, + statePubkey: null, imageUri: "", name: values.productName, symbol: values.productName.substring(0, 4).toUpperCase(), @@ -102,7 +102,7 @@ export default function Create() { } as Partial, }, ], - isEnabled: true, + enabled: true, rawOpenfunds: { fundDomicileAlpha2: "XS", legalFundNameIncludingUmbrella: values.productName, diff --git a/playground/src/app/(playground)/playground/flows/page.tsx b/playground/src/app/(playground)/playground/flows/page.tsx index d0d1cde3..39fd66ad 100644 --- a/playground/src/app/(playground)/playground/flows/page.tsx +++ b/playground/src/app/(playground)/playground/flows/page.tsx @@ -76,7 +76,7 @@ function InvestorDisclaimers({ direction: string; method: string; }) { - const share = fund?.mints[0]; + const share = fund?.mints ? fund.mints[0] : null; if (!fund || !share) return null; const lockUp = Number(share?.lockUpPeriodInSeconds || 0); @@ -405,7 +405,7 @@ function InvestorWidget({ fundModel }: { fundModel?: StateModel }) { setBalance(0); if (direction === "redeem") { - const symbol = fundModel?.mints[0]?.symbol || "Share"; + const symbol = (fundModel?.mints || [])[0]?.symbol || "Share"; const mint = fundModel?.shareClassMints[0]; setAmountInAsset(symbol); @@ -424,7 +424,7 @@ function InvestorWidget({ fundModel }: { fundModel?: StateModel }) { setAmountInAsset(fundModel?.rawOpenfunds?.fundCurrency || "SOL"); // update balance - const mint = fundModel?.assets[0] || WSOL; + const mint = (fundModel?.assets || [WSOL])[0]; if (WSOL.equals(mint)) { setBalance((userWallet?.balanceLamports || 0) / LAMPORTS_PER_SOL); } else { @@ -512,7 +512,7 @@ function InvestorWidget({ fundModel }: { fundModel?: StateModel }) { }, ); } else { - const asset = fundModel.assets[0]; + const asset = (fundModel.assets || [])[0]; const ata = (userWallet?.tokenAccounts || []).find((a) => a.mint.equals(asset), )!; @@ -718,7 +718,7 @@ export default function Flows() { No product found. {allGlamStates - .filter((f) => f.mints.length > 0) + .filter((f) => f.mints && f.mints.length > 0) .map((f) => ( glamClient.fetchMetadataAccount(new PublicKey(fundId)), + queryFn: () => + glamClient.fetchOpenfundsMetadataAccount(new PublicKey(fundId)), }); if (!metaAccount) return null; diff --git a/playground/src/app/(playground)/playground/products/[product]/page.tsx b/playground/src/app/(playground)/playground/products/[product]/page.tsx index 11e8885d..823f8d16 100644 --- a/playground/src/app/(playground)/playground/products/[product]/page.tsx +++ b/playground/src/app/(playground)/playground/products/[product]/page.tsx @@ -166,7 +166,7 @@ async function updateHoldersData( stateModel: StateModel, ): Promise { const holdersData = await Promise.all( - stateModel.mints.map(async (shareClassModel, i) => { + (stateModel.mints || []).map(async (shareClassModel, i) => { const mintAddress = stateModel.shareClassMints[i].toBase58(); const holderData = await fetchHolderData(mintAddress); if (!holderData) { @@ -617,11 +617,13 @@ export default function ProductPage() {

Symbol

- {stateModel.mints[0]?.symbol} + {(stateModel.mints || [])[0]?.symbol}

@@ -629,12 +631,13 @@ export default function ProductPage() {

Class Asset

- {stateModel.mints[0]?.rawOpenfunds?.shareClassCurrency} + {(stateModel.mints || [])[0]?.rawOpenfunds?.shareClassCurrency}

@@ -867,7 +870,7 @@ export default function ProductPage() {
{ - stateModel.mints[0]?.rawOpenfunds + (stateModel.mints || [])[0]?.rawOpenfunds ?.shareClassCurrency }
@@ -878,7 +881,7 @@ export default function ProductPage() {
{ - stateModel.mints[0]?.rawOpenfunds + (stateModel.mints || [])[0]?.rawOpenfunds ?.shareClassLaunchDate }
@@ -889,7 +892,7 @@ export default function ProductPage() {
{ - stateModel.mints[0]?.rawOpenfunds + (stateModel.mints || [])[0]?.rawOpenfunds ?.shareClassLifecycle }
@@ -900,7 +903,7 @@ export default function ProductPage() {
{ - stateModel.mints[0]?.rawOpenfunds + (stateModel.mints || [])[0]?.rawOpenfunds ?.investmentStatus }
@@ -931,7 +934,7 @@ export default function ProductPage() {
{ - stateModel.mints[0]?.rawOpenfunds + (stateModel.mints || [])[0]?.rawOpenfunds ?.shareClassDistributionPolicy }
@@ -1016,7 +1019,8 @@ export default function ProductPage() {
- Share Class 1 {stateModel?.mints[0]?.symbol} + Share Class 1{" "} + {(stateModel.mints || [])[0]?.symbol}
- (allGlamStates || []).map((f) => ({ - id: f.idStr, - sparkleKey: f.sparkleKey, - name: f.name || f.idStr || "", - symbol: f.mints[0]?.symbol || "-", - baseAsset: f.rawOpenfunds?.fundCurrency || "SOL", - inception: f.rawOpenfunds?.fundLaunchDate || "-", + (allGlamStates || []).map((s) => ({ + id: s.idStr, + sparkleKey: s.sparkleKey, + name: s.name || s.idStr || "", + symbol: (s.mints || [])[0]?.symbol || "-", + baseAsset: s.rawOpenfunds?.fundCurrency || "SOL", + inception: s.rawOpenfunds?.fundLaunchDate || "-", status: "active", - product: f.productType, + product: s.productType, })), [allGlamStates], ); diff --git a/playground/src/app/(vault)/vault/create/page.tsx b/playground/src/app/(vault)/vault/create/page.tsx index f10f2bf6..552a2779 100644 --- a/playground/src/app/(vault)/vault/create/page.tsx +++ b/playground/src/app/(vault)/vault/create/page.tsx @@ -65,8 +65,9 @@ export default function Create() { setIsLoading(true); try { const glamState = { + accountType: { vault: {} }, name: values.productName, - isEnabled: true, + enabled: true, assets: values.assets.map((address) => new PublicKey(address)), }; diff --git a/playground/src/app/(vault)/vault/policies/page.tsx b/playground/src/app/(vault)/vault/policies/page.tsx index 6424accf..3886bb5b 100644 --- a/playground/src/app/(vault)/vault/policies/page.tsx +++ b/playground/src/app/(vault)/vault/policies/page.tsx @@ -47,14 +47,17 @@ export default function VaultPoliciesPage() { if (state) { form.setValue( "assets", - state.assets.map((a) => a.toBase58()), + (state.assets || []).map((a) => a.toBase58()), ); } }, [state]); const handleReset = (event: React.MouseEvent) => { event.preventDefault(); - form.setValue("assets", state?.assets.map((a) => a.toBase58()) || []); + form.setValue( + "assets", + (state?.assets || []).map((a) => a.toBase58()), + ); }; const handleUpdateAssets = async (event: React.MouseEvent) => { @@ -62,10 +65,10 @@ export default function VaultPoliciesPage() { if (!state) { return; } - const vaultAssets = state.assets.map((a) => a.toBase58()).sort(); + const vaultAssets = (state.assets || []).map((a) => a.toBase58()).sort(); const formAssets = form.getValues().assets.sort(); if ( - state.assets.length === formAssets.length && + (state.assets || []).length === formAssets.length && vaultAssets.every((value, index) => value === formAssets[index]) ) { toast({ diff --git a/playground/src/app/(vault)/vault/transfer/page.tsx b/playground/src/app/(vault)/vault/transfer/page.tsx index 4aa8f14b..88ee5a6b 100644 --- a/playground/src/app/(vault)/vault/transfer/page.tsx +++ b/playground/src/app/(vault)/vault/transfer/page.tsx @@ -375,9 +375,7 @@ export default function Transfer() { {warning && ( <> - + )} diff --git a/playground/src/components/PageAccess.tsx b/playground/src/components/PageAccess.tsx index b5d72b9c..86e7f469 100644 --- a/playground/src/components/PageAccess.tsx +++ b/playground/src/components/PageAccess.tsx @@ -56,6 +56,7 @@ export default function PageAccess({ }, [state, getLabel]); const handleSuccess = useCallback(() => { + console.log("Delegate access updated successfully"); refresh(); }, []); diff --git a/playground/src/components/PageIntegrations.tsx b/playground/src/components/PageIntegrations.tsx index 3aa067a9..568edb57 100644 --- a/playground/src/components/PageIntegrations.tsx +++ b/playground/src/components/PageIntegrations.tsx @@ -13,7 +13,7 @@ import { toast } from "../components/ui/use-toast"; import { parseTxError } from "../lib/error"; import { ExplorerLink } from "../components/ExplorerLink"; import { allIntegrations, Integration } from "./integrations/data"; -import { useGlam, IntegrationName } from "@glam/anchor/react"; +import { useGlam, Integration as IntegrationType } from "@glam/anchor/react"; export default function PageIntegrations() { const { glamClient, allGlamStates, activeGlamState } = useGlam(); @@ -43,19 +43,16 @@ export default function PageIntegrations() { const updated = action === "disable" ? { - integrationAcls: stateModel!.integrationAcls.filter( + integrations: (stateModel?.integrations || []).filter( // @ts-ignore - (acl) => Object.keys(acl.name)[0] !== integration, + (integ) => Object.keys(integ)[0] !== integration, ), } : { - integrationAcls: [ - ...stateModel!.integrationAcls, - { - // @ts-ignore - name: { [integration]: {} } as IntegrationName, - features: [], - }, + integrations: [ + ...(stateModel?.integrations || []), + // @ts-ignore + { [integration]: {} } as IntegrationType, ], }; try { @@ -90,11 +87,10 @@ export default function PageIntegrations() { useEffect(() => { const enabled = - (allGlamStates || []) - .find((s) => s.idStr === activeGlamState?.address) - ?.integrationAcls.map((acl) => - Object.keys(acl.name)[0].toLowerCase(), - ) || []; + ( + (allGlamStates || []).find((s) => s.idStr === activeGlamState?.address) + ?.integrations || [] + ).map((integ) => Object.keys(integ)[0].toLowerCase()) || []; integrations.forEach((integ, index) => { if (enabled.includes(integ.name.toLowerCase())) { diff --git a/playground/src/components/access/data-table-toolbar.tsx b/playground/src/components/access/data-table-toolbar.tsx index 4b96aec6..22bde431 100644 --- a/playground/src/components/access/data-table-toolbar.tsx +++ b/playground/src/components/access/data-table-toolbar.tsx @@ -1,5 +1,6 @@ "use client"; +import { BN } from "@coral-xyz/anchor"; import { Cross2Icon, PlusIcon } from "@radix-ui/react-icons"; import { Table } from "@tanstack/react-table"; import { Button } from "@/components/ui/button"; @@ -92,11 +93,12 @@ export function DataTableToolbar({ } const delegateAcls = [ - { + new DelegateAcl({ pubkey, // @ts-ignore permissions: permissions.map((p) => ({ [p!]: {} })), - } as DelegateAcl, + expiresAt: new BN(0), + }), ]; try { diff --git a/playground/src/components/access/data-table.tsx b/playground/src/components/access/data-table.tsx index da4804de..0b03b80e 100644 --- a/playground/src/components/access/data-table.tsx +++ b/playground/src/components/access/data-table.tsx @@ -181,11 +181,11 @@ export function DataTable({ } const delegateAcls = [ - { + new DelegateAcl({ pubkey, //@ts-ignore permissions: permissions.map((p) => ({ [p!]: {} })), - } as DelegateAcl, + }), ]; try {