Skip to content
Launch App >

If a deposit transaction succeeds on-chain but fails to be indexed, you can revert it to recover your funds. This is a safety mechanism for edge cases.

Revert is needed when:

  • Deposit transaction confirmed but not indexed
  • Indexer missed your deposit
  • Network issues prevented indexing

Revert is NOT needed when:

  • Transaction failed (funds never left your wallet)
  • You successfully withdrew (nullifier spent)
  • Deposit is indexed but you haven’t withdrawn yet
  1. You prove you made the original deposit
  2. You prove the nullifier hasn’t been spent
  3. Program returns your funds minus fees
  1. Check Deposit Status

    First, verify your deposit wasn’t indexed:

    async function checkDepositIndexed(
    mint: string,
    commitment: Uint8Array,
    network: string = 'mainnet'
    ): Promise<boolean> {
    // Try to get the leaf index for your commitment
    // If not found, deposit wasn't indexed
    try {
    const response = await fetch(
    `${API_BASE}/get_merkle/${mint}/32/${network}`,
    { method: 'POST' }
    );
    const data = await response.json();
    // Search for commitment in tree
    // Implementation depends on indexer API
    return commitmentExistsInTree(data, commitment);
    } catch {
    return false;
    }
    }
  2. Check Nullifier Not Spent

    Ensure you haven’t already withdrawn:

    const nullifierHash = poseidon([nullifier]);
    const isSpent = await isNullifierSpent(mint, nullifierHash, network);
    if (isSpent) {
    throw new Error('Already withdrawn - cannot revert');
    }
  3. Sign Revert Message

    Create and sign the revert authorization:

    async function signRevertMessage(
    wallet: { publicKey: PublicKey; signMessage: Function },
    txSignature: string
    ): Promise<{ signature: string; message: string }> {
    const message = `Revert deposit: ${txSignature}`;
    const messageBytes = new TextEncoder().encode(message);
    const signatureBytes = await wallet.signMessage(messageBytes);
    const signature = bs58.encode(signatureBytes);
    return { signature, message };
    }
  4. Call Revert Endpoint

    interface RevertRequest {
    wallet: string;
    signature: string;
    message: string;
    }
    async function revertDeposit(
    authToken: string,
    txSignature: string,
    wallet: string,
    signature: string,
    message: string,
    network: string = 'mainnet'
    ): Promise<string> {
    const response = await fetch(
    `${API_BASE}/revert/${network}/${authToken}/${txSignature}`,
    {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
    wallet,
    signature,
    message,
    }),
    }
    );
    if (!response.ok) {
    const error = await response.text();
    throw new Error(`Revert failed: ${error}`);
    }
    return response.text(); // Returns tx signature
    }
async function performRevert(
deposit: DepositSecrets,
originalTxSignature: string,
wallet: { publicKey: PublicKey; signMessage: Function },
authToken: string,
network: string = 'mainnet'
): Promise<string> {
// 1. Verify deposit wasn't indexed
const isIndexed = await checkDepositIndexed(
deposit.mint,
deposit.commitment,
network
);
if (isIndexed) {
throw new Error('Deposit is indexed - use normal withdraw');
}
// 2. Verify not already spent
const nullifierHash = poseidon([deposit.nullifier]);
const isSpent = await isNullifierSpent(deposit.mint, nullifierHash, network);
if (isSpent) {
throw new Error('Already withdrawn');
}
// 3. Sign revert message
const { signature, message } = await signRevertMessage(
wallet,
originalTxSignature
);
// 4. Call revert
const revertTxSig = await revertDeposit(
authToken,
originalTxSignature,
wallet.publicKey.toBase58(),
signature,
message,
network
);
// 5. Clean up local storage
deleteDepositRecord(originalTxSignature);
return revertTxSig;
}

Revert returns your deposit minus fees:

FeeAmount
Revert FeeSame as deposit fee

You receive: originalDeposit - revertFee

ErrorCauseSolution
DepositNotFoundTx doesn’t existCheck transaction signature
AlreadyIndexedDeposit was indexedUse normal withdraw
NullifierSpentAlready withdrawnNo action needed
InvalidSignatureWrong walletSign with depositing wallet
NotDepositorWallet didn’t make depositCan only revert own deposits

Revert breaks privacy for that specific deposit:

  • On-chain link between your wallet and the deposit
  • Anyone can see you made that deposit and reverted it

Revert may have time limits:

  • Must be done before the deposit is indexed
  • Once indexed, use normal withdrawal

You must sign with the same wallet that made the original deposit. The program verifies ownership.

Before reverting, check:

  • Transaction confirmed on-chain?
  • Deposit not in Merkle tree?
  • Nullifier not spent?
  • Have original transaction signature?
  • Have access to depositing wallet?