# IOP \_ ThunderNFT 34455 - \[Smart Contract - Low] Double Token Vulnerability leads to drain funds

Submitted on Tue Aug 13 2024 04:28:52 GMT-0400 (Atlantic Standard Time) by @bugtester for [IOP | ThunderNFT](https://immunefi.com/bounty/thundernft-iop/)

Report ID: #34455

Report type: Smart Contract

Report severity: Low

Target: <https://github.com/ThunderFuel/smart-contracts/tree/main/contracts-v1/pool>

Impacts:

* Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

## Description

## Brief/Intro

The vulnerability resided within the smart contract's \_transfer function, which is responsible for handling balance update. This function unfortunately relied on cached balance values for both the sender and recipient of the tokens. This mechanism created a loophole , leading to a double token vulnerability.

<https://github.com/ThunderFuel/smart-contracts/blob/260c9859e2cd28c188e8f6283469bcf57c9347de/contracts-v1/pool/src/main.sw#L237C1-L257C2>

```
fn _transfer(from: Identity, to: Identity, asset: AssetId, amount: u64) {
    require(
        to != ZERO_IDENTITY_ADDRESS &&
        to != ZERO_IDENTITY_CONTRACT,
        PoolErrors::IdentityMustBeNonZero
    );

    let from_balance = _balance_of(from, asset);
    let to_balance = _balance_of(to, asset);
    require(from_balance >= amount, PoolErrors::AmountHigherThanBalance);

    storage.balance_of.insert((from, asset), from_balance - amount);
    storage.balance_of.insert((to, asset), to_balance + amount);

    log(Transfer {
        from,
        to,
        asset,
        amount,
    });
}
```

## Impact

This vulnerability can lead to an incorrect cache balance state, potentially allowing attacker to duplicate tokens and exploit the contract for drain funds

## References

few months back Minerercx was exploit with same issue - <https://techfund.jp/en/media/Miner-Vulnerability-Exploited>

## Proof of concept

## Proof of Concept

Steps to Reproduce:

Call the \_transfer function with the same from and to identity. Observe that the balance is incremented twice, leading to token duplication. due to cache balance

### example

> The transferfunction was triggered with the from and to addresses being identical, signifying a self-transfer.

> Before the actual transfer took place, the function retrieved the cached balance values for both the sender and recipient (which was the same address in this case).

```
let from_balance = _balance_of(from, asset);
let to_balance = _balance_of(to, asset);
```

> The function then subtracted the transfer amount from the cached sender balance and added the same amount to the cached recipient balance.

> Since both cached values pointed to the same address, this operation essentially doubled the user's balance, leading to the exploitation

### fix

```
fn _transfer(from: Identity, to: Identity, asset: AssetId, amount: u64) {
    require(
        to != ZERO_IDENTITY_ADDRESS &&
        to != ZERO_IDENTITY_CONTRACT,
        PoolErrors::IdentityMustBeNonZero
    );
    
    // Check if from and to identities are the same
    if from == to {
        // If the same, ensure the balance is sufficient and log without updating
        let balance = _balance_of(from, asset);
        require(balance >= amount, PoolErrors::AmountHigherThanBalance);
        log(Transfer {
            from,
            to,
            asset,
            amount,
        });
        return;
    }

    let from_balance = _balance_of(from, asset);
    let to_balance = _balance_of(to, asset);
    require(from_balance >= amount, PoolErrors::AmountHigherThanBalance);

    storage.balance_of.insert((from, asset), from_balance - amount);
    storage.balance_of.insert((to, asset), to_balance + amount);

    log(Transfer {
        from,
        to,
        asset,
        amount,
    });
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://reports.immunefi.com/thundernft-or-iop/iop-_-thundernft-34455-smart-contract-low-double-token-vulnerability-leads-to-drain-funds.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
