Skip to content

Commit c900dfb

Browse files
committed
feat: address comments and change update user share behaviour
1 parent 1decc35 commit c900dfb

File tree

17 files changed

+471
-132
lines changed

17 files changed

+471
-132
lines changed

CHANGELOG.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2626
### Added
2727

2828
- Add a new field `mutable_flag` to `FeeVault` to indicate its mutability
29-
- Add a new field `operator` to `FeeVault`. The `operator` and vault owner can perform admin instructions on mutable `FeeVault`
29+
- Add a new field `operator` to `FeeVault`. The `operator` and vault owner can perform operator instructions on mutable `FeeVault`
3030
- Add a new owner endpoint `update_operator` for vault owner to update the operator field
31-
- Add a new admin endpoint `remove_user` which removes a user and distributes their unclaimed fees proportionally based on the remaining users' share
32-
- Add a new admin endpoint `update_user_share` to update a user's share. This affects the fees the user will be entitled to when the vault is funded. Any fees users earned before the share changed will be preserved
31+
- Add a new operator endpoint `remove_user` which removes a user and transfers any unclaimed fee into an account for the removed user to claim
32+
- Add a new endpoint `claim_removed_user_fee` where a user who have been removed from the `FeeVault` can claim any unclaimed fees
33+
- Add a new operator endpoint `update_user_share` to update a user's share. This affects the fees the user will be entitled to when the vault is funded. Any fees users earned before the share changed will be preserved
3334

3435
## dynamic-fee-sharing [0.1.1] [PR #8](https://github.com/MeteoraAg/dynamic-fee-sharing/pull/8)
3536

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ incremental = false
1414
codegen-units = 1
1515

1616
[workspace.dependencies]
17-
anchor-lang = {version = "0.31.1", features = ["event-cpi"]}
17+
anchor-lang = {version = "0.31.1", features = ["event-cpi", "init-if-needed"]}
1818
anchor-spl = "0.31.1"
1919
bytemuck = { version = "1.20.0", features = ["derive", "min_const_generics"] }

programs/dynamic-fee-sharing/src/constants.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod seeds {
99
pub const FEE_VAULT_PREFIX: &[u8] = b"fee_vault";
1010
pub const FEE_VAULT_AUTHORITY_PREFIX: &[u8] = b"fee_vault_authority";
1111
pub const TOKEN_VAULT_PREFIX: &[u8] = b"token_vault";
12+
pub const REMOVED_USER_TOKEN_VAULT: &[u8] = b"removed_user_token_vault";
1213
}
1314

1415
// (program_id, instruction, index_of_token_vault_account)

