What it does: A group of people deposits funds regularly into a shared smart contract. Each month, the contract randomly selects one participant to receive the pot (minus collateralized safeguards to prevent cheating).
Why it matters: Encourages savings habits and offers interest-like liquidity access without banks.
How it works:
Members commit to recurring deposits.
Deposits are locked via staking commitments.
Random selection uses verifiable random functions.
Penalties automatically apply to anyone who misses deposits.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* Community Savings Pool (Gamified)
*
* WARNING:
* 1) Audit before mainnet.
* 2) Use testnets first.
* 3) Plug in a real VRF implementation (Chainlink, etc.).
*/
interface VRFConsumer {
function requestRandom() external returns (uint256 requestId);
}
contract CommunitySavingsPool {
struct Member {
bool joined;
uint256 depositedThisCycle;
uint256 totalDeposited;
uint256 totalWon;
uint256 missedDeposits;
}
enum PoolState { Open, Active, Closed }
address public admin;
uint256 public cycleDuration; // seconds (e.g., 30 days)
uint256 public requiredDeposit; // per cycle
uint256 public penaltyFee; // % (in basis points)
uint256 public maxMembers;
uint256 public currentCycleStart;
uint256 public currentCycle;
PoolState public state;
address[] public members;
mapping(address => Member) public memberInfo;
// VRF randomness
VRFConsumer public vrf;
mapping(uint256 => bool) public randomProcessed;
// Events
event MemberJoined(address member);
event DepositMade(address member, uint256 amount, uint256 cycle);
event WinnerSelected(address winner, uint256 amount, uint256 cycle);
event PenaltyApplied(address member, uint256 amount);
event PoolActivated(uint256 startCycle);
event PoolClosed();
modifier onlyAdmin() {
require(msg.sender == admin, "Not authorized");
_;
}
modifier onlyMember() {
require(memberInfo[msg.sender].joined, "Not a member");
_;
}
modifier inState(PoolState s) {
require(state == s, "Invalid pool state");
_;
}
constructor(
uint256 _cycleDuration,
uint256 _requiredDeposit,
uint256 _penaltyFeeBps,
uint256 _maxMembers,
address _vrf
) {
require(_maxMembers > 1, "Pool must have multiple members");
require(_penaltyFeeBps 20%)");
admin = msg.sender;
cycleDuration = _cycleDuration;
requiredDeposit = _requiredDeposit;
penaltyFee = _penaltyFeeBps;
maxMembers = _maxMembers;
state = PoolState.Open;
vrf = VRFConsumer(_vrf);
}
// ---- MEMBERSHIP ----
function join() external inState(PoolState.Open) {
require(!memberInfo[msg.sender].joined, "Already joined");
require(members.length < maxMembers, "Pool full");
memberInfo[msg.sender].joined = true;
members.push(msg.sender);
emit MemberJoined(msg.sender);
// When full, pool becomes ready to activate
if (members.length == maxMembers) {
state = PoolState.Active;
currentCycle = 1;
currentCycleStart = block.timestamp;
emit PoolActivated(currentCycle);
}
}
// ---- DEPOSITS ----
function deposit() external payable onlyMember inState(PoolState.Active) {
require(block.timestamp currentCycleStart + cycleDuration, "Cycle not finished");
// Apply penalties and compute pot
uint256 pot = address(this).balance;
for (uint256 i = 0; i members.length) {
state = PoolState.Closed;
emit PoolClosed();
}
}
// ---- VIEW HELPERS ----
function membersCount() external view returns (uint256) {
return members.length;
}
function timeLeftInCycle() external view returns (uint256) {
if (block.timestamp >= currentCycleStart + cycleDuration) return 0;
return (currentCycleStart + cycleDuration) - block.timestamp;
}
// ---- ADMIN ----
function closePool() external onlyAdmin {
require(state != PoolState.Closed, "Already closed");
state = PoolState.Closed;
emit PoolClosed();
}
// Fallback
receive() external payable {}
}
Build and Grow By Nam Le Thanh