Skip to content

Commit f483327

Browse files
committed
Rewrite README with professional formatting
1 parent 314bfc8 commit f483327

1 file changed

Lines changed: 155 additions & 21 deletions

File tree

README.md

Lines changed: 155 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,193 @@
1+
<div align="center">
2+
13
# FundForwarder
24

3-
A Solidity smart contract that receives ETH deposits and automatically forwards them to a predefined target wallet. Built with Hardhat and OpenZeppelin v5.
5+
**A non-custodial ETH forwarding smart contract built on Ethereum**
6+
7+
[![Solidity](https://img.shields.io/badge/Solidity-0.8.20-363636?logo=solidity)](https://soliditylang.org/)
8+
[![Hardhat](https://img.shields.io/badge/Built%20with-Hardhat-yellow)](https://hardhat.org/)
9+
[![OpenZeppelin](https://img.shields.io/badge/OpenZeppelin-v5.0.2-4E5EE4?logo=openzeppelin)](https://www.openzeppelin.com/contracts)
10+
[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
11+
[![Tests](https://img.shields.io/badge/Tests-32%20passing-brightgreen)]()
12+
[![Coverage](https://img.shields.io/badge/Coverage-100%25%20lines-brightgreen)]()
13+
14+
</div>
15+
16+
---
17+
18+
## Overview
19+
20+
FundForwarder is a production-ready Solidity smart contract that accepts ETH deposits and instantly forwards them to a designated target wallet. It is designed for scenarios where a fixed, public-facing deposit address is needed while retaining the flexibility to update the destination — all secured by timelocks, two-step ownership, and pause controls.
21+
22+
### How It Works
23+
24+
```
25+
Sender ──► [ FundForwarder Contract ] ──► Target Wallet
26+
27+
├─ Validates minimum deposit
28+
├─ Emits Deposit event
29+
├─ Forwards full balance
30+
└─ Emits Forwarded event
31+
```
32+
33+
---
434

535
## Features
636

7-
- Automatic ETH forwarding on deposit
8-
- Configurable minimum deposit amount (capped at 10 ETH)
9-
- Time-locked target wallet changes (24-hour delay)
10-
- Pausable deposits for emergency scenarios
11-
- Owner fund recovery
12-
- Two-step ownership transfer (Ownable2Step)
13-
- Reentrancy protection
37+
| Feature | Description |
38+
|---|---|
39+
| **Instant Forwarding** | ETH is forwarded to the target wallet in the same transaction as the deposit |
40+
| **Minimum Deposit** | Configurable threshold to filter dust/spam transactions (capped at 10 ETH) |
41+
| **Time-locked Wallet Changes** | 24-hour mandatory delay before a new target wallet takes effect |
42+
| **Two-step Ownership (Ownable2Step)** | Ownership transfers require explicit acceptance, preventing irreversible mistakes |
43+
| **Pausable** | Owner can halt all deposits during emergencies or maintenance |
44+
| **Emergency Recovery** | Owner can withdraw any ETH stuck in the contract |
45+
| **Reentrancy Protection** | Guards against reentrancy on deposit and recovery paths |
46+
47+
---
48+
49+
## Architecture
50+
51+
```
52+
contracts/
53+
├── FundForwarder.sol # Main contract
54+
└── test/
55+
├── ForceFeeder.sol # Test helper — force-sends ETH
56+
└── Reverter.sol # Test helper — always-reverting receiver
57+
```
58+
59+
### Contract Inheritance
60+
61+
```
62+
Ownable ─► Ownable2Step ─┐
63+
Pausable ─────┤── FundForwarder
64+
ReentrancyGuard ────┘
65+
```
66+
67+
### Access Control
68+
69+
| Function | Access | Description |
70+
|---|---|---|
71+
| `receive()` | Public | Accept and forward ETH deposits |
72+
| `fallback()` | Public | Rejects calls with calldata |
73+
| `requestTargetWalletChange()` | Owner | Initiate a wallet change (starts 24h timelock) |
74+
| `finalizeTargetWalletChange()` | Owner | Apply the pending wallet change after timelock |
75+
| `cancelTargetWalletChange()` | Owner | Cancel a pending wallet change |
76+
| `updateMinDeposit()` | Owner | Update the minimum deposit threshold |
77+
| `pause()` / `unpause()` | Owner | Toggle deposit acceptance |
78+
| `recoverFunds()` | Owner | Withdraw stuck ETH to the owner |
79+
| `transferOwnership()` | Owner | Initiate two-step ownership transfer |
80+
| `acceptOwnership()` | Pending Owner | Accept ownership transfer |
81+
82+
---
83+
84+
## Getting Started
85+
86+
### Prerequisites
1487

15-
## Setup
88+
- [Node.js](https://nodejs.org/) >= 18
89+
- [npm](https://www.npmjs.com/) >= 9
90+
91+
### Installation
1692

1793
```bash
94+
git clone https://github.com/SP1R4/FundsForwarder.git
95+
cd FundsForwarder
1896
npm install
97+
```
98+
99+
### Environment Setup
100+
101+
```bash
19102
cp .env.example .env
20-
# Fill in .env with your keys
21103
```
22104

23-
## Compile
105+
Fill in your `.env`:
106+
107+
```env
108+
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY
109+
PRIVATE_KEY=your_deployer_private_key
110+
ETHERSCAN_API_KEY=your_etherscan_key
111+
TARGET_WALLET=0xYourTargetWalletAddress
112+
REPORT_GAS=false
113+
```
114+
115+
> **Warning:** Never commit your `.env` file. It is excluded via `.gitignore`.
116+
117+
---
118+
119+
## Usage
120+
121+
### Compile
24122

25123
```bash
26124
npx hardhat compile
27125
```
28126

29-
## Test
127+
### Test
30128

31129
```bash
32130
npx hardhat test
33131
```
34132

35-
## Coverage
133+
### Coverage
36134

37135
```bash
38136
npx hardhat coverage
39137
```
40138

41-
## Gas Report
139+
### Gas Report
42140

43141
```bash
44142
REPORT_GAS=true npx hardhat test
45143
```
46144

47-
## Deploy
145+
### Deploy to Sepolia
48146

49147
```bash
50148
npx hardhat run scripts/deploy.js --network sepolia
51149
```
52150

53-
## Security Model
151+
The deploy script reads `TARGET_WALLET` from `.env` and automatically verifies the contract on Etherscan after deployment.
152+
153+
---
154+
155+
## Test Suite
156+
157+
32 tests across 8 categories with **100% line and function coverage**.
158+
159+
```
160+
FundForwarder
161+
Deployment (6 tests)
162+
Deposits & Forwarding (4 tests)
163+
Wallet Change Flow (8 tests)
164+
MinDeposit Updates (4 tests)
165+
Pause / Unpause (3 tests)
166+
Recovery (3 tests)
167+
Ownable2Step (2 tests)
168+
Fallback (1 test)
169+
Edge Cases (1 test)
170+
171+
32 passing
172+
```
173+
174+
---
175+
176+
## Security Considerations
177+
178+
- **Timelock on wallet changes** — A mandatory 24-hour delay between requesting and finalizing a target wallet change prevents instant fund redirection in the event of a compromised owner key, giving stakeholders time to respond.
179+
- **Ownable2Step** — Prevents ownership from being accidentally transferred to an incorrect or inaccessible address. The new owner must explicitly call `acceptOwnership()`.
180+
- **MAX_MIN_DEPOSIT cap** — The minimum deposit is bounded at 10 ETH to prevent misconfiguration that could lock the contract from receiving funds.
181+
- **No calldata accepted** — The `fallback()` function reverts on any call with data, reducing the attack surface to only plain ETH transfers.
182+
- **Reentrancy guards** — Applied to both `receive()` and `recoverFunds()` to prevent reentrancy exploits.
183+
184+
### Known Limitations
185+
186+
- The contract does not support ERC-20 token forwarding — ETH only.
187+
- `selfdestruct`-based force-sends can deposit ETH below `minDeposit` without triggering forwarding. The `recoverFunds()` function handles this case.
188+
189+
---
190+
191+
## License
54192

55-
- **Ownable2Step**: Ownership transfers require explicit acceptance by the new owner, preventing accidental transfers to wrong addresses.
56-
- **Timelock**: Target wallet changes require a 24-hour waiting period, giving stakeholders time to react to unauthorized changes.
57-
- **Pausable**: The owner can pause deposits in case of emergency.
58-
- **ReentrancyGuard**: Prevents reentrancy attacks on deposit and recovery functions.
59-
- **MAX_MIN_DEPOSIT**: Caps the minimum deposit at 10 ETH to prevent owner misconfiguration.
193+
This project is licensed under the [ISC License](https://opensource.org/licenses/ISC).

0 commit comments

Comments
 (0)