programs/dynamic-fee-sharing/src/event.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,20 @@ pub struct EvtClaimFee {
3131
#[event]
3232
pub struct EvtUpdateUserShare {
3333
pub fee_vault: Pubkey,
34-
pub index: u8,
34+
pub user: Pubkey,
3535
pub share: u32,
3636
}
3737

3838
#[event]
3939
pub struct EvtRemoveUser {
4040
pub fee_vault: Pubkey,
4141
pub user: Pubkey,
42+
pub unclaimed_fee: u64,
43+
}
44+
45+
#[event]
46+
pub struct EvtClaimRemovedUserFee {
47+
pub fee_vault: Pubkey,
48+
pub user: Pubkey,
49+
pub claimed_fee: u64,
4250
}

programs/dynamic-fee-sharing/src/instructions/admin/ix_remove_user.rs

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use crate::const_pda;
2+
use crate::constants::seeds::REMOVED_USER_TOKEN_VAULT;
3+
use crate::event::EvtClaimRemovedUserFee;
4+
use crate::state::FeeVault;
5+
use crate::utils::token::transfer_from_fee_vault;
6+
use anchor_lang::prelude::*;
7+
use anchor_spl::token_interface::{
8+
close_account, CloseAccount, Mint, TokenAccount, TokenInterface,
9+
};
10+
11+
#[event_cpi]
12+
#[derive(Accounts)]
13+
pub struct ClaimRemovedUserFeeCtx<'info> {
14+
#[account(has_one = token_mint, has_one = owner)]
15+
pub fee_vault: AccountLoader<'info, FeeVault>,
16+
17+
/// CHECK: fee vault authority
18+
#[account(address = const_pda::fee_vault_authority::ID)]
19+
pub fee_vault_authority: UncheckedAccount<'info>,
20+
21+
pub token_mint: Box<InterfaceAccount<'info, Mint>>,
22+
23+
#[account(
24+
mut,
25+
seeds = [
26+
REMOVED_USER_TOKEN_VAULT,
27+
fee_vault.key().as_ref(),
28+
token_mint.key().as_ref(),
29+
user.key().as_ref(),
30+
],
31+
bump,
32+
token::mint = token_mint,
33+
token::authority = fee_vault_authority,
34+
)]
35+
pub removed_user_token_vault: Box<InterfaceAccount<'info, TokenAccount>>,
36+
37+
#[account(
38+
mut,
39+
token::authority = user,
40+
token::mint = token_mint,
41+
)]
42+
pub user_token_vault: Box<InterfaceAccount<'info, TokenAccount>>,
43+
44+
/// CHECK: fee vault owner, receives rent from closed account
45+
#[account(mut)]
46+
pub owner: UncheckedAccount<'info>,
47+
48+
pub user: Signer<'info>,
49+
50+
pub token_program: Interface<'info, TokenInterface>,
51+
}
52+
53+
pub fn handle_claim_removed_user_fee(ctx: Context<ClaimRemovedUserFeeCtx>) -> Result<()> {
54+
let fee_being_claimed = ctx.accounts.removed_user_token_vault.amount;
55+
56+
if fee_being_claimed > 0 {
57+
transfer_from_fee_vault(
58+
ctx.accounts.fee_vault_authority.to_account_info(),
59+
&ctx.accounts.token_mint,
60+
&ctx.accounts.removed_user_token_vault,
61+
&ctx.accounts.user_token_vault,
62+
&ctx.accounts.token_program,
63+
fee_being_claimed,
64+
)?;
65+
}
66+
67+
let signer_seeds = fee_vault_authority_seeds!();
68+
close_account(CpiContext::new_with_signer(
69+
ctx.accounts.token_program.to_account_info(),
70+
CloseAccount {
71+
account: ctx.accounts.removed_user_token_vault.to_account_info(),
72+
destination: ctx.accounts.owner.to_account_info(),
73+
authority: ctx.accounts.fee_vault_authority.to_account_info(),
74+
},
75+
&[&signer_seeds[..]],
76+
))?;
77+
78+
emit_cpi!(EvtClaimRemovedUserFee {
79+
fee_vault: ctx.accounts.fee_vault.key(),
80+
user: ctx.accounts.user.key(),
81+
claimed_fee: fee_being_claimed,
82+
});
83+
84+
Ok(())
85+
}

programs/dynamic-fee-sharing/src/instructions/ix_initialize_fee_vault.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
1313
#[derive(AnchorSerialize, AnchorDeserialize, Debug, Clone)]
1414
pub struct InitializeFeeVaultParameters {
1515
pub padding: [u8; 63], // for future use
16-
pub mutable_flag: u8,
16+
pub mutable_flag: bool,
1717
pub users: Vec<UserShare>,
1818
}
1919

@@ -40,10 +40,6 @@ impl InitializeFeeVaultParameters {
4040
FeeVaultError::InvalidUserAddress
4141
);
4242
}
43-
require!(
44-
self.mutable_flag == 0 || self.mutable_flag == 1,
45-
FeeVaultError::InvalidFeeVaultParameters
46-
);
4743
// that is fine to leave user addresses are duplicated?
4844
Ok(())
4945
}
@@ -113,7 +109,7 @@ pub fn handle_initialize_fee_vault(
113109
&Pubkey::default(),
114110
0,
115111
FeeVaultType::NonPdaAccount.into(),
116-
params.mutable_flag,
112+
params.mutable_flag.into(),
117113
)?;
118114

