Skip to content

Commit ee8fcc5

Browse files
authored
Merge pull request #71 from MyWewe-io/feat/unwrap_pool_fees
Add wsol unwrap logic
2 parents 4a9d27f + 0ebd07c commit ee8fcc5

3 files changed

Lines changed: 128 additions & 4 deletions

File tree

programs/wewe-token-launch-pad/src/instructions/admin/ix_collect_pool_fees.rs

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use anchor_lang::prelude::*;
22
use anchor_spl::{
33
associated_token::AssociatedToken,
4-
token::Token,
4+
token::{close_account, CloseAccount, Token},
55
token_interface::{TokenAccount, TokenInterface},
66
};
77

@@ -52,6 +52,7 @@ pub struct ClaimPositionFee<'info> {
5252
/// CHECK:
5353
pub token_b_mint: UncheckedAccount<'info>,
5454

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

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

123+
/// CHECK: Temporary WSOL account for treasury unwrapping (PDA derived from token program)
124+
/// PDA: [b"temp_wsol", vault_authority, proposal, b"treasury"]
125+
#[account(
126+
mut,
127+
constraint = {
128+
let (expected_pda, _) = Pubkey::find_program_address(
129+
&[
130+
b"temp_wsol",
131+
vault_authority.key().as_ref(),
132+
proposal.key().as_ref(),
133+
b"treasury",
134+
],
135+
&token_b_program.key(),
136+
);
137+
treasury_temp_wsol.key() == expected_pda
138+
} @ ProposalError::IncorrectAccount
139+
)]
140+
pub treasury_temp_wsol: UncheckedAccount<'info>,
141+
142+
/// CHECK: Temporary WSOL account for maker unwrapping (PDA derived from token program)
143+
/// PDA: [b"temp_wsol", vault_authority, proposal, b"maker"]
144+
#[account(
145+
mut,
146+
constraint = {
147+
let (expected_pda, _) = Pubkey::find_program_address(
148+
&[
149+
b"temp_wsol",
150+
vault_authority.key().as_ref(),
151+
proposal.key().as_ref(),
152+
b"maker",
153+
],
154+
&token_b_program.key(),
155+
);
156+
maker_temp_wsol.key() == expected_pda
157+
} @ ProposalError::IncorrectAccount
158+
)]
159+
pub maker_temp_wsol: UncheckedAccount<'info>,
160+
121161
pub token_a_program: Interface<'info, TokenInterface>,
122162

123163
pub token_b_program: Interface<'info, TokenInterface>,
@@ -217,34 +257,91 @@ impl<'info> ClaimPositionFee<'info> {
217257
)?;
218258
}
219259

260+
// Unwrap WSOL (token_b) to SOL before transferring
261+
// We use PDA-derived temporary accounts (validated at account struct level)
220262
if treasury_b > 0 {
263+
// Transfer WSOL to temporary PDA account
221264
anchor_spl::token::transfer(
222265
CpiContext::new_with_signer(
223266
self.token_b_program.to_account_info(),
224267
anchor_spl::token::Transfer {
225268
from: self.token_b_account.to_account_info(),
226-
to: self.wewe_wsol_account.to_account_info(),
269+
to: self.treasury_temp_wsol.to_account_info(),
227270
authority: self.vault_authority.to_account_info(),
228271
},
229272
&[&vault_authority_seeds[..]],
230273
),
231274
treasury_b,
232275
)?;
276+
277+
// Close the temporary WSOL account to unwrap it to SOL
278+
// The SOL (lamports) will be sent to the account owner (vault_authority)
279+
close_account(
280+
CpiContext::new_with_signer(
281+
self.token_b_program.to_account_info(),
282+
CloseAccount {
283+
account: self.treasury_temp_wsol.to_account_info(),
284+
destination: self.vault_authority.to_account_info(),
285+
authority: self.vault_authority.to_account_info(),
286+
},
287+
&[&vault_authority_seeds[..]],
288+
),
289+
)?;
290+
291+
// Transfer SOL from vault_authority to treasury
292+
anchor_lang::system_program::transfer(
293+
CpiContext::new_with_signer(
294+
self.system_program.to_account_info(),
295+
anchor_lang::system_program::Transfer {
296+
from: self.vault_authority.to_account_info(),
297+
to: self.wewe_treasury.to_account_info(),
298+
},
299+
&[&vault_authority_seeds[..]],
300+
),
301+
treasury_b,
302+
)?;
233303
}
234304

235305
if maker_b > 0 {
306+
// Transfer WSOL to temporary PDA account
236307
anchor_spl::token::transfer(
237308
CpiContext::new_with_signer(
238309
self.token_b_program.to_account_info(),
239310
anchor_spl::token::Transfer {
240311
from: self.token_b_account.to_account_info(),
241-
to: self.maker_wsol_account.to_account_info(),
312+
to: self.maker_temp_wsol.to_account_info(),
242313
authority: self.vault_authority.to_account_info(),
243314
},
244315
&[&vault_authority_seeds[..]],
245316
),
246317
maker_b,
247318
)?;
319+
320+
// Close the temporary WSOL account to unwrap it to SOL
321+
close_account(
322+
CpiContext::new_with_signer(
323+
self.token_b_program.to_account_info(),
324+
CloseAccount {
325+
account: self.maker_temp_wsol.to_account_info(),
326+
destination: self.vault_authority.to_account_info(),
327+
authority: self.vault_authority.to_account_info(),
328+
},
329+
&[&vault_authority_seeds[..]],
330+
),
331+
)?;
332+
333+
// Transfer SOL from vault_authority to maker
334+
anchor_lang::system_program::transfer(
335+
CpiContext::new_with_signer(
336+
self.system_program.to_account_info(),
337+
anchor_lang::system_program::Transfer {
338+
from: self.vault_authority.to_account_info(),
339+
to: self.maker.to_account_info(),
340+
},
341+
&[&vault_authority_seeds[..]],
342+
),
343+
maker_b,
344+
)?;
248345
}
249346

250347
emit!(PositionFeeClaimed {

tests/utils.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// utils/helpers.ts
22
import * as anchor from '@coral-xyz/anchor';
33
import Decimal from "decimal.js";
4-
import { getAssociatedTokenAddressSync } from '@solana/spl-token';
4+
import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from '@solana/spl-token';
55
import BN from "bn.js";
66

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

122+
export function findTempWsolPDA(
123+
vaultAuthority: anchor.web3.PublicKey,
124+
proposal: anchor.web3.PublicKey,
125+
isTreasury: boolean
126+
): anchor.web3.PublicKey {
127+
const [tempWsolPDA] = anchor.web3.PublicKey.findProgramAddressSync(
128+
[
129+
Buffer.from("temp_wsol"),
130+
vaultAuthority.toBuffer(),
131+
proposal.toBuffer(),
132+
Buffer.from(isTreasury ? "treasury" : "maker"),
133+
],
134+
TOKEN_PROGRAM_ID
135+
);
136+
137+
return tempWsolPDA;
138+
}
139+
122140
export const derivePoolPDAs = (
123141
programId: anchor.web3.PublicKey,
124142
cpAmmProgramId: anchor.web3.PublicKey,

tests/wewe-token-launch-pad.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
findConfigPDA,
3838
findBackerProposalCountPDA,
3939
findMetadataPDA,
40+
findTempWsolPDA,
4041
} from './utils';
4142

4243
const TOKEN_METADATA_PROGRAM_ID = new anchor.web3.PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
@@ -1507,6 +1508,8 @@ describe('Wewe Token Launch Pad - Integration Tests', () => {
15071508
const weweTokenAccount = getAssociatedTokenAddressSync(mint.publicKey, weweTreasury, true);
15081509
const makerWsolAccount = getAssociatedTokenAddressSync(WSOL_MINT, maker.publicKey, true);
15091510
const [wsolVault] = getTokenVaultAddress(vaultAuthority, WSOL_MINT, program.programId);
1511+
const treasuryTempWsol = findTempWsolPDA(vaultAuthority, proposal, true);
1512+
const makerTempWsol = findTempWsolPDA(vaultAuthority, proposal, false);
15101513
const computeUnitsIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 });
15111514

15121515
const tx = await program.methods
@@ -1531,6 +1534,8 @@ describe('Wewe Token Launch Pad - Integration Tests', () => {
15311534
tokenAMint: mint.publicKey,
15321535
tokenBMint: WSOL_MINT,
15331536
positionNftAccount: pdas.positionNftAccount,
1537+
treasuryTempWsol,
1538+
makerTempWsol,
15341539
tokenAProgram: TOKEN_PROGRAM_ID,
15351540
tokenBProgram: TOKEN_PROGRAM_ID,
15361541
ammProgram: cpAmm.programId,
@@ -2476,6 +2481,8 @@ describe('Wewe Token Launch Pad - Integration Tests', () => {
24762481
const weweTokenAccount = getAssociatedTokenAddressSync(mint.publicKey, weweTreasury, true);
24772482
const makerWsolAccount = getAssociatedTokenAddressSync(WSOL_MINT, maker.publicKey, true);
24782483
const [wsolVault] = getTokenVaultAddress(vaultAuthority, WSOL_MINT, program.programId);
2484+
const treasuryTempWsol = findTempWsolPDA(vaultAuthority, proposal, true);
2485+
const makerTempWsol = findTempWsolPDA(vaultAuthority, proposal, false);
24792486

24802487
try {
24812488
await program.methods
@@ -2500,6 +2507,8 @@ describe('Wewe Token Launch Pad - Integration Tests', () => {
25002507
tokenAMint: mint.publicKey,
25012508
tokenBMint: WSOL_MINT,
25022509
positionNftAccount: pdas.positionNftAccount,
2510+
treasuryTempWsol,
2511+
makerTempWsol,
25032512
tokenAProgram: TOKEN_PROGRAM_ID,
25042513
tokenBProgram: TOKEN_PROGRAM_ID,
25052514
ammProgram: cpAmm.programId,

0 commit comments

Comments
 (0)