From 3a1473f585ee4476f23733e7d93bf6d1ade97c89 Mon Sep 17 00:00:00 2001 From: Andrew Nguyen Date: Mon, 1 Jul 2024 09:27:48 +0700 Subject: [PATCH] feat: claim and staked jup locked voter --- Cargo.lock | 45 +++ Cargo.toml | 1 + cli/Cargo.toml | 3 +- cli/src/bin/cli.rs | 24 ++ cli/src/bin/instructions/process_claim.rs | 14 + .../instructions/process_claim_from_api.rs | 15 + cli/src/bin/instructions/process_new_claim.rs | 15 + .../instructions/process_new_distributor.rs | 8 + programs/merkle-distributor/Cargo.toml | 4 + programs/merkle-distributor/src/error.rs | 2 + .../src/instructions/claim_locked.rs | 69 +++- .../src/instructions/new_claim.rs | 73 ++++- .../src/instructions/new_distributor.rs | 8 +- programs/merkle-distributor/src/lib.rs | 10 +- .../src/state/merkle_distributor.rs | 8 +- target/types/merkle_distributor.ts | 294 ++++++++++++++++-- 16 files changed, 520 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8984055..2565e51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1819,6 +1819,18 @@ dependencies = [ "scroll", ] +[[package]] +name = "govern" +version = "0.1.0" +source = "git+https://github.com/TeamRaccoons/WAGMI?rev=425ac5e3a51f15831c825874e531409d4dc12c71#425ac5e3a51f15831c825874e531409d4dc12c71" +dependencies = [ + "anchor-lang", + "anchor-spl", + "num-traits", + "smart-wallet", + "vipers", +] + [[package]] name = "h2" version = "0.3.22" @@ -2252,6 +2264,7 @@ dependencies = [ "clap 3.2.25", "csv", "jito-merkle-tree", + "locked-voter", "merkle-distributor", "rand 0.7.3", "reqwest", @@ -2381,6 +2394,18 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "locked-voter" +version = "0.2.0" +source = "git+https://github.com/TeamRaccoons/WAGMI?rev=425ac5e3a51f15831c825874e531409d4dc12c71#425ac5e3a51f15831c825874e531409d4dc12c71" +dependencies = [ + "anchor-lang", + "anchor-spl", + "govern", + "num-traits", + "vipers", +] + [[package]] name = "log" version = "0.4.20" @@ -2443,6 +2468,7 @@ dependencies = [ "anchor-spl", "bytemuck", "jito-merkle-verify", + "locked-voter", "solana-program", "solana-security-txt", ] @@ -3886,6 +3912,15 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +[[package]] +name = "smart-wallet" +version = "0.1.0" +source = "git+https://github.com/TeamRaccoons/WAGMI?rev=425ac5e3a51f15831c825874e531409d4dc12c71#425ac5e3a51f15831c825874e531409d4dc12c71" +dependencies = [ + "anchor-lang", + "vipers", +] + [[package]] name = "socket2" version = "0.4.10" @@ -5602,6 +5637,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vipers" +version = "0.1.0" +source = "git+https://github.com/TeamRaccoons/WAGMI?rev=425ac5e3a51f15831c825874e531409d4dc12c71#425ac5e3a51f15831c825874e531409d4dc12c71" +dependencies = [ + "anchor-lang", + "anchor-spl", + "num-traits", +] + [[package]] name = "void" version = "1.0.2" diff --git a/Cargo.toml b/Cargo.toml index c641e56..05ef032 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,3 +60,4 @@ tracing = { version = "0.1.37" } tracing-core = "0.1.32" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } rust_decimal = "1.35.0" +locked-voter = { git = "https://github.com/TeamRaccoons/WAGMI", rev = "425ac5e3a51f15831c825874e531409d4dc12c71" } \ No newline at end of file diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 081fd24..bf60af7 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -22,4 +22,5 @@ serde = { workspace = true } serde_json = { workspace = true } zip = {version = "0.6.0", features = ["deflate"] } rand = "0.7.3" -reqwest = { version = "0.11", features = ["blocking"] } \ No newline at end of file +reqwest = { version = "0.11", features = ["blocking"] } +locked-voter = { workspace = true } \ No newline at end of file diff --git a/cli/src/bin/cli.rs b/cli/src/bin/cli.rs index 13eedad..2314b63 100644 --- a/cli/src/bin/cli.rs +++ b/cli/src/bin/cli.rs @@ -240,6 +240,12 @@ pub struct NewDistributorArgs { /// Clawback receiver owner #[clap(long, env)] pub clawback_receiver_owner: Pubkey, + + #[clap(long, env)] + pub locker: Pubkey, + + #[clap(long, env)] + pub min_stake_duration: u64, } // NewDistributor subcommand args @@ -281,6 +287,12 @@ pub struct NewDistributorWithBonusArgs { #[clap(long, env)] pub clawback_receiver_owner: Pubkey, + #[clap(long, env)] + pub locker: Pubkey, + + #[clap(long, env)] + pub min_stake_duration: u64, + #[clap(long, env)] pub bonus_vesting_duration: u64, @@ -301,6 +313,8 @@ impl NewDistributorWithBonusArgs { skip_verify: self.skip_verify, base_path: self.base_path.clone(), clawback_receiver_owner: self.clawback_receiver_owner, + locker: self.locker, + min_stake_duration: self.min_stake_duration, } } } @@ -620,6 +634,8 @@ fn check_distributor_onchain_matches( new_distributor_args: &NewDistributorArgs, total_bonus: u64, bonus_vesting_duration: u64, + locker: Pubkey, + min_locked_duration: u64, pubkey: Pubkey, base: Pubkey, args: &Args, @@ -671,6 +687,14 @@ fn check_distributor_onchain_matches( return Err("bonus_vesting_duration mismatch"); } + if distributor.locker != locker { + return Err("locker mismatch"); + } + + if distributor.min_locked_duration != min_locked_duration { + return Err("min_locked_duration mismatch"); + } + // TODO fix code let clawback_receiver_token_account = spl_associated_token_account::get_associated_token_address( diff --git a/cli/src/bin/instructions/process_claim.rs b/cli/src/bin/instructions/process_claim.rs index 5ed6351..bfc3d98 100644 --- a/cli/src/bin/instructions/process_claim.rs +++ b/cli/src/bin/instructions/process_claim.rs @@ -16,6 +16,8 @@ pub fn process_claim(args: &Args, claim_args: &ClaimArgs) { &args.mint, merkle_tree.airdrop_version, ); + let program_client = args.get_program_client(); + let distributor_state: MerkleDistributor = program_client.account(distributor).unwrap(); println!("distributor pubkey {}", distributor); let (claim_status_pda, _bump) = get_claim_status_pda(&args.program_id, &claimant, &distributor); @@ -46,6 +48,14 @@ pub fn process_claim(args: &Args, claim_args: &ClaimArgs) { let claimant_ata = get_associated_token_address(&claimant, &args.mint); + let (escrow, _bump) = Pubkey::find_program_address( + &[ + b"Escrow".as_ref(), + distributor_state.locker.as_ref(), + claimant.key().as_ref(), + ], + &locked_voter::ID, + ); ixs.push(Instruction { program_id: args.program_id, accounts: merkle_distributor::accounts::ClaimLocked { @@ -55,6 +65,10 @@ pub fn process_claim(args: &Args, claim_args: &ClaimArgs) { to: claimant_ata, claimant, token_program: token::ID, + voter_program: locked_voter::ID, + locker: distributor_state.locker, + escrow, + escrow_tokens: get_associated_token_address(&escrow, &args.mint), } .to_account_metas(None), data: merkle_distributor::instruction::ClaimLocked {}.data(), diff --git a/cli/src/bin/instructions/process_claim_from_api.rs b/cli/src/bin/instructions/process_claim_from_api.rs index 088496e..62b2215 100644 --- a/cli/src/bin/instructions/process_claim_from_api.rs +++ b/cli/src/bin/instructions/process_claim_from_api.rs @@ -20,6 +20,9 @@ pub fn process_claim_from_api(args: &Args, claim_args: &ClaimFromApiArgs) { let distributor = Pubkey::from_str(&kv_proof.merkle_tree).unwrap(); + let program_client = args.get_program_client(); + let distributor_state: MerkleDistributor = program_client.account(distributor).unwrap(); + let (claim_status_pda, _bump) = get_claim_status_pda(&args.program_id, &claimant, &distributor); let client = RpcClient::new_with_commitment(&args.rpc_url, CommitmentConfig::confirmed()); @@ -45,6 +48,14 @@ pub fn process_claim_from_api(args: &Args, claim_args: &ClaimFromApiArgs) { ); } + let (escrow, _bump) = Pubkey::find_program_address( + &[ + b"Escrow".as_ref(), + distributor_state.locker.as_ref(), + claimant.key().as_ref(), + ], + &locked_voter::ID, + ); ixs.push(Instruction { program_id: args.program_id, accounts: merkle_distributor::accounts::NewClaim { @@ -55,6 +66,10 @@ pub fn process_claim_from_api(args: &Args, claim_args: &ClaimFromApiArgs) { claimant, token_program: token::ID, system_program: solana_program::system_program::ID, + voter_program: locked_voter::ID, + locker: distributor_state.locker, + escrow, + escrow_tokens: get_associated_token_address(&escrow, &args.mint), } .to_account_metas(None), data: merkle_distributor::instruction::NewClaim { diff --git a/cli/src/bin/instructions/process_new_claim.rs b/cli/src/bin/instructions/process_new_claim.rs index 483aff1..ab26411 100644 --- a/cli/src/bin/instructions/process_new_claim.rs +++ b/cli/src/bin/instructions/process_new_claim.rs @@ -5,6 +5,7 @@ use crate::*; pub fn process_new_claim(args: &Args, claim_args: &ClaimArgs) { let keypair = read_keypair_file(&args.keypair_path.clone().unwrap()) .expect("Failed reading keypair file"); + let program_client = args.get_program_client(); let claimant = keypair.pubkey(); println!("Claiming tokens for user {}...", claimant); @@ -18,6 +19,8 @@ pub fn process_new_claim(args: &Args, claim_args: &ClaimArgs) { merkle_tree.airdrop_version, ); + let distributor_state: MerkleDistributor = program_client.account(distributor).unwrap(); + // Get user's node in claim let node = merkle_tree.get_node(&claimant); @@ -54,6 +57,14 @@ pub fn process_new_claim(args: &Args, claim_args: &ClaimArgs) { } } + let (escrow, _bump) = Pubkey::find_program_address( + &[ + b"Escrow".as_ref(), + distributor_state.locker.as_ref(), + claimant.key().as_ref(), + ], + &locked_voter::ID, + ); ixs.push(Instruction { program_id: args.program_id, accounts: merkle_distributor::accounts::NewClaim { @@ -64,6 +75,10 @@ pub fn process_new_claim(args: &Args, claim_args: &ClaimArgs) { claimant, token_program: token::ID, system_program: solana_program::system_program::ID, + voter_program: locked_voter::ID, + locker: distributor_state.locker, + escrow, + escrow_tokens: get_associated_token_address(&escrow, &args.mint), } .to_account_metas(None), data: merkle_distributor::instruction::NewClaim { diff --git a/cli/src/bin/instructions/process_new_distributor.rs b/cli/src/bin/instructions/process_new_distributor.rs index 07dc6e0..9e45215 100644 --- a/cli/src/bin/instructions/process_new_distributor.rs +++ b/cli/src/bin/instructions/process_new_distributor.rs @@ -119,6 +119,8 @@ fn create_new_distributor( new_distributor_args, total_bonus, bonus_vesting_duration, + new_distributor_args.locker, + new_distributor_args.min_stake_duration, keypair.pubkey(), base.pubkey(), &args, @@ -190,6 +192,8 @@ fn create_new_distributor( clawback_start_ts: new_distributor_args.clawback_start_ts, enable_slot: new_distributor_args.enable_slot, closable: new_distributor_args.closable, + locker: new_distributor_args.locker, + min_locked_duration: new_distributor_args.min_stake_duration, } .data(), }); @@ -220,6 +224,8 @@ fn create_new_distributor( closable: new_distributor_args.closable, total_bonus, bonus_vesting_slot_duration: bonus_vesting_duration, + locker: new_distributor_args.locker, + min_locked_duration: new_distributor_args.min_stake_duration, } .data(), }); @@ -277,6 +283,8 @@ fn create_new_distributor( new_distributor_args, total_bonus, bonus_vesting_duration, + new_distributor_args.locker, + new_distributor_args.min_stake_duration, keypair.pubkey(), base.pubkey(), args, diff --git a/programs/merkle-distributor/Cargo.toml b/programs/merkle-distributor/Cargo.toml index 0d483a4..6ae4eba 100644 --- a/programs/merkle-distributor/Cargo.toml +++ b/programs/merkle-distributor/Cargo.toml @@ -23,3 +23,7 @@ bytemuck = "1.14.0" jito-merkle-verify = { path = "../../verify" } solana-program = "1.16.16" solana-security-txt = "1.1.1" +locked-voter = { git = "https://github.com/TeamRaccoons/WAGMI", features = [ + "cpi", + "no-entrypoint", +], rev = "425ac5e3a51f15831c825874e531409d4dc12c71" } diff --git a/programs/merkle-distributor/src/error.rs b/programs/merkle-distributor/src/error.rs index b02e889..7406ea9 100644 --- a/programs/merkle-distributor/src/error.rs +++ b/programs/merkle-distributor/src/error.rs @@ -45,4 +45,6 @@ pub enum ErrorCode { CannotCloseDistributor, #[msg("Cannot close claim status")] CannotCloseClaimStatus, + #[msg("Remaning locked duration is too small")] + RemaningLockedDurationIsTooSmall, } diff --git a/programs/merkle-distributor/src/instructions/claim_locked.rs b/programs/merkle-distributor/src/instructions/claim_locked.rs index 2cc11ff..7ff3b53 100644 --- a/programs/merkle-distributor/src/instructions/claim_locked.rs +++ b/programs/merkle-distributor/src/instructions/claim_locked.rs @@ -5,7 +5,7 @@ use anchor_lang::{ prelude::*, Accounts, Result, ToAccountInfo, }; -use anchor_spl::token::{self, Token, TokenAccount}; +use anchor_spl::token::{Token, TokenAccount}; use crate::{ error::ErrorCode, @@ -14,12 +14,14 @@ use crate::{ merkle_distributor::MerkleDistributor, }, }; +use locked_voter::{self as voter, Escrow}; +use locked_voter::{program::LockedVoter as Voter, Locker}; /// [merkle_distributor::claim_locked] accounts. #[derive(Accounts)] pub struct ClaimLocked<'info> { /// The [MerkleDistributor]. - #[account(mut)] + #[account(mut, has_one = locker)] pub distributor: Account<'info, MerkleDistributor>, /// Claim Status PDA @@ -54,6 +56,29 @@ pub struct ClaimLocked<'info> { /// SPL [Token] program. pub token_program: Program<'info, Token>, + + /// Voter program + pub voter_program: Program<'info, Voter>, + + /// CHECK: Locker + #[account(mut)] + pub locker: Box>, + + /// CHECK: escrow + #[account(mut, + seeds = [ + b"Escrow".as_ref(), + locker.key().as_ref(), + claimant.key().as_ref() + ], + seeds::program = voter_program.key(), + bump + )] + pub escrow: Box>, + + /// CHECK: escrow_tokens + #[account(mut)] + pub escrow_tokens: UncheckedAccount<'info>, } /// Claim locked tokens as they become unlocked. @@ -70,6 +95,16 @@ pub fn handle_claim_locked(ctx: Context) -> Result<()> { let curr_ts = Clock::get()?.unix_timestamp; let curr_slot = Clock::get()?.slot; + let escrow = &ctx.accounts.escrow; + let locker = &ctx.accounts.locker; + let remaing_locked_duration = escrow + .get_remaining_duration_until_expiration(curr_ts, locker) + .unwrap(); + require!( + remaing_locked_duration >= distributor.min_locked_duration, + ErrorCode::RemaningLockedDurationIsTooSmall + ); + require!(!distributor.clawed_back, ErrorCode::ClaimExpired); require!( @@ -89,19 +124,23 @@ pub fn handle_claim_locked(ctx: Context) -> Result<()> { &distributor.version.to_le_bytes(), &[ctx.accounts.distributor.bump], ]; - - token::transfer( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - token::Transfer { - from: ctx.accounts.from.to_account_info(), - to: ctx.accounts.to.to_account_info(), - authority: ctx.accounts.distributor.to_account_info(), - }, - ) - .with_signer(&[&seeds[..]]), - amount, - )?; + let seeds = &[&seeds[..]]; + + // CPI to voter + let cpi_ctx = CpiContext::new( + ctx.accounts.voter_program.to_account_info(), + voter::cpi::accounts::IncreaseLockedAmount { + locker: ctx.accounts.locker.to_account_info(), + escrow: ctx.accounts.escrow.to_account_info(), + escrow_tokens: ctx.accounts.escrow_tokens.to_account_info(), + payer: ctx.accounts.distributor.to_account_info(), + source_tokens: ctx.accounts.from.to_account_info(), + token_program: ctx.accounts.token_program.to_account_info(), + }, + ) + .with_signer(seeds); + + voter::cpi::increase_locked_amount(cpi_ctx, amount)?; claim_status.locked_amount_withdrawn = claim_status .locked_amount_withdrawn diff --git a/programs/merkle-distributor/src/instructions/new_claim.rs b/programs/merkle-distributor/src/instructions/new_claim.rs index 678d571..d75eb49 100644 --- a/programs/merkle-distributor/src/instructions/new_claim.rs +++ b/programs/merkle-distributor/src/instructions/new_claim.rs @@ -2,10 +2,7 @@ use anchor_lang::{ context::Context, prelude::*, solana_program::hash::hashv, system_program::System, Accounts, Key, Result, }; -use anchor_spl::{ - token, - token::{Token, TokenAccount}, -}; +use anchor_spl::token::{Token, TokenAccount}; use jito_merkle_verify::verify; use crate::{ @@ -17,6 +14,9 @@ use crate::{ }, }; +use locked_voter::program::LockedVoter as Voter; +use locked_voter::{self as voter, Escrow, Locker}; + // We need to discern between leaf and intermediate nodes to prevent trivial second // pre-image attacks. // https://flawed.net.nz/2018/02/21/attacking-merkle-trees-with-a-second-preimage-attack @@ -26,7 +26,7 @@ const LEAF_PREFIX: &[u8] = &[0]; #[derive(Accounts)] pub struct NewClaim<'info> { /// The [MerkleDistributor]. - #[account(mut)] + #[account(mut, has_one = locker)] pub distributor: Account<'info, MerkleDistributor>, /// Claim status PDA @@ -69,6 +69,29 @@ pub struct NewClaim<'info> { /// The [System] program. pub system_program: Program<'info, System>, + + /// Voter program + pub voter_program: Program<'info, Voter>, + + /// CHECK: Locker + #[account(mut)] + pub locker: Box>, + + /// CHECK: escrow + #[account(mut, + seeds = [ + b"Escrow".as_ref(), + locker.key().as_ref(), + claimant.key().as_ref() + ], + seeds::program = voter_program.key(), + bump + )] + pub escrow: Box>, + + /// CHECK: escrow_tokens + #[account(mut)] + pub escrow_tokens: UncheckedAccount<'info>, } /// Initializes a new claim from the [MerkleDistributor]. @@ -93,6 +116,16 @@ pub fn handle_new_claim( let curr_ts = Clock::get()?.unix_timestamp; let curr_slot = Clock::get()?.slot; + let escrow = &ctx.accounts.escrow; + let locker = &ctx.accounts.locker; + let remaing_locked_duration = escrow + .get_remaining_duration_until_expiration(curr_ts, locker) + .unwrap(); + require!( + remaing_locked_duration >= distributor.min_locked_duration, + ErrorCode::RemaningLockedDurationIsTooSmall + ); + require!(!distributor.clawed_back, ErrorCode::ClaimExpired); require!( distributor.enable_slot <= curr_slot, @@ -143,21 +176,27 @@ pub fn handle_new_claim( &distributor.version.to_le_bytes(), &[ctx.accounts.distributor.bump], ]; + let seeds = &[&seeds[..]]; let bonus = distributor.get_bonus_for_a_claimaint(claim_status.unlocked_amount, curr_slot)?; let amount_with_bonus = claim_status.unlocked_amount.safe_add(bonus)?; - token::transfer( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - token::Transfer { - from: ctx.accounts.from.to_account_info(), - to: ctx.accounts.to.to_account_info(), - authority: ctx.accounts.distributor.to_account_info(), - }, - ) - .with_signer(&[&seeds[..]]), - amount_with_bonus, - )?; + + // CPI to voter + let cpi_ctx = CpiContext::new( + ctx.accounts.voter_program.to_account_info(), + voter::cpi::accounts::IncreaseLockedAmount { + locker: ctx.accounts.locker.to_account_info(), + escrow: ctx.accounts.escrow.to_account_info(), + escrow_tokens: ctx.accounts.escrow_tokens.to_account_info(), + payer: ctx.accounts.distributor.to_account_info(), + source_tokens: ctx.accounts.from.to_account_info(), + token_program: ctx.accounts.token_program.to_account_info(), + }, + ) + .with_signer(seeds); + + voter::cpi::increase_locked_amount(cpi_ctx, amount_with_bonus)?; + let distributor = &mut ctx.accounts.distributor; distributor.total_amount_claimed = distributor .total_amount_claimed diff --git a/programs/merkle-distributor/src/instructions/new_distributor.rs b/programs/merkle-distributor/src/instructions/new_distributor.rs index a0541a7..3de46c3 100644 --- a/programs/merkle-distributor/src/instructions/new_distributor.rs +++ b/programs/merkle-distributor/src/instructions/new_distributor.rs @@ -88,6 +88,8 @@ pub fn handle_new_distributor( closable: bool, total_bonus: u64, bonus_vesting_slot_duration: u64, + locker: Pubkey, + min_locked_duration: u64, ) -> Result<()> { let curr_ts = Clock::get()?.unix_timestamp; @@ -140,10 +142,12 @@ pub fn handle_new_distributor( vesting_slot_duration: bonus_vesting_slot_duration, total_claimed_bonus: 0, }; + distributor.locker = locker; + distributor.min_locked_duration = min_locked_duration; // Note: might get truncated, do not rely on msg! { - "New distributor created with version = {}, mint={}, vault={} max_total_claim={}, max_nodes: {}, start_ts: {}, end_ts: {}, clawback_start: {}, clawback_receiver: {} enable_slot {} total_bonus {}, bonus_vesting_slot_duration {}", + "New distributor created with version = {}, mint={}, vault={} max_total_claim={}, max_nodes: {}, start_ts: {}, end_ts: {}, clawback_start: {}, clawback_receiver: {} enable_slot {} total_bonus {}, bonus_vesting_slot_duration {}, locker {} min_locked_duration {}", distributor.version, distributor.mint, ctx.accounts.token_vault.key(), @@ -156,6 +160,8 @@ pub fn handle_new_distributor( distributor.enable_slot, distributor.airdrop_bonus.total_bonus, distributor.airdrop_bonus.vesting_slot_duration, + distributor.locker, + distributor.min_locked_duration, }; Ok(()) diff --git a/programs/merkle-distributor/src/lib.rs b/programs/merkle-distributor/src/lib.rs index b389bdf..3799e14 100644 --- a/programs/merkle-distributor/src/lib.rs +++ b/programs/merkle-distributor/src/lib.rs @@ -20,7 +20,7 @@ pub mod math; pub mod state; use crate::error::ErrorCode::ArithmeticError; use solana_security_txt::security_txt; -declare_id!("DiSLRwcSFvtwvMWSs7ubBMvYRaYNYupa76ZSuYLe6D7j"); +declare_id!("Dis2TfkFnXFkrtvAktEkw37sdb7qwJgY6H7YZJwk51wK"); security_txt! { // Required fields @@ -67,6 +67,8 @@ pub mod merkle_distributor { clawback_start_ts: i64, enable_slot: u64, closable: bool, + locker: Pubkey, + min_locked_duration: u64, ) -> Result<()> { handle_new_distributor( ctx, @@ -81,6 +83,8 @@ pub mod merkle_distributor { closable, 0, 0, + locker, + min_locked_duration, ) } @@ -98,6 +102,8 @@ pub mod merkle_distributor { closable: bool, total_bonus: u64, bonus_vesting_slot_duration: u64, + locker: Pubkey, + min_locked_duration: u64, ) -> Result<()> { let max_total_claim = total_claim .checked_add(total_bonus) @@ -115,6 +121,8 @@ pub mod merkle_distributor { closable, total_bonus, bonus_vesting_slot_duration, + locker, + min_locked_duration, ) } /// only available in test phase diff --git a/programs/merkle-distributor/src/state/merkle_distributor.rs b/programs/merkle-distributor/src/state/merkle_distributor.rs index 34caed9..e6bf4bb 100644 --- a/programs/merkle-distributor/src/state/merkle_distributor.rs +++ b/programs/merkle-distributor/src/state/merkle_distributor.rs @@ -45,10 +45,10 @@ pub struct MerkleDistributor { pub closable: bool, /// bonus multiplier pub airdrop_bonus: AirdropBonus, - /// Buffer 0 - pub buffer_0: [u8; 8], - /// Buffer 1 - pub buffer_1: [u8; 32], + /// min_locked_duration + pub min_locked_duration: u64, + /// locker + pub locker: Pubkey, /// Buffer 2 pub buffer_2: [u8; 32], } diff --git a/target/types/merkle_distributor.ts b/target/types/merkle_distributor.ts index b786ae7..032a63f 100644 --- a/target/types/merkle_distributor.ts +++ b/target/types/merkle_distributor.ts @@ -1,5 +1,5 @@ export type MerkleDistributor = { - "version": "0.0.1", + "version": "0.0.2", "name": "merkle_distributor", "instructions": [ { @@ -166,6 +166,14 @@ export type MerkleDistributor = { { "name": "closable", "type": "bool" + }, + { + "name": "locker", + "type": "publicKey" + }, + { + "name": "minLockedDuration", + "type": "u64" } ] }, @@ -321,6 +329,14 @@ export type MerkleDistributor = { { "name": "bonusVestingSlotDuration", "type": "u64" + }, + { + "name": "locker", + "type": "publicKey" + }, + { + "name": "minLockedDuration", + "type": "u64" } ] }, @@ -445,6 +461,9 @@ export type MerkleDistributor = { "isSigner": false, "docs": [ "The [MerkleDistributor]." + ], + "relations": [ + "locker" ] }, { @@ -514,6 +533,54 @@ export type MerkleDistributor = { "docs": [ "The [System] program." ] + }, + { + "name": "voterProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Voter program" + ] + }, + { + "name": "locker", + "isMut": true, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "Escrow" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Locker", + "path": "locker" + }, + { + "kind": "account", + "type": "publicKey", + "path": "claimant" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "voter_program" + } + } + }, + { + "name": "escrowTokens", + "isMut": true, + "isSigner": false } ], "args": [ @@ -547,6 +614,9 @@ export type MerkleDistributor = { "isSigner": false, "docs": [ "The [MerkleDistributor]." + ], + "relations": [ + "locker" ] }, { @@ -609,6 +679,54 @@ export type MerkleDistributor = { "docs": [ "SPL [Token] program." ] + }, + { + "name": "voterProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Voter program" + ] + }, + { + "name": "locker", + "isMut": true, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "Escrow" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Locker", + "path": "locker" + }, + { + "kind": "account", + "type": "publicKey", + "path": "claimant" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "voter_program" + } + } + }, + { + "name": "escrowTokens", + "isMut": true, + "isSigner": false } ], "args": [] @@ -932,28 +1050,18 @@ export type MerkleDistributor = { } }, { - "name": "buffer0", + "name": "minLockedDuration", "docs": [ - "Buffer 0" + "min_locked_duration" ], - "type": { - "array": [ - "u8", - 8 - ] - } + "type": "u64" }, { - "name": "buffer1", + "name": "locker", "docs": [ - "Buffer 1" + "locker" ], - "type": { - "array": [ - "u8", - 32 - ] - } + "type": "publicKey" }, { "name": "buffer2", @@ -1136,12 +1244,17 @@ export type MerkleDistributor = { "code": 6020, "name": "CannotCloseClaimStatus", "msg": "Cannot close claim status" + }, + { + "code": 6021, + "name": "RemaningLockedDurationIsTooSmall", + "msg": "Remaning locked duration is too small" } ] }; export const IDL: MerkleDistributor = { - "version": "0.0.1", + "version": "0.0.2", "name": "merkle_distributor", "instructions": [ { @@ -1308,6 +1421,14 @@ export const IDL: MerkleDistributor = { { "name": "closable", "type": "bool" + }, + { + "name": "locker", + "type": "publicKey" + }, + { + "name": "minLockedDuration", + "type": "u64" } ] }, @@ -1463,6 +1584,14 @@ export const IDL: MerkleDistributor = { { "name": "bonusVestingSlotDuration", "type": "u64" + }, + { + "name": "locker", + "type": "publicKey" + }, + { + "name": "minLockedDuration", + "type": "u64" } ] }, @@ -1587,6 +1716,9 @@ export const IDL: MerkleDistributor = { "isSigner": false, "docs": [ "The [MerkleDistributor]." + ], + "relations": [ + "locker" ] }, { @@ -1656,6 +1788,54 @@ export const IDL: MerkleDistributor = { "docs": [ "The [System] program." ] + }, + { + "name": "voterProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Voter program" + ] + }, + { + "name": "locker", + "isMut": true, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "Escrow" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Locker", + "path": "locker" + }, + { + "kind": "account", + "type": "publicKey", + "path": "claimant" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "voter_program" + } + } + }, + { + "name": "escrowTokens", + "isMut": true, + "isSigner": false } ], "args": [ @@ -1689,6 +1869,9 @@ export const IDL: MerkleDistributor = { "isSigner": false, "docs": [ "The [MerkleDistributor]." + ], + "relations": [ + "locker" ] }, { @@ -1751,6 +1934,54 @@ export const IDL: MerkleDistributor = { "docs": [ "SPL [Token] program." ] + }, + { + "name": "voterProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Voter program" + ] + }, + { + "name": "locker", + "isMut": true, + "isSigner": false + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "Escrow" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Locker", + "path": "locker" + }, + { + "kind": "account", + "type": "publicKey", + "path": "claimant" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "voter_program" + } + } + }, + { + "name": "escrowTokens", + "isMut": true, + "isSigner": false } ], "args": [] @@ -2074,28 +2305,18 @@ export const IDL: MerkleDistributor = { } }, { - "name": "buffer0", + "name": "minLockedDuration", "docs": [ - "Buffer 0" + "min_locked_duration" ], - "type": { - "array": [ - "u8", - 8 - ] - } + "type": "u64" }, { - "name": "buffer1", + "name": "locker", "docs": [ - "Buffer 1" + "locker" ], - "type": { - "array": [ - "u8", - 32 - ] - } + "type": "publicKey" }, { "name": "buffer2", @@ -2278,6 +2499,11 @@ export const IDL: MerkleDistributor = { "code": 6020, "name": "CannotCloseClaimStatus", "msg": "Cannot close claim status" + }, + { + "code": 6021, + "name": "RemaningLockedDurationIsTooSmall", + "msg": "Remaning locked duration is too small" } ] };