Reputation: 638
When broadcastring a token-2022 program transaction using the transferCheckedWithFee
instruction, solana documentation suggest to compute token fee using this formula:
const fee = (amountToSend * feeBasisPoints) / 10_000
const feeCharged = fee > maximumFee ? maximumFee : fee
But you can end up getting an error like:
Program log: TransferFeeInstruction: TransferCheckedWithFee","Program log: Calculated fee 18799792, received 18799791","Program log: Calculated fee does not match expected fee","Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 7544 of 200000 compute units
Likely a rounding issue. Is it fine to Math.ceil
the fee amount?
Upvotes: 0
Views: 232
Reputation: 8472
You've got it right, you need to ceil
the division.
The fee calculation in the program will ceiling the division for you at https://github.com/solana-labs/solana-program-library/blob/b3f68a8c3c362ed2ce3677c565bcf9ed9f136310/token/program-2022/src/extension/transfer_fee/mod.rs#L45-L71.
Posting the code here for posterity:
/// Calculate ceiling-division
///
/// Ceiling-division
/// `ceil[ numerator / denominator ]`
/// can be represented as a floor-division
/// `floor[ (numerator + denominator - 1) / denominator]`
fn ceil_div(numerator: u128, denominator: u128) -> Option<u128> {
numerator
.checked_add(denominator)?
.checked_sub(1)?
.checked_div(denominator)
}
/// Calculate the transfer fee
pub fn calculate_fee(&self, pre_fee_amount: u64) -> Option<u64> {
let transfer_fee_basis_points = u16::from(self.transfer_fee_basis_points) as u128;
if transfer_fee_basis_points == 0 || pre_fee_amount == 0 {
Some(0)
} else {
let numerator = (pre_fee_amount as u128).checked_mul(transfer_fee_basis_points)?;
let raw_fee = Self::ceil_div(numerator, ONE_IN_BASIS_POINTS)?
.try_into() // guaranteed to be okay
.ok()?;
Some(cmp::min(raw_fee, u64::from(self.maximum_fee)))
}
}
Upvotes: 1