Reproduced Exploit

FIVA YTMinter — a shared redeem deposit lets anyone finish the position

A paired PT+YT redemption is stored under a key of (minter, recipient) only. Eve names Alice as the recipient and supplies 1 nano YT. The deposit finalizes min(1000000000, 1) = 1, mints 1 SY to Alice, and pushes 999999999 PT back. The deposit is then destroyed, so Alice's own finishRedeem of the or…

May 2025•Other•access-control•4 min read

Chain

Other

Category

access-control

Date

May 2025

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 57083-an-attacker-can-prevent-the-redemption-of-yt-and-pt-tokens-f. Standalone Foundry PoC and full write-up: 57083-an-attacker-can-prevent-the-redemption-of-yt-and-pt-tokens-f_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/access-control/missing-auth · vuln/logic/missing-check · vuln/dos/griefing

Reproduction: a self-contained Foundry PoC that compiles and runs offline with only forge-std — no fork and no RPC. Trace: output.txt. PoC: test/57083-an-attacker-can-prevent-the-redemption-of-yt-and-pt-tokens-f_exp.sol.


Key info#

ImpactHIGH — Alice's redemption of 1000000000 PT pays 1 SY, and her follow-up redeem reverts
ProtocolFIVA yield tokenization, YTMinter / RedeemDeposit
Vulnerable codedepositKey hashes (minter, recipient) and omits the jetton sender
Bug classMissing sender in the redeem-deposit init state
FindingTrail of Bits — Tarun Bansal, May 2025 · id 57083
Report2025-05 FIVA review (PDF)
SourceAuditVault finding
StatusReproduced as a local Solidity reduction of the FunC init-state bug
Compilersolc 0.8.27, cancun, via IR

TL;DR#

A paired PT+YT redemption is stored under a key of (minter, recipient) only. Eve names Alice as the recipient and supplies 1 nano YT. The deposit finalizes min(1000000000, 1) = 1, mints 1 SY to Alice, and pushes 999999999 PT back. The deposit is then destroyed, so Alice's own finishRedeem of the original amount reverts.

Including msg.sender in the key gives Eve her own deposit. Alice's two legs then pay 1000000000 SY.

The vulnerable code#

The FunC helper is calculate_redeem_deposit_state_init(my_address(), recipient_addr, code). The sender of the jetton is not an argument. The reduction keeps that hash:

SOLIDITY
function depositKey(address recipient) public view returns (bytes32) {
    // @> VULN: init_state is (minter, recipient). The sender is omitted, so any caller hits this deposit.
    return keccak256(abi.encode(address(this), recipient));
}

When both legs are non-zero the deposit redeems the minimum and destroys itself, which is the register_jetton handler in contracts/YT/redeem/deposit.fc.

Root cause#

The temporary redeem account is addressable by anyone who knows the recipient. The first honest leg opens it. The second leg, from any caller, closes it at min(pt, yt).

Preconditions#

  • Alice has registered 1000000000 PT and has not yet sent the YT leg.
  • Eve can call registerJetton with Alice as the recipient and 1 YT.

Attack walkthrough#

  1. Alice's wallet registers 1000000000 PT. The key is (minter, Alice).
  2. Eve registers 1 YT with the same recipient. Both balances are non-zero.
  3. Finalize mints 1 SY and returns 999999999 PT. The deposit is marked destroyed.
  4. Alice calls finishRedeem for her YT. It reverts with redeem closed.

Control: the key is (minter, recipient, msg.sender). Eve's 1 YT sits on her own deposit. Alice then delivers her own YT and receives 1000000000 SY.

Diagrams#

flowchart TD A["Alice registers 1000000000 PT under a key of minter and recipient"] --> B["Eve registers 1 YT with Alice as the recipient"] B --> C["The shared deposit finalizes the minimum"] C --> D["Alice is paid 1 SY and 999999999 PT is pushed back"] D --> E["Alice's follow-up redeem reverts because the deposit was destroyed"]

Impact#

The N-sized redemption does not pay N. Alice receives 1 SY instead of 1000000000, 999999999 PT is forced back, and the follow-up call reverts. The fixed key pays 1000000000 SY and leaves Eve's 1 YT on a separate deposit.

Remediation#

Put the jetton sender in the redeem-deposit init state, so a deposit is unique to (minter, recipient, sender).

Sources#

Taxonomy#

  • severity/high
  • vuln/access-control/missing-auth · vuln/logic/missing-check · vuln/dos/griefing

Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.