Skip to content
This repository has been archived by the owner on Jan 10, 2025. It is now read-only.

token-2022: Add transfer fee types and instructions #2608

Merged
merged 10 commits into from
Dec 14, 2021
178 changes: 177 additions & 1 deletion token/program-2022/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub enum TokenInstruction {
/// Otherwise another party can acquire ownership of the uninitialized
/// account.
///
/// Any mixins must be initialized before this instruction.
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The mint to initialize.
Expand All @@ -54,6 +56,8 @@ pub enum TokenInstruction {
/// Otherwise another party can acquire ownership of the uninitialized
/// account.
///
/// Any mixins must be initialized before this instruction.
mvines marked this conversation as resolved.
Show resolved Hide resolved
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The account to initialize.
Expand Down Expand Up @@ -85,11 +89,17 @@ pub enum TokenInstruction {
/// account.
m: u8,
},
/// NOTE This instruction is deprecated in favor of `TransferChecked` or
/// `TransferCheckedWithFee`
joncinque marked this conversation as resolved.
Show resolved Hide resolved
///
/// Transfers tokens from one account to another either directly or via a
/// delegate. If this account is associated with the native mint then equal
/// amounts of SOL and Tokens will be transferred to the destination
/// account.
///
/// If the accounts contain `AccountTransferFeeMixin`s, this will fail. Mints with
/// the `MintTransferFeeMixin` are required in order to assess the fee.
///
/// Accounts expected by this instruction:
///
/// * Single owner/delegate
Expand Down Expand Up @@ -197,6 +207,9 @@ pub enum TokenInstruction {
/// Close an account by transferring all its SOL to the destination account.
/// Non-native accounts may only be closed if its token amount is zero.
///
/// Accounts with the `AccountTransferFeeMixin` may only be closed if the withheld
/// amount is zero.
///
joncinque marked this conversation as resolved.
Show resolved Hide resolved
/// Accounts expected by this instruction:
///
/// * Single owner
Expand Down Expand Up @@ -251,6 +264,9 @@ pub enum TokenInstruction {
/// decimals value is checked by the caller. This may be useful when
/// creating transactions offline or within a hardware wallet.
///
/// If the accounts contain `AccountTransferFeeMixin`s, the fee is withheld in the
/// destination account.
mvines marked this conversation as resolved.
Show resolved Hide resolved
///
/// Accounts expected by this instruction:
///
/// * Single owner/delegate
Expand Down Expand Up @@ -354,11 +370,13 @@ pub enum TokenInstruction {
/// Cross Program Invocation from an instruction that does not need the owner's
/// `AccountInfo` otherwise.
///
/// Any mixins must be initialized before this instruction.
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The account to initialize.
/// 1. `[]` The mint this account will be associated with.
/// 3. `[]` Rent sysvar
/// 2. `[]` Rent sysvar
InitializeAccount2 {
/// The new account's owner/multisignature.
owner: Pubkey,
Expand All @@ -375,6 +393,8 @@ pub enum TokenInstruction {
SyncNative,
/// Like InitializeAccount2, but does not require the Rent sysvar to be provided
///
/// Any mixins must be initialized before this instruction.
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The account to initialize.
Expand All @@ -397,6 +417,8 @@ pub enum TokenInstruction {
},
/// Like InitializeMint, but does not require the Rent sysvar to be provided
///
/// Any mixins must be initialized before this instruction.
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The mint to initialize.
Expand All @@ -409,6 +431,148 @@ pub enum TokenInstruction {
/// The freeze authority/multisignature of the mint.
freeze_authority: COption<Pubkey>,
},
/// Gets the required size of an account for the given mint as a little-endian
/// `u64`.
mvines marked this conversation as resolved.
Show resolved Hide resolved
///
/// Return data can be fetched using `sol_get_return_data` and deserializing
/// the return data as a little-endian `u64`.
///
/// Accounts expected by this instruction:
///
/// 0. `[]` The mint to calculate for
GetAccountLength,
joncinque marked this conversation as resolved.
Show resolved Hide resolved
/// Initializes the empty mixin on a new account.
///
/// The account must have a size 356, which requires it to use this mixin.
/// The size of 356 comes from the size of a multisig (355) plus 1 byte of
/// padding.
///
/// Fails if the account has already been initialized, so must be called before
/// `InitializeAccount`. The account must have enough data allocated for all
/// mixins included in the mint.
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The account to initialize.
InitializeEmptyMixin,
mvines marked this conversation as resolved.
Show resolved Hide resolved
/// Initialize the close account mixin on a new mint.
joncinque marked this conversation as resolved.
Show resolved Hide resolved
///
/// Fails if the mint has already been initialized, so must be called before
/// `InitializeMint`.
///
/// The mint must have exactly enough space allocated for the base mint (82
/// bytes), plus 83 bytes of padding, 1 byte reserved for the account type,
CriesofCarrots marked this conversation as resolved.
Show resolved Hide resolved
/// then space required for the mixin.
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The mint to initialize.
InitializeMintCloseMixin,
joncinque marked this conversation as resolved.
Show resolved Hide resolved
/// Initialize the transfer fee mixin on a new mint.
joncinque marked this conversation as resolved.
Show resolved Hide resolved
///
/// Fails if the mint has already been initialized, so must be called before
/// `InitializeMint`.
///
/// The mint must have exactly enough space allocated for the base mint (82
/// bytes), plus 83 bytes of padding, 1 byte reserved for the account type,
/// then space required for the mixin.
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The mint to initialize.
InitializeMintTransferFeeMixin {
joncinque marked this conversation as resolved.
Show resolved Hide resolved
/// Pubkey that may update the fees
fee_config_authority: COption<Pubkey>,
/// Harvest instructions must be signed by this key
fee_withdraw_authority: COption<Pubkey>,
/// Amount of transfer collected as fees, expressed as basis points of the
/// transfer amount
transfer_fee_basis_points: u16,
/// Maximum fee assessed on transfers
maximum_fee: u64,
},
/// Initializes the transfer fee mixin on a new account.
///
/// Fails if the account has already been initialized, so must be called before
/// `InitializeAccount`. The account must have enough data allocated for all
/// mixins included in the mint.
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The account to initialize.
/// 1. `[]` The mint this account will be associated with.
InitializeAccountTransferFeeMixin,
joncinque marked this conversation as resolved.
Show resolved Hide resolved
joncinque marked this conversation as resolved.
Show resolved Hide resolved
/// Transfer, providing expected mint information and fees
///
/// Accounts expected by this instruction:
///
/// * Single owner/delegate
/// 0. `[writable]` The source account. Must include the `AccountTransferFeeMixin`.
/// 1. `[]` The token mint. Must include the `AccountMintFeeMixin`.
/// 2. `[writable]` The destination account. Must include the `AccountTransferFeeMixin`.
/// 3. `[signer]` The source account's owner/delegate.
///
/// * Multisignature owner/delegate
/// 0. `[writable]` The source account.
/// 1. `[]` The token mint.
/// 2. `[writable]` The destination account.
/// 3. `[]` The source account's multisignature owner/delegate.
/// 4. ..4+M `[signer]` M signer accounts.
TransferCheckedWithFee {
/// The amount of tokens to transfer.
amount: u64,
/// Expected number of base 10 digits to the right of the decimal place.
decimals: u8,
/// Expected fee assessed on this transfer, calculated off-chain based on
/// the transfer_fee_basis_points and maximum_fee of the mint.
fee: u64,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the mint doesn't include an MintTransferFee extension, should we still allow this instruction to be used but the fee must be 0?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to me

},
/// Transfer all withheld tokens to a fee account. Signed by the mint's
joncinque marked this conversation as resolved.
Show resolved Hide resolved
/// fee withdraw authority.
///
/// Accounts expected by this instruction:
///
/// * Single owner/delegate
/// 0. `[writable]` The token mint. Must include the `MintTransferFeeMixin`.
/// 1. `[writable]` The fee receiver account. Must include the `AccountTransferFeeMixin`.
/// associated with the provided mint.
/// 2. `[signer]` The mint's `fee_withdraw_authority`
///
/// * Multisignature owner/delegate
/// 0. `[writable]` The token mint.
/// 1. `[writable]` The destination account.
/// 2. `[]` The mint's `fee_withdraw_authority`'s multisignature owner/delegate.
/// 3. ..3+M `[signer]` M signer accounts.
HarvestFeeFromMint,
joncinque marked this conversation as resolved.
Show resolved Hide resolved
/// Permissionless instruction to transfer all withheld tokens to the mint.
///
/// Succeeds for frozen accounts.
///
/// Accounts expected by this instruction:
///
/// 0. `[writable]` The mint.
/// 1. ..1+N `[writable]` The source accounts to harvest from.
joncinque marked this conversation as resolved.
Show resolved Hide resolved
/// Must include the `AccountTransferFeeMixin`.
HarvestFeeToMint,
joncinque marked this conversation as resolved.
Show resolved Hide resolved
/// Set transfer fee. Only supported for mints that include the `MintTransferFeeMixin`.
///
/// Accounts expected by this instruction:
///
/// * Single authority
/// 0. `[writable]` The mint.
/// 1. `[signer]` The mint's fee account owner.
///
/// * Multisignature authority
/// 0. `[writable]` The mint.
/// 1. `[]` The mint's multisignature fee account owner.
/// 2. ..2+M `[signer]` M signer accounts.
SetTransferFee {
/// Amount of transfer collected as fees, expressed as basis points of the
/// transfer amount
transfer_fee_basis_points: u16,
/// Maximum fee assessed on transfers
maximum_fee: u64,
mvines marked this conversation as resolved.
Show resolved Hide resolved
},
}
impl TokenInstruction {
/// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html).
Expand Down Expand Up @@ -674,6 +838,10 @@ pub enum AuthorityType {
AccountOwner,
/// Authority to close a token account
CloseAccount,
/// Authority to set the fee
FeeConfig,
joncinque marked this conversation as resolved.
Show resolved Hide resolved
joncinque marked this conversation as resolved.
Show resolved Hide resolved
/// Authority to withdraw fees from accounts and mint
FeeWithdraw,
joncinque marked this conversation as resolved.
Show resolved Hide resolved
}

impl AuthorityType {
Expand All @@ -683,6 +851,8 @@ impl AuthorityType {
AuthorityType::FreezeAccount => 1,
AuthorityType::AccountOwner => 2,
AuthorityType::CloseAccount => 3,
AuthorityType::FeeConfig => 4,
AuthorityType::FeeWithdraw => 5,
}
}

Expand All @@ -692,6 +862,8 @@ impl AuthorityType {
1 => Ok(AuthorityType::FreezeAccount),
2 => Ok(AuthorityType::AccountOwner),
3 => Ok(AuthorityType::CloseAccount),
4 => Ok(AuthorityType::FeeConfig),
5 => Ok(AuthorityType::FeeWithdraw),
_ => Err(TokenError::InvalidInstruction.into()),
}
}
Expand Down Expand Up @@ -887,6 +1059,10 @@ pub fn initialize_multisig2(
}

/// Creates a `Transfer` instruction.
#[deprecated(
since = "4.0.0",
note = "please use `transfer_checked` or `transfer_checked_with_fee` instead"
)]
pub fn transfer(
token_program_id: &Pubkey,
source_pubkey: &Pubkey,
Expand Down
Loading