Feat/support permanent delegate and pausable extensions + block transfer hook [EXO-7]#104
Conversation
…t-delegate-and-pausable-extensions
Greptile SummaryThis PR flips the Token-2022 extension policy:
Confidence Score: 3/5Safe to merge after fixing the closed-ATA error handling in get_ata_balance; remaining findings are P2 and non-blocking. One P1 finding: when a permanent delegate fully closes the escrow ATA, get_ata_balance surfaces an RpcError instead of returning 0, causing the operator to classify the failure as Transient and restart indefinitely rather than routing to ManualReview. This directly affects the correctness of the permanent-delegate pre-flight that is the central new feature of this PR. indexer/src/operator/utils/mint_util.rs — get_ata_balance error handling for missing ATA; contra-escrow-program/program/src/error.rs + indexer/src/operator/utils/transaction_util.rs — verify deployment ordering for renumbered error codes. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[process_release_funds receives DbTransaction] --> B[build_release_funds\nwarms MintCache]
B -->|InvalidBuilder| C[halt_withdrawal_pipeline]
B -->|Ok| D[check_withdrawal_preflights]
D --> E{Token-2022 mint?}
E -->|No SPL Token| F[Skip pre-flight → dispatch]
E -->|Yes| G[get_extension_flags\nCache → DB → RPC + write-back]
G --> H{is_pausable?}
H -->|Yes| I[check_paused via live RPC]
I -->|paused=true| J[quarantine_single → ManualReview]
I -->|paused=false| K{has_permanent_delegate?}
H -->|No| K
K -->|Yes| L[get_ata_balance via live RPC]
L -->|AccountNotFound → RpcError| M[⚠️ Transient restart loop]
L -->|on_chain < amount| J
L -->|on_chain >= amount| F
K -->|No| F
F --> N[Scheduled rotation if boundary nonce]
N --> O[Dispatch to sender]
|
…t-delegate-and-pausable-extensions
Support Pausable + Permanent-Delegate Token-2022 Extensions
Summary
Accept Token-2022
PausableConfigandPermanentDelegatemints (previously rejected on-chain). The withdrawal operator pre-flights eachReleaseFunds: if the mint is paused, or a permanent delegate has drained the escrow ATA below the withdrawal amount, the row is routed toManualReviewand a webhook fires.Also blocks the
TransferHookextension on-chain — honoring hooks needsExtraAccountMetaListresolution in the CPI, which the pinocchioTransferCheckedbuilder does not support.Changes
On-chain:
validate_token2022_extensionsaccepts pausable + permanent-delegate, rejects transfer-hook. Removed now-dead error variants; enum renumbered; generated clients regenerated.Indexer:
DbMintgainsis_pausable/has_permanent_delegate(lazy RPC resolution + DB write-back). NewTransactionStatus::ManualReview.check_withdrawal_preflightsruns afterbuild_release_funds, short-circuits for non-2022 mints, and bails viaquarantine_singlefor paused / drained mints.Tests: LiteSVM coverage for all three extensions (accept / accept / block), indexer unit tests for the pre-flight, and two E2E tests (
pausable_mint_integration,permanent_delegate_mint_integration).Why the split
Pause state and delegate balance change over time, so static on-chain rejection cannot catch them — off-chain live checks are the only option. Transfer hooks would fail at CPI time anyway, so rejecting at
AllowMint/Depositavoids burning fees.Coverage Report