Skip to main content
A callback runs in its own receipt. If it panics, all state changes made by that callback are reverted, and any later transfer is never scheduled. Do not panic when an expected cross-contract call fails. Settle the operation instead.

The vulnerable pattern

This callback tries to remove a pending mint and refund the user after the mint fails:
Pseudocode: vulnerable flow
The panic aborts the callback. The pending mint is restored because its removal is reverted, and the transfer line never runs.

How the issue happens

  1. The contract calls another contract to mint for a user, attaching NEAR tokens.
  2. The mint fails, so the attached NEAR tokens return to the calling contract.
  3. mint_callback starts, removes the pending operation, then panics.
  4. The entire callback receipt is reverted: the pending operation remains and no refund transfer is scheduled.
The user has neither the mint nor their NEAR tokens back. Repeating the operation can make the state even harder to settle.

The safe pattern

Handle the failure and return normally:
Pseudocode: safe flow
Now the cleanup persists and the refund is scheduled. If the refund needs more checks or gas than this callback can safely provide, credit a withdrawable balance instead.

General rule

Use panic for invalid input or a broken invariant before changing state. In a callback’s expected failure path, record the outcome, clean up, and refund or otherwise settle the operation without panicking. See refund NEAR tokens for the refund pattern and private callbacks for callback access control.