Skip to content

Commit b79edd0

Browse files
committed
feat: Add RaffleForGood - Decentralized raffle system with Pyth Entropy
- Implements Pyth Entropy V2 for verifiable randomness - Factory pattern for easy raffle creation - Binary search O(log n) for winner selection - OpenZeppelin security patterns (ReentrancyGuard, PullPayment) - Deployed on Base Sepolia with 10+ active raffles - 0.05% platform fee for sustainable crowdfunding - Comprehensive README with deployment and usage examples
1 parent b48bc74 commit b79edd0

File tree

15 files changed

+1522
-0
lines changed

15 files changed

+1522
-0
lines changed

entropy/raffle-for-good/README.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# RaffleForGood - Decentralized Raffle System with Pyth Entropy
2+
3+
> A transparent crowdfunding platform that gamifies donations through blockchain-based raffles using Pyth Entropy for verifiable randomness.
4+
5+
## 🎯 Overview
6+
7+
RaffleForGood transforms traditional crowdfunding by creating raffles where participants buy tickets to support projects while having a chance to win prizes. The system uses **Pyth Entropy** to ensure fair and verifiable random winner selection.
8+
9+
## ✨ Key Features
10+
11+
- **Pyth Entropy Integration**: Verifiable random number generation for fair winner selection
12+
- **Factory Pattern**: Easy raffle creation without code
13+
- **Gas Optimized**: Binary search O(log n) for winner selection
14+
- **Secure**: OpenZeppelin's PullPayment pattern for fund distribution
15+
- **Transparent**: All operations on-chain and auditable
16+
- **Low Fees**: Only 0.05% platform fee
17+
18+
## 🏗️ Architecture
19+
20+
```
21+
User → RaffleFactory.createRaffle()
22+
23+
ProjectRaffle Contract
24+
25+
buyTickets() → Pyth Entropy Request → entropyCallback()
26+
27+
Winner Selection (Binary Search) → Fund Distribution
28+
```
29+
30+
## 📋 Contracts
31+
32+
### `ProjectRaffle.sol`
33+
Main raffle contract that:
34+
- Manages ticket purchases (1 wei = 1 ticket)
35+
- Requests randomness from Pyth Entropy
36+
- Selects winner using binary search
37+
- Distributes funds via pull payment pattern
38+
39+
### `RaffleFactory.sol`
40+
Factory contract for creating multiple raffles with custom parameters.
41+
42+
### Pyth Entropy Integration
43+
44+
```solidity
45+
// Request entropy
46+
function requestEntropy(bytes32 userRandomNumber) external payable {
47+
uint256 fee = entropy.getFee(entropyProvider);
48+
entropySequenceNumber = entropy.request{value: fee}(
49+
entropyProvider,
50+
userRandomNumber,
51+
true // use blockhash
52+
);
53+
}
54+
55+
// Receive callback
56+
function entropyCallback(
57+
uint64 sequenceNumber,
58+
address provider,
59+
bytes32 randomNumber
60+
) external override {
61+
require(msg.sender == address(entropy), "Only entropy contract");
62+
winner = _selectWinner(randomNumber);
63+
state = RaffleState.DrawExecuted;
64+
}
65+
```
66+
67+
## 🚀 Deployment
68+
69+
### Prerequisites
70+
71+
```bash
72+
npm install
73+
```
74+
75+
### Environment Variables
76+
77+
Create `.env`:
78+
```env
79+
PRIVATE_KEY=your_private_key
80+
BASE_SEPOLIA_RPC_URL=your_rpc_url
81+
ENTROPY_ADDRESS=0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c
82+
```
83+
84+
### Deploy Factory
85+
86+
```bash
87+
npx hardhat ignition deploy ignition/modules/RaffleFactoryModule.ts --network baseSepolia
88+
```
89+
90+
### Create a Raffle
91+
92+
```bash
93+
npx tsx scripts/createNewRaffle.ts
94+
```
95+
96+
## 📖 Usage Example
97+
98+
### 1. Create Raffle
99+
```typescript
100+
const tx = await factory.createRaffle(
101+
"Save the Ocean", // name
102+
"Help clean our oceans", // description
103+
3000, // 30% for project
104+
projectAddress, // beneficiary
105+
604800 // 7 days duration
106+
);
107+
```
108+
109+
### 2. Buy Tickets
110+
```typescript
111+
await raffle.buyTickets({ value: parseEther("0.01") });
112+
```
113+
114+
### 3. Close Raffle & Select Winner
115+
```typescript
116+
// Request entropy (owner/admin only)
117+
await raffle.requestEntropy(randomBytes32, { value: entropyFee });
118+
119+
// Pyth calls entropyCallback() automatically
120+
// Winner is selected using binary search
121+
```
122+
123+
### 4. Distribute Funds
124+
```typescript
125+
await raffle.distributeFunds();
126+
// Beneficiaries withdraw with withdrawPayments()
127+
```
128+
129+
## 🧪 Testing
130+
131+
```bash
132+
npm test
133+
```
134+
135+
Tests include:
136+
- Ticket purchasing and range calculation
137+
- Entropy request and callback simulation
138+
- Winner selection accuracy
139+
- Fund distribution correctness
140+
- Edge cases and security scenarios
141+
142+
## 🎲 Pyth Entropy Benefits
143+
144+
1. **Verifiable Randomness**: Cryptographically secure and verifiable on-chain
145+
2. **Tamper-Proof**: No party can manipulate the outcome
146+
3. **Cost-Effective**: Low fee (~0.0001 ETH per request)
147+
4. **Fast**: Callback within 2-5 minutes
148+
5. **Reliable**: Backed by Pyth's oracle network
149+
150+
## 📊 Live Deployment
151+
152+
- **Network**: Base Sepolia
153+
- **Factory**: `0x104032d5377be9b78441551e169f3C8a3d520672`
154+
- **Entropy**: `0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c`
155+
- **Active Raffles**: 10+
156+
157+
## 🔐 Security Features
158+
159+
- ReentrancyGuard on all state-changing functions
160+
- PullPayment pattern prevents reentrancy in withdrawals
161+
- Only authorized addresses can request entropy
162+
- Time-locked raffle closure (optional)
163+
- Comprehensive input validation
164+
165+
## 💡 Use Cases
166+
167+
- **Open Source Projects**: Fund development while rewarding community
168+
- **Social Causes**: Transparent fundraising for NGOs
169+
- **Content Creators**: Monetize audience without ads
170+
- **Web3 Startups**: Community-driven fundraising with incentives
171+
172+
## 🛠️ Technical Highlights
173+
174+
### Binary Search Winner Selection
175+
176+
```solidity
177+
function _selectWinner(bytes32 entropySeed) internal view returns (address) {
178+
uint256 randomTicket = uint256(entropySeed) % totalTickets;
179+
180+
uint256 left = 0;
181+
uint256 right = participants.length - 1;
182+
183+
while (left < right) {
184+
uint256 mid = (left + right) / 2;
185+
if (participants[mid].upperBound > randomTicket) {
186+
right = mid;
187+
} else {
188+
left = mid + 1;
189+
}
190+
}
191+
192+
return participants[left].owner;
193+
}
194+
```
195+
196+
This O(log n) algorithm efficiently finds winners even with millions of participants.
197+
198+
## 📈 Project Structure
199+
200+
```
201+
contract/
202+
├── contracts/
203+
│ ├── ProjectRaffle.sol # Main raffle logic
204+
│ ├── RaffleFactory.sol # Factory for creating raffles
205+
│ └── interfaces/
206+
│ ├── IEntropyConsumer.sol # Pyth callback interface
207+
│ └── IEntropyV2.sol # Pyth entropy interface
208+
├── scripts/
209+
│ ├── createNewRaffle.ts # Deploy new raffle
210+
│ ├── buyTickets.ts # Purchase tickets
211+
│ ├── closeRaffle.ts # Request entropy & close
212+
│ ├── distributeFunds.ts # Distribute winnings
213+
│ └── listRaffles.ts # List all raffles
214+
├── hardhat.config.ts
215+
├── package.json
216+
└── tsconfig.json
217+
```
218+
219+
## 🔄 Raffle Lifecycle
220+
221+
1. **Creation**: Owner creates raffle with project details and percentages
222+
2. **Active**: Users buy tickets (1 wei = 1 ticket)
223+
3. **Close**: Owner requests Pyth Entropy for randomness
224+
4. **Callback**: Pyth responds with random number, winner is selected
225+
5. **Distribution**: Funds are split between project, winner, and platform
226+
6. **Withdrawal**: Each beneficiary withdraws their share
227+
228+
## 💰 Fee Structure
229+
230+
- **Platform Fee**: 0.05% of total pool
231+
- **Project**: Configurable percentage (e.g., 30%)
232+
- **Winner**: Remaining pool after fees
233+
234+
Example with 1 ETH pool and 30% project:
235+
- Platform: 0.0005 ETH (0.05%)
236+
- Project: 0.2998 ETH (30% of 0.9995 ETH)
237+
- Winner: 0.6997 ETH (remaining)
238+
239+
## 🚦 Getting Started
240+
241+
1. Clone the repository
242+
2. Install dependencies: `npm install`
243+
3. Set up `.env` with your keys
244+
4. Deploy factory: `npx hardhat ignition deploy...`
245+
5. Create your first raffle: `npx tsx scripts/createNewRaffle.ts`
246+
6. Share raffle address with participants
247+
7. Close raffle after duration expires
248+
8. Distribute funds and celebrate! 🎉
249+
250+
## 📝 License
251+
252+
MIT
253+
254+
## 🙋 Support & Contact
255+
256+
- GitHub: [eth-global-hackathon](https://github.com/NicoCaz/eth-global-hackathon)
257+
- Built for ETH Global Hackathon
258+
- Powered by Pyth Network
259+
260+
---
261+
262+
**Built with ❤️ using Pyth Entropy for fair and transparent crowdfunding**
263+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
/**
5+
* @title IEntropyConsumer
6+
* @notice Minimal interface for contracts that consume Pyth Entropy randomness.
7+
* @dev Only the functions required by the project have been declared.
8+
*/
9+
interface IEntropyConsumer {
10+
/**
11+
* @notice Callback invoked by the Entropy contract when randomness is ready.
12+
*/
13+
function entropyCallback(
14+
uint64 sequenceNumber,
15+
address provider,
16+
bytes32 randomNumber
17+
) external;
18+
19+
/**
20+
* @notice Returns the address of the Entropy contract being used.
21+
*/
22+
function getEntropy() external view returns (address);
23+
}
24+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.20;
3+
4+
/**
5+
* @title IEntropyV2
6+
* @notice Minimal subset of the Pyth Entropy V2 interface used by ProjectRaffle.
7+
*/
8+
interface IEntropyV2 {
9+
/**
10+
* @notice Returns the default randomness provider configured in the Entropy contract.
11+
*/
12+
function getDefaultProvider() external view returns (address);
13+
14+
/**
15+
* @notice Returns the fee required to request randomness from a provider.
16+
*/
17+
function getFee(address provider) external view returns (uint256);
18+
19+
/**
20+
* @notice Requests randomness from the Entropy contract.
21+
* @param provider Randomness provider address.
22+
* @param userRandomNumber User provided commitment (bytes32).
23+
* @param useBlockhash Flag to mix in the blockhash.
24+
* @return sequenceNumber Unique sequence identifier for the request.
25+
*/
26+
function request(
27+
address provider,
28+
bytes32 userRandomNumber,
29+
bool useBlockhash
30+
) external payable returns (uint64 sequenceNumber);
31+
}
32+

0 commit comments

Comments
 (0)