How to Calculate Funds at Risk

When submitting a bug report, the most important step a whitehat can take is to prove the magnitude of the bug’s impact.

The higher the real and immediate impact, the higher the payout. It’s that simple.

In this article, we’ll show you how to determine the funds at risk for bugs in projects on EVM-based blockchains. But first, let’s discuss how impacts and vulnerabilities work on Immunefi.

Impact is defined according to the severity classification scale that the bug bounty program on Immunefi subscribes to, and you can find that scale in the Rewards by Threat Level section on each program page. Sometimes they have different versions.

Here’s an example of the latest severity classification scale: version 2.3.

Once you have the definition of impact for all severity levels, you need to prove that the vulnerability described in your bug report meets that definition with a Proof of Concept (PoC).

There are always at least three questions used to assess bug reports where whitehats claim “direct loss of funds”, which is an impact listed at the critical severity level:

  1. Is the impact and asset in-scope for the bug bounty program? If the asset isn’t in-scope, does the project subscribe to Primacy of Impact?
  2. Is there an actual, direct loss of funds? Does the PoC do what it claims to do?
  3. What is the impact today? Not a week from now or a month from now, but what is the impact today? How much is your PoC able to drain from the project right now?

The first question establishes that the bug is in-scope for the program.

The second question establishes that the bug is indeed critical.

The third question establishes what the payout amount will be. You can observe that various bug bounty programs have different methods for calculating payout once it’s established that a bug is critical or some other severity level.

Sometimes there are minimum payout amounts listed for a critical severity bug. This means that even if there is no TVL in the contract yet, as long as the asset is in scope and the bug meets the definition of critical, then the whitehat should receive the minimum payout amount.

Usually, there’s a payout range for different severity levels. A payout range for a critical bug could be anywhere from $100,000 — $500,000, to give just one example.

So how do you get $500,000 instead of $100,000? You prove that your in-scope critical bug has a massive, real, and immediate impact by demonstrating with a PoC (forked local copy of the blockchain) precisely how much in funds are at risk of exploitation. The bounty program page will state how extensive the impact has to be to merit the $500,000 payout. Sometimes the calculation is simple: the payout is capped at 10% of funds at risk. So if you want the $500,000 payout, you’d have to definitively prove that $5,000,000 is at real, immediate risk.

With this background understanding, we can now demonstrate how to calculate funds at risk using Dune Analytics for EVM-based blockchains.

For non-EVM-based chains that are not supported by Dune, you can determine the funds at risk by analyzing the project’s TVL on DefiLlamaCoinGecko, or looking at the funds stored in the contract through a blockchain explorer.

What is Dune Analytics

Dune Analytics is a blockchain analysis tool for querying and visualizing data from blockchains like Ethereum, Binance, etc. Dune uses an SQL-like language to query the data from the blockchain, which makes it easier for other users to query the data from the blockchain.

In essence, Dune aggregates all of the popular blockchain data (including transactions and events) and makes it query-able for the user.

Currently, there are multiple attack vectors that could lead to an exploit, whether it’s from an approval logic bug, reentrancy bug, or a logic issue in the smart contract that could lead to loss of funds.

There are multiple methods that we can use to calculate funds at risk. The most common queries are:

  • Calculating the current balance of the address.
  • Calculating the balance of the contract at a specific time.
  • Calculating the trade volume of an NFT.
  • Calculating how many calls that user made to a specific function.
  • Calculating the funds at risk, based on the current price.
  • Determining unused approval.

We’ll write a Dune query for these exact use cases.

Calculating the current balance of the address

For calculating the current balance of an address, we have to aggregate the outbound and inbound transactions of the address and get the sum from all of those transactions.

