substreams_solana_program_instructions/transfer_fee_instruction.rs
1use anyhow::anyhow;
2use {
3 substreams::errors::Error,
4 crate::{token_instruction_2022::TokenInstruction},
5};
6
7#[cfg(feature = "serde-traits")]
8use {
9 crate::serialization::coption_fromstr,
10 serde::{Deserialize, Serialize},
11};
12use crate::option::COption;
13use crate::pubkey::Pubkey;
14
15/// Transfer Fee extension instructions
16#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))]
17#[cfg_attr(
18 feature = "serde-traits",
19 serde(rename_all = "camelCase", rename_all_fields = "camelCase")
20)]
21#[derive(Clone, Copy, Debug, PartialEq)]
22#[repr(u8)]
23pub enum TransferFeeInstruction {
24 /// Initialize the transfer fee on a new mint.
25 ///
26 /// Fails if the mint has already been initialized, so must be called before
27 /// `InitializeMint`.
28 ///
29 /// The mint must have exactly enough space allocated for the base mint (82
30 /// bytes), plus 83 bytes of padding, 1 byte reserved for the account type,
31 /// then space required for this extension, plus any others.
32 ///
33 /// Accounts expected by this instruction:
34 ///
35 /// 0. `[writable]` The mint to initialize.
36 InitializeTransferFeeConfig {
37 /// Pubkey that may update the fees
38 #[cfg_attr(feature = "serde-traits", serde(with = "coption_fromstr"))]
39 transfer_fee_config_authority: COption<Pubkey>,
40 /// Withdraw instructions must be signed by this key
41 #[cfg_attr(feature = "serde-traits", serde(with = "coption_fromstr"))]
42 withdraw_withheld_authority: COption<Pubkey>,
43 /// Amount of transfer collected as fees, expressed as basis points of the
44 /// transfer amount
45 transfer_fee_basis_points: u16,
46 /// Maximum fee assessed on transfers
47 maximum_fee: u64,
48 },
49 /// Transfer, providing expected mint information and fees
50 ///
51 /// Accounts expected by this instruction:
52 ///
53 /// * Single owner/delegate
54 /// 0. `[writable]` The source account. Must include the `TransferFeeAmount` extension.
55 /// 1. `[]` The token mint. Must include the `TransferFeeConfig` extension.
56 /// 2. `[writable]` The destination account. Must include the `TransferFeeAmount` extension.
57 /// 3. `[signer]` The source account's owner/delegate.
58 ///
59 /// * Multisignature owner/delegate
60 /// 0. `[writable]` The source account.
61 /// 1. `[]` The token mint.
62 /// 2. `[writable]` The destination account.
63 /// 3. `[]` The source account's multisignature owner/delegate.
64 /// 4. ..4+M `[signer]` M signer accounts.
65 TransferCheckedWithFee {
66 /// The amount of tokens to transfer.
67 amount: u64,
68 /// Expected number of base 10 digits to the right of the decimal place.
69 decimals: u8,
70 /// Expected fee assessed on this transfer, calculated off-chain based on
71 /// the transfer_fee_basis_points and maximum_fee of the mint.
72 fee: u64,
73 },
74 /// Transfer all withheld tokens in the mint to an account. Signed by the mint's
75 /// withdraw withheld tokens authority.
76 ///
77 /// Accounts expected by this instruction:
78 ///
79 /// * Single owner/delegate
80 /// 0. `[writable]` The token mint. Must include the `TransferFeeConfig` extension.
81 /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount` extension
82 /// associated with the provided mint.
83 /// 2. `[signer]` The mint's `withdraw_withheld_authority`.
84 ///
85 /// * Multisignature owner/delegate
86 /// 0. `[writable]` The token mint.
87 /// 1. `[writable]` The destination account.
88 /// 2. `[]` The mint's multisig `withdraw_withheld_authority`.
89 /// 3. ..3+M `[signer]` M signer accounts.
90 WithdrawWithheldTokensFromMint,
91 /// Transfer all withheld tokens to an account. Signed by the mint's
92 /// withdraw withheld tokens authority.
93 ///
94 /// Accounts expected by this instruction:
95 ///
96 /// * Single owner/delegate
97 /// 0. `[]` The token mint. Must include the `TransferFeeConfig` extension.
98 /// 1. `[writable]` The fee receiver account. Must include the `TransferFeeAmount`
99 /// extension and be associated with the provided mint.
100 /// 2. `[signer]` The mint's `withdraw_withheld_authority`.
101 /// 3. ..3+N `[writable]` The source accounts to withdraw from.
102 ///
103 /// * Multisignature owner/delegate
104 /// 0. `[]` The token mint.
105 /// 1. `[writable]` The destination account.
106 /// 2. `[]` The mint's multisig `withdraw_withheld_authority`.
107 /// 3. ..3+M `[signer]` M signer accounts.
108 /// 3+M+1. ..3+M+N `[writable]` The source accounts to withdraw from.
109 WithdrawWithheldTokensFromAccounts {
110 /// Number of token accounts harvested
111 num_token_accounts: u8,
112 },
113 /// Permissionless instruction to transfer all withheld tokens to the mint.
114 ///
115 /// Succeeds for frozen accounts.
116 ///
117 /// Accounts provided should include the `TransferFeeAmount` extension. If not,
118 /// the account is skipped.
119 ///
120 /// Accounts expected by this instruction:
121 ///
122 /// 0. `[writable]` The mint.
123 /// 1. ..1+N `[writable]` The source accounts to harvest from.
124 HarvestWithheldTokensToMint,
125 /// Set transfer fee. Only supported for mints that include the `TransferFeeConfig` extension.
126 ///
127 /// Accounts expected by this instruction:
128 ///
129 /// * Single authority
130 /// 0. `[writable]` The mint.
131 /// 1. `[signer]` The mint's fee account owner.
132 ///
133 /// * Multisignature authority
134 /// 0. `[writable]` The mint.
135 /// 1. `[]` The mint's multisignature fee account owner.
136 /// 2. ..2+M `[signer]` M signer accounts.
137 SetTransferFee {
138 /// Amount of transfer collected as fees, expressed as basis points of the
139 /// transfer amount
140 transfer_fee_basis_points: u16,
141 /// Maximum fee assessed on transfers
142 maximum_fee: u64,
143 },
144}
145impl TransferFeeInstruction {
146 /// Unpacks a byte buffer into a TransferFeeInstruction
147 pub fn unpack(input: &[u8]) -> Result<(Self, &[u8]), Error> {
148 let (&tag, rest) = input.split_first().ok_or(anyhow!("Invalid Transfer Fee Instruction"))?;
149 Ok(match tag {
150 0 => {
151 let (transfer_fee_config_authority, rest) =
152 TokenInstruction::unpack_pubkey_option(rest)?;
153 let (withdraw_withheld_authority, rest) =
154 TokenInstruction::unpack_pubkey_option(rest)?;
155 let (transfer_fee_basis_points, rest) = TokenInstruction::unpack_u16(rest)?;
156 let (maximum_fee, rest) = TokenInstruction::unpack_u64(rest)?;
157 let instruction = Self::InitializeTransferFeeConfig {
158 transfer_fee_config_authority,
159 withdraw_withheld_authority,
160 transfer_fee_basis_points,
161 maximum_fee,
162 };
163 (instruction, rest)
164 }
165 1 => {
166 let (amount, decimals, rest) = TokenInstruction::unpack_amount_decimals(rest)?;
167 let (fee, rest) = TokenInstruction::unpack_u64(rest)?;
168 let instruction = Self::TransferCheckedWithFee {
169 amount,
170 decimals,
171 fee,
172 };
173 (instruction, rest)
174 }
175 2 => (Self::WithdrawWithheldTokensFromMint, rest),
176 3 => {
177 let (&num_token_accounts, rest) = rest.split_first().ok_or(anyhow!("Invalid Transfer Fee Instruction - 3"))?;
178 let instruction = Self::WithdrawWithheldTokensFromAccounts { num_token_accounts };
179 (instruction, rest)
180 }
181 4 => (Self::HarvestWithheldTokensToMint, rest),
182 5 => {
183 let (transfer_fee_basis_points, rest) = TokenInstruction::unpack_u16(rest)?;
184 let (maximum_fee, rest) = TokenInstruction::unpack_u64(rest)?;
185 let instruction = Self::SetTransferFee {
186 transfer_fee_basis_points,
187 maximum_fee,
188 };
189 (instruction, rest)
190 }
191 _ => return Err(anyhow!("Invalid Transfer Fee Instruction - unpack didn't match any tag value: {}", tag)),
192 })
193 }
194}