Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions 3-Player-Lottery/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# SimpleLottery Contract

## Contract Name
SimpleLottery

## Overview
SimpleLottery is a minimal, chain-agnostic smart contract that implements a trustless lottery game among exactly three participants. Each participant deposits a fixed entry fee (0.01 ether equivalent) into the contract. When all three participants have entered, the contract uses on-chain block data to select a winner pseudo-randomly and automatically transfers the full prize pool (0.03 ether equivalent) to the winner.

The contract includes an owner-set timeout mechanism to refund participants if the lottery does not fill within a certain block period, ensuring funds are never locked indefinitely.

## Prerequisites
To deploy and test the SimpleLottery contract, you will need:
* **Ethereum-compatible wallet**: MetaMask, Q-Safe (for Quranium), or any wallet configured for the target chain.
* **Test tokens (native coin of the chain)**: e.g., Sepolia ETH on Sepolia testnet, QRN on Quranium testnet, etc. These are required for gas fees and deposits.
* **Q-Remix**: To compile, deploy, and interact.
* **Basic Solidity knowledge**: Familiarity with contract deployment, interaction, and functions.

## Contract Details

### State Variables
- **players**: `address payable` — stores the three unique participants.[3]
- **playerCount**: `uint` — counts the currently joined players (max 3).
- **ENTRY_FEE**: `uint constant` — fixed entry fee, set to 0.01 ether (chain native).
- **winnerSelected**: `bool` — true when a winner has been picked.
- **winner**: `address` — winner’s address.
- **owner**: `address` — deployer of the contract, who can issue refunds on timeout.
- **timeoutBlock**: `uint` — block number after which refund is allowed if lottery incomplete.

### Main Functions

#### Constructor
- **Purpose**: Sets the contract owner and configures the timeout block for refunds.
- **Parameters**:
- `_timeoutBlock` (uint): block number after which refunds can be issued if lottery not complete.

#### enter
- **Purpose**: Allows a unique participant to join by sending exactly 0.01 ether.
- **Requirements**:
- Game not finished.
- Less than 3 players currently.
- Caller not already entered.
- Sent value matches `ENTRY_FEE`.
- **Behavior**:
- Adds caller to players.
- Emits `PlayerJoined` event.
- Automatically picks winner and pays out when 3 players join.

#### _pickWinner (internal)
- **Purpose**: Randomly selects one of the three players as winner using blockhash entropy.
- **Behavior**:
- Sets `winner`.
- Emits `WinnerSelected` event.
- Calls payout function.

#### _payout (internal)
- **Purpose**: Transfers the entire contract balance to the winner.

#### refund
- **Access**: Only owner.
- **Purpose**: Refunds all participants if the lottery didn't complete and timeout reached.
- **Requirements**:
- Lottery not finished.
- Current block number >= `timeoutBlock`.
- **Behavior:**
- Sends deposits back to players.
- Resets player count.
- Emits `RefundIssued` event.

### Events
- **PlayerJoined(address indexed player)** — emitted when a player joins the lottery.
- **WinnerSelected(address indexed winner)** — emitted when a winner is picked.
- **RefundIssued()** — emitted when a refund is processed.

***

## Usage Flow
1. Deploy contract on target EVM blockchain with a suitable timeout block.
2. Participants call `enter()` sending exactly the entry fee native coin.
3. After three participants join, winner is chosen and prize sent automatically.
4. If lottery fails to fill before timeout, owner can call `refund()` to return deposits.

***

## Notes
- Randomness uses blockhash and is not secure against manipulations; for production, integrate Chainlink VRF or secure randomness oracles.
- ENTRY_FEE and timeoutBlock can be modified or parameterized to suit different chains or use-cases.


98 changes: 98 additions & 0 deletions 3-Player-Lottery/lottery.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

/// @title SimpleLottery - A minimal lottery contract for 3 players with timeout and refund
/// @notice Players join by paying 0.01 ether. When 3 players join, a random winner is chosen.
/// @dev Randomness uses blockhash, which is insecure for production. Owner can trigger refunds if timeout passes.
contract SimpleLottery {
address payable[3] public players; // Fixed array to store player addresses
uint public playerCount = 0; // Number of players currently entered
uint public constant ENTRY_FEE = 0.01 ether; // Entry fee for the lottery
bool public winnerSelected = false; // Flag to indicate if a winner was chosen
address public winner; // Address of the winner
address public owner; // Contract owner (deployer)
uint public timeoutBlock; // Block number after which refund is allowed

/// @notice Restricts access to only the contract owner
modifier onlyOwner() {
require(msg.sender == owner, "Only owner");
_;
}

/// @notice Emitted when a new player joins the lottery
/// @param player Address of the player that joined
event PlayerJoined(address indexed player);

/// @notice Emitted when a winner is selected
/// @param winner Address of the winner
event WinnerSelected(address indexed winner);

/// @notice Emitted when a refund is issued to players
event RefundIssued();

/// @notice Initializes the lottery contract with a timeout block
/// @param _timeoutBlock The block number after which refund can be triggered
constructor(uint _timeoutBlock) {
owner = msg.sender;
timeoutBlock = _timeoutBlock;
}

/// @notice Enter the lottery by paying exactly 0.01 ether
/// @dev Reverts if already joined, if fee is wrong, or if lottery is full
function enter() external payable {
require(!winnerSelected, "Game finished");
require(playerCount < 3, "Lottery full");
require(msg.value == ENTRY_FEE, "Deposit exact fee");

// Prevent duplicate entries
for (uint i = 0; i < playerCount; i++) {
require(players[i] != msg.sender, "Already joined");
}

// Add new player
players[playerCount] = payable(msg.sender);
playerCount++;

emit PlayerJoined(msg.sender);

// If 3 players have joined, pick a winner
if (playerCount == 3) {
_pickWinner();
}
}

/// @notice Picks a random winner once 3 players have entered
/// @dev Uses blockhash for pseudo-randomness (not secure for real money)
function _pickWinner() internal {
require(playerCount == 3, "Not enough players");

// Pick random index (0, 1, or 2)
uint randomIndex = uint(blockhash(block.number - 1)) % 3;
winner = players[randomIndex];
winnerSelected = true;

emit WinnerSelected(winner);

_payout();
}

/// @notice Transfers the entire contract balance to the winner
function _payout() internal {
payable(winner).transfer(address(this).balance);
}

/// @notice Refunds players if the lottery did not finish before the timeout
/// @dev Only the owner can call this, after `timeoutBlock`
function refund() external onlyOwner {
require(!winnerSelected, "Winner already selected");
require(block.number >= timeoutBlock, "Timeout not reached yet");

// Refund all entered players
for (uint i = 0; i < playerCount; i++) {
players[i].transfer(ENTRY_FEE);
}
playerCount = 0;

emit RefundIssued();
}
}
Loading