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}