Example (https://dune.com/queries/2388006):

with TokenIn as (
    SELECT 
        cast(value as int256) as "value"
    FROM erc20_ethereum.evt_Transfer
    WHERE "contract_address" = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 -- Token Contract
    AND "to" = 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640 -- Target Contract
)
, TokenOut as (
    SELECT 
        -cast(value as int256) as "value"
    FROM erc20_ethereum.evt_Transfer
    WHERE "contract_address" = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 -- Token Contract
    AND "from" = 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640 -- Target Contract
)

SELECT
    sum(value) / power(10, 18) as "total_token"
FROM (
    SELECT value FROM TokenIn
    UNION ALL
    SELECT value FROM TokenOUT
    ) as value

Calculating balance of the contract at a specific time

To calculate the total amount of tokens in the target contract at a specific time, we just have to modify our query a little bit by adding the time constraint for each of the subqueries (outbound and inbound).

Example (https://dune.com/queries/2388834):

with TokenIn as (
    SELECT 
        cast(value as int256) as "value"
    FROM erc20_ethereum.evt_Transfer
    WHERE "contract_address" = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 -- Token Contract
    AND "to" = 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640 -- Target Contract
    AND evt_block_time < timestamp '2023-01-1'
)
, TokenOut as (
    SELECT 
        -cast(value as int256) as "value"
    FROM erc20_ethereum.evt_Transfer
    WHERE "contract_address" = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 -- Token Contract
    AND "from" = 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640 -- Target Contract
    AND evt_block_time < timestamp '2023-01-1'
)

SELECT
    sum(value) / power(10, 6) as "total_token"
FROM (
    SELECT value FROM TokenIn
    UNION ALL
    SELECT value FROM TokenOUT
    ) as value

Calculating the trade volume of an NFT

Conveniently, Dune already provides the data table for all of the trades that were made to date, So, we can just query the data by taking it from the `nft.trades` table. In this example, we will try to calculate the trading volume of the popular NFT collection Bored Ape Yacht Club (BAYC).

Example (https://dune.com/queries/2390211):

SELECT
    sum(amount_usd) as "amount in USD"
FROM nft.trades
WHERE nft_contract_address = 0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D

Calculating how many calls that user made to a specific function

Sometimes, a smart contract doesn’t emit an event for an important function, and this function is crucial for us to determine the funds at risk. Wat we can do in this case is to query the data based on the function signature of that important function. In this example, we will try to get all of the `mintApe` function call that was made to the BAYC NFT contract.

Example (https://dune.com/queries/2390224):

SELECT
    *
FROM ethereum.transactions
WHERE "to" = '\xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D'
AND encode("data", 'hex') like '%a723533e%' – mintApe function signature

Calculating the funds at risk, based on the current price

We already learn how to calculate the funds at risk based on the total tokens in the contract. Due to some fluctuations in the token price, sometimes our calculations might go awry when we try to convert those tokens to USD. Thankfully, Dune already provides some price data in USD that we can integrate with our query. In this example, we will try to get the total WETH in the target contract, and with the `prices.usd` tables from Dune, we will convert the WETH to its current USD value.

Example (https://dune.com/queries/2390251):

with TokenIn as (
    SELECT 
        cast(value as int256) as "value"
    FROM erc20_ethereum.evt_Transfer
    WHERE "contract_address" = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 -- Token Contract
    AND "to" = 0x582E3DA39948C6339433008703211aD2c13EB2ac -- Target Contract
)
, TokenOut as (
    SELECT 
        -cast(value as int256) as "value"
    FROM erc20_ethereum.evt_Transfer
    WHERE "contract_address" = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 -- Token Contract
    AND "from" = 0x582E3DA39948C6339433008703211aD2c13EB2ac -- Target Contract
), totalToken as (
    SELECT
        1 as "hook",
        sum(value) as "total_token"
    FROM (
        SELECT value FROM TokenIn
        UNION ALL
        SELECT value FROM TokenOUT
        ) as value
), tokenPrice as (
    SELECT
        1 as "hook",
        price,
        decimals
    FROM prices.usd
    WHERE contract_address = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 -- Token address
    ORDER BY minute desc
    LIMIT 1
)

SELECT
    total_token / power(10, decimals) as "Total Tokens",
    (total_token / power(10, decimals)) * price as "Total Tokens in USD"
FROM totalToken a
JOIN tokenPrice b 
ON a.hook = b.hook

Determining unused approval

Some bugs may rely on an approval that was left unused by the user and not zeroed after being used. This could lead to an attacker spending the unused approval to steal user tokens. By using the approval event table from Dune, we can check which addresses have unused approval, thereby determining which addresses are vulnerable to the approval logic bug.

Example (https://dune.com/queries/2406988):

SELECT 
    MAX(evt_block_time) as "event_block_time",
    owner,
    spender,
    value,
    contract_address,
    evt_tx_hash
FROM erc20_ethereum.evt_Approval
WHERE spender = 0xd89a09084555a7d0abe7b111b1f78dfeddd638be -- Target Contract
AND contract_address = 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 -- Token Address
AND value != cast(0 as UINT256)
GROUP BY owner, evt_block_time, value, contract_address, spender, evt_tx_hash

What we learned

There are multiple ways to calculate the funds at risk from a smart contract vulnerability. All of these queries, however, only touch the most common cases that we encounter when calculating funds at risk. It may vary depending on the exploit scenario that you have.

We hope this article will amplify your knowledge of funds at risk calculations and help you score a bigger payout in the future.

See you on the leaderboard.

This article was written by Omik, an Immunefi Smart Contract Triager.