ft_on_transfer and ft_resolve_transfer.
ft_on_transfer: accept only expected tokens
ft_on_transfer executes on the receiving contract. Its predecessor_account_id is the FT contract that transferred the tokens; sender_id is the account that initiated ft_transfer_call.
The vulnerable pattern
This receiver credits every token contract that calls it:Pseudocode: vulnerable flow
The safe pattern
Check the predecessor before using the deposit. Return all of an unexpected token as unused so the FT contract refunds it to the sender:Pseudocode: safe flow
ft_resolve_transfer: refund only transferred tokens
ft_resolve_transfer executes on the FT contract itself after ft_on_transfer finishes. It must be private. On failure, the original amount should be refundable; on success, ft_on_transfer returns how many tokens it did not use.
The vulnerable pattern
Do not trust the requested refund without a bound:Pseudocode: vulnerable flow
The safe pattern
Cap the refund by the original amount and by the receiver’s current balance:Pseudocode: safe flow
near-contract-standards instead of implementing this accounting yourself.
Review before deployment
- In
ft_on_transfer, check that the predecessor is an allowed FT contract. - Return the unused amount:
0when all tokens were used, oramountwhen none were used. - Keep
ft_resolve_transferprivate. - Refund no more than the original transfer and no more than the receiver still holds.
- Test unexpected FT contracts, failed receiver calls, malformed return values, partial use, and a requested refund larger than the transfer.