Fix/escrow support transfer fee [EXO-8]#97
Conversation
Greptile SummaryThis PR adds proper Token2022 Confidence Score: 5/5Safe to merge — all remaining findings are P2 documentation issues that do not affect runtime correctness. The on-chain logic is correct: TransferChecked is the proper CPI for Token2022 mints with extensions, the balance-delta pattern accurately accounts for withheld fees on deposit, and release correctly captures the full escrow debit (fees are always at the destination). Only P2 doc/comment issues found. No files require special attention; test documentation comments in test_release_funds/mod.rs are slightly inaccurate but do not affect correctness. Important Files Changed
Sequence DiagramsequenceDiagram
participant U as User/Operator
participant EP as EscrowProgram
participant T22 as SPL Token 2022
Note over EP,T22: Deposit (fee-bearing mint)
U->>EP: deposit(amount)
EP->>EP: read escrow_balance_before
EP->>T22: TransferChecked(from=user_ata, to=escrow_ata, amount, mint, decimals)
T22-->>T22: withhold fee at escrow_ata (destination)
T22-->>EP: ok
EP->>EP: read escrow_balance_after
EP->>EP: received = after - before
EP->>EP: emit DepositEvent(amount=received)
Note over EP,T22: Release (fee-bearing mint)
U->>EP: release_funds(amount)
EP->>EP: read escrow_balance_before
EP->>T22: TransferChecked(from=escrow_ata, to=user_ata, amount, mint, decimals)
T22-->>T22: withhold fee at user_ata (destination)
T22-->>EP: ok
EP->>EP: read escrow_balance_after
EP->>EP: released = before - after
EP->>EP: update SMT root
EP->>EP: emit ReleaseFundsEvent(amount=released)
Reviews (1): Last reviewed commit: "fix: include program processor changes f..." | Re-trigger Greptile |
fix(escrow): support Token2022 TransferFeeConfig extension
Summary
Adds proper support for SPL Token 2022 mints with the
TransferFeeConfigextension in both thedepositandrelease_fundsinstructions.Previously, both processors used
Transfer(the basic Token2022 CPI), which is rejected by the SPL Token 2022 program with errorCustom(31)("Mint required for this account to transfer tokens, usetransfer_checked") whenever the mint has any extensions configured. Additionally, the processors assumed a 1:1 relationship between the requested amount and the tokens actually transferred — an assumption that breaks with transfer fee mints, where fees are withheld at the destination ATA.Changes
Program (
deposit.rs,release_funds.rs)Transfer→TransferChecked: Both processors now useTransferChecked, which requires passing themintaccount anddecimals. This is required by SPL Token 2022 for any mint with extensions.Balance-delta pattern: Instead of asserting
balance_after == balance_before ± args.amount(which fails when fees are withheld), both processors now compute the actual transferred amount from the observed balance change:received = escrow_balance_after - escrow_balance_beforereleased = escrow_balance_before - escrow_balance_afterThe delta is used in the emitted event and replaces the strict equality check that was previously returning
InvalidEscrowBalance.Tests (
test_deposit/mod.rs,test_release_funds/mod.rs,utils.rs)Added
create_mint_2022_with_transfer_feehelper inutils.rsthat creates a Token2022 mint withTransferFeeConfigusing real on-chain instructions (create_account→initialize_transfer_fee_config→initialize_mint). The order matters — the extension must be initialized before the base mint.Fixed
get_token_balanceinutils.rsto useStateWithExtensions::<Token2022Account>::unpackinstead ofToken2022Account::unpack. The plain unpack fails on accounts that have theTransferFeeAmountextension attached (which is added automatically to every ATA for a fee-bearing mint).Added
test_deposit_token_2022_transfer_fee_successandtest_release_funds_token_2022_transfer_fee_success.Note on test assertions: These two tests do not use the shared
assert_get_or_deposit/assert_deposit_balanceshelpers used by the rest of the suite. Those helpers assert that the escrow balance increases by exactlydeposit_amount, which is incorrect for fee-bearing mints (escrow only receivesamount - fee). The transfer fee tests use inline balance assertions with explicit fee calculations instead:The release funds test also raises the compute unit limit to
1_200_000(vs the default200_000) because SMT proof verification is expensive enough to exceed the default budget.Coverage Report