119115
emit_cpi!(EvtInitializeFeeVault {

programs/dynamic-fee-sharing/src/instructions/ix_initialize_fee_vault_pda.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ pub fn handle_initialize_fee_vault_pda(
8080
&ctx.accounts.base.key,
8181
ctx.bumps.fee_vault,
8282
FeeVaultType::PdaAccount.into(),
83-
params.mutable_flag,
83+
params.mutable_flag.into(),
8484
)?;
8585

8686
emit_cpi!(EvtInitializeFeeVault {

programs/dynamic-fee-sharing/src/instructions/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ pub mod ix_initialize_fee_vault_pda;
88
pub use ix_initialize_fee_vault_pda::*;
99
pub mod ix_fund_by_claiming_fee;
1010
pub use ix_fund_by_claiming_fee::*;
11-
pub mod admin;
12-
pub use admin::*;
11+
pub mod ix_claim_removed_user_fee;
12+
pub use ix_claim_removed_user_fee::*;
13+
pub mod operator;
14+
pub use operator::*;
1315
pub mod owner;
1416
pub use owner::*;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use crate::const_pda;
2+
use crate::constants::seeds::REMOVED_USER_TOKEN_VAULT;
3+
use crate::event::EvtRemoveUser;
4+
use crate::state::FeeVault;
5+
use crate::utils::token::transfer_from_fee_vault;
6+
use anchor_lang::prelude::*;
7+
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface};
8+
9+
#[event_cpi]
10+
#[derive(Accounts)]
11+
pub struct RemoveUserCtx<'info> {
12+
#[account(mut, has_one = token_vault, has_one = token_mint)]
13+
pub fee_vault: AccountLoader<'info, FeeVault>,
14+
15+
/// CHECK: fee vault authority
16+
#[account(address = const_pda::fee_vault_authority::ID)]
17+
pub fee_vault_authority: UncheckedAccount<'info>,
18+
19+
#[account(mut)]
20+
pub token_vault: Box<InterfaceAccount<'info, TokenAccount>>,
21+
22+
pub token_mint: Box<InterfaceAccount<'info, Mint>>,
23+
24+
/// CHECK: the user being removed
25+
pub user: UncheckedAccount<'info>,
26+
27+
#[account(
28+
init_if_needed,
29+
payer = signer,
30+
seeds = [
31+
REMOVED_USER_TOKEN_VAULT,
32+
fee_vault.key().as_ref(),
33+
token_mint.key().as_ref(),
34+
user.key().as_ref(),
35+
],
36+
bump,
37+
token::mint = token_mint,
38+
token::authority = fee_vault_authority,
39+
)]
40+
pub removed_user_token_vault: Box<InterfaceAccount<'info, TokenAccount>>,
41+
42+
#[account(mut)]
43+
pub signer: Signer<'info>,
44+
45+
pub token_program: Interface<'info, TokenInterface>,
46+
pub system_program: Program<'info, System>,
47+
}
48+
49+
pub fn handle_remove_user(ctx: Context<RemoveUserCtx>) -> Result<()> {
50+
let mut fee_vault = ctx.accounts.fee_vault.load_mut()?;
51+
let user = ctx.accounts.user.key();
52+
let unclaimed_fee = fee_vault.validate_and_remove_user_and_get_unclaimed_fee(&user)?;
53+
54+
if unclaimed_fee > 0 {
55+
transfer_from_fee_vault(
56+
ctx.accounts.fee_vault_authority.to_account_info(),
57+
&ctx.accounts.token_mint,
58+
&ctx.accounts.token_vault,
59+
&ctx.accounts.removed_user_token_vault,
60+
&ctx.accounts.token_program,
61+
unclaimed_fee,
62+
)?;
63+
}
64+
65+
emit_cpi!(EvtRemoveUser {
66+
fee_vault: ctx.accounts.fee_vault.key(),
67+
user,
68+
unclaimed_fee,
69+
});
70+
71+
Ok(())
72+
}

0 commit comments

Comments
 (0)