Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anchor_lang::prelude::*;
use anchor_spl::{
associated_token::AssociatedToken,
token::Token,
token::{close_account, CloseAccount, Token},
token_interface::{TokenAccount, TokenInterface},
};

Expand Down Expand Up @@ -52,6 +52,7 @@ pub struct ClaimPositionFee<'info> {
/// CHECK:
pub token_b_mint: UncheckedAccount<'info>,

/// WSOL account - can be owned by treasury or vault_authority (we'll use as temp account)
#[account(
init_if_needed,
payer = payer,
Expand All @@ -68,6 +69,7 @@ pub struct ClaimPositionFee<'info> {
)]
pub wewe_token_account: Box<InterfaceAccount<'info, TokenAccount>>,

/// WSOL account - can be owned by maker or vault_authority (we'll use as temp account)
#[account(
init_if_needed,
payer = payer,
Expand Down Expand Up @@ -118,6 +120,44 @@ pub struct ClaimPositionFee<'info> {
/// CHECK:
pub position_nft_account: UncheckedAccount<'info>,

/// CHECK: Temporary WSOL account for treasury unwrapping (PDA derived from token program)
/// PDA: [b"temp_wsol", vault_authority, proposal, b"treasury"]
#[account(
mut,
constraint = {
let (expected_pda, _) = Pubkey::find_program_address(
&[
b"temp_wsol",
vault_authority.key().as_ref(),
proposal.key().as_ref(),
b"treasury",
],
&token_b_program.key(),
);
treasury_temp_wsol.key() == expected_pda
} @ ProposalError::IncorrectAccount
)]
pub treasury_temp_wsol: UncheckedAccount<'info>,

/// CHECK: Temporary WSOL account for maker unwrapping (PDA derived from token program)
/// PDA: [b"temp_wsol", vault_authority, proposal, b"maker"]
#[account(
mut,
constraint = {
let (expected_pda, _) = Pubkey::find_program_address(
&[
b"temp_wsol",
vault_authority.key().as_ref(),
proposal.key().as_ref(),
b"maker",
],
&token_b_program.key(),
);
maker_temp_wsol.key() == expected_pda
} @ ProposalError::IncorrectAccount
)]
pub maker_temp_wsol: UncheckedAccount<'info>,

pub token_a_program: Interface<'info, TokenInterface>,

pub token_b_program: Interface<'info, TokenInterface>,
Expand Down Expand Up @@ -217,34 +257,91 @@ impl<'info> ClaimPositionFee<'info> {
)?;
}

// Unwrap WSOL (token_b) to SOL before transferring
// We use PDA-derived temporary accounts (validated at account struct level)
if treasury_b > 0 {
// Transfer WSOL to temporary PDA account
anchor_spl::token::transfer(
CpiContext::new_with_signer(
self.token_b_program.to_account_info(),
anchor_spl::token::Transfer {
from: self.token_b_account.to_account_info(),
to: self.wewe_wsol_account.to_account_info(),
to: self.treasury_temp_wsol.to_account_info(),
authority: self.vault_authority.to_account_info(),
},
&[&vault_authority_seeds[..]],
),
treasury_b,
)?;

// Close the temporary WSOL account to unwrap it to SOL
// The SOL (lamports) will be sent to the account owner (vault_authority)
close_account(
CpiContext::new_with_signer(
self.token_b_program.to_account_info(),
CloseAccount {
account: self.treasury_temp_wsol.to_account_info(),
destination: self.vault_authority.to_account_info(),
authority: self.vault_authority.to_account_info(),
},
&[&vault_authority_seeds[..]],
),
)?;

// Transfer SOL from vault_authority to treasury
anchor_lang::system_program::transfer(
CpiContext::new_with_signer(
self.system_program.to_account_info(),
anchor_lang::system_program::Transfer {
from: self.vault_authority.to_account_info(),
to: self.wewe_treasury.to_account_info(),
},
&[&vault_authority_seeds[..]],
),
treasury_b,
)?;
}

if maker_b > 0 {
// Transfer WSOL to temporary PDA account
anchor_spl::token::transfer(
CpiContext::new_with_signer(
self.token_b_program.to_account_info(),
anchor_spl::token::Transfer {
from: self.token_b_account.to_account_info(),
to: self.maker_wsol_account.to_account_info(),
to: self.maker_temp_wsol.to_account_info(),
authority: self.vault_authority.to_account_info(),
},
&[&vault_authority_seeds[..]],
),
maker_b,
)?;

// Close the temporary WSOL account to unwrap it to SOL
close_account(
CpiContext::new_with_signer(
self.token_b_program.to_account_info(),
CloseAccount {
account: self.maker_temp_wsol.to_account_info(),
destination: self.vault_authority.to_account_info(),
authority: self.vault_authority.to_account_info(),
},
&[&vault_authority_seeds[..]],
),
)?;

// Transfer SOL from vault_authority to maker
anchor_lang::system_program::transfer(
CpiContext::new_with_signer(
self.system_program.to_account_info(),
anchor_lang::system_program::Transfer {
from: self.vault_authority.to_account_info(),
to: self.maker.to_account_info(),
},
&[&vault_authority_seeds[..]],
),
maker_b,
)?;
}

emit!(PositionFeeClaimed {
Expand Down
20 changes: 19 additions & 1 deletion tests/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// utils/helpers.ts
import * as anchor from '@coral-xyz/anchor';
import Decimal from "decimal.js";
import { getAssociatedTokenAddressSync } from '@solana/spl-token';
import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from '@solana/spl-token';
import BN from "bn.js";

export const WSOL_MINT = new anchor.web3.PublicKey("So11111111111111111111111111111111111111112");
Expand Down Expand Up @@ -119,6 +119,24 @@ export function findMetadataPDA(mint: anchor.web3.PublicKey): anchor.web3.Public
return metadataPDA;
}

export function findTempWsolPDA(
vaultAuthority: anchor.web3.PublicKey,
proposal: anchor.web3.PublicKey,
isTreasury: boolean
): anchor.web3.PublicKey {
const [tempWsolPDA] = anchor.web3.PublicKey.findProgramAddressSync(
[
Buffer.from("temp_wsol"),
vaultAuthority.toBuffer(),
proposal.toBuffer(),
Buffer.from(isTreasury ? "treasury" : "maker"),
],
TOKEN_PROGRAM_ID
);

return tempWsolPDA;
}

export const derivePoolPDAs = (
programId: anchor.web3.PublicKey,
cpAmmProgramId: anchor.web3.PublicKey,
Expand Down
9 changes: 9 additions & 0 deletions tests/wewe-token-launch-pad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
findConfigPDA,
findBackerProposalCountPDA,
findMetadataPDA,
findTempWsolPDA,
} from './utils';

const TOKEN_METADATA_PROGRAM_ID = new anchor.web3.PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
Expand Down Expand Up @@ -1494,6 +1495,8 @@ describe('Wewe Token Launch Pad - Integration Tests', () => {
const weweTokenAccount = getAssociatedTokenAddressSync(mint.publicKey, weweTreasury, true);
const makerWsolAccount = getAssociatedTokenAddressSync(WSOL_MINT, maker.publicKey, true);
const [wsolVault] = getTokenVaultAddress(vaultAuthority, WSOL_MINT, program.programId);
const treasuryTempWsol = findTempWsolPDA(vaultAuthority, proposal, true);
const makerTempWsol = findTempWsolPDA(vaultAuthority, proposal, false);
const computeUnitsIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 });

const tx = await program.methods
Expand All @@ -1518,6 +1521,8 @@ describe('Wewe Token Launch Pad - Integration Tests', () => {
tokenAMint: mint.publicKey,
tokenBMint: WSOL_MINT,
positionNftAccount: pdas.positionNftAccount,
treasuryTempWsol,
makerTempWsol,
tokenAProgram: TOKEN_PROGRAM_ID,
tokenBProgram: TOKEN_PROGRAM_ID,
ammProgram: cpAmm.programId,
Expand Down Expand Up @@ -2481,6 +2486,8 @@ describe('Wewe Token Launch Pad - Integration Tests', () => {
const weweTokenAccount = getAssociatedTokenAddressSync(mint.publicKey, weweTreasury, true);
const makerWsolAccount = getAssociatedTokenAddressSync(WSOL_MINT, maker.publicKey, true);
const [wsolVault] = getTokenVaultAddress(vaultAuthority, WSOL_MINT, program.programId);
const treasuryTempWsol = findTempWsolPDA(vaultAuthority, proposal, true);
const makerTempWsol = findTempWsolPDA(vaultAuthority, proposal, false);

try {
await program.methods
Expand All @@ -2505,6 +2512,8 @@ describe('Wewe Token Launch Pad - Integration Tests', () => {
tokenAMint: mint.publicKey,
tokenBMint: WSOL_MINT,
positionNftAccount: pdas.positionNftAccount,
treasuryTempWsol,
makerTempWsol,
tokenAProgram: TOKEN_PROGRAM_ID,
tokenBProgram: TOKEN_PROGRAM_ID,
ammProgram: cpAmm.programId,
Expand Down
Loading