RotatingPool (ROSCA) Contract
Traditional rotating savings and credit association digitized on blockchain.
RotatingPool Contract (ROSCA)
LotteryPoolV3 contract implements Rotating Savings and Credit Association (ROSCA/Pasanaku/Tanda) on blockchain.
Contract Details
Address: Contact us for deployment
Features:
- Fixed rotation order
- Predefined payout schedule
- No yields (social credit)
- Locked until user's turn
- Trustless execution
Key Concepts
ROSCA: Group of people who contribute fixed amounts regularly. Each period, one member receives the entire pot.
Example:
- 10 members contribute 100 MUSD monthly
- Each month, 1 member receives 1,000 MUSD
- After 10 months, everyone has received 1,000 MUSD
State Variables
contract RotatingPool {
address[] public rotationOrder;
uint256 public currentRound;
uint256 public contributionAmount;
uint256 public roundInterval; // in seconds
uint256 public nextPayoutTime;
mapping(address => bool) public hasClaimed;
mapping(address => uint256) public totalContributed;
}Read Functions
getRotationOrder
function getRotationOrder() external view returns (address[] memory)getCurrentRecipient
function getCurrentRecipient() external view returns (address)nextPayoutTime
function nextPayoutTime() external view returns (uint256)hasClaimed
function hasClaimed(address member) external view returns (bool)Write Functions
contribute
Make monthly contribution.
function contribute() external nonReentrant onlyMemberRequirements:
- Must be current round
- Haven't contributed this round
- Pay exactly contributionAmount
Example:
const { writeContract } = useWriteContract()
writeContract({
address: ROTATING_POOL_ADDRESS,
abi: ROTATING_POOL_ABI,
functionName: 'contribute'
})claimPayout
Claim payout when it's your turn.
function claimPayout() external nonReentrantRequirements:
- Is current round recipient
- Time >= nextPayoutTime
- All members have contributed
Example:
writeContract({
address: ROTATING_POOL_ADDRESS,
abi: ROTATING_POOL_ABI,
functionName: 'claimPayout'
})Events
event Contribution(address indexed member, uint256 amount, uint256 round);
event PayoutClaimed(address indexed recipient, uint256 amount, uint256 round);
event RoundAdvanced(uint256 newRound, address nextRecipient);Complete Workflow
function ROSCAInterface() {
const { address } = useAccount()
// Check if it's your turn
const { data: currentRecipient } = useReadContract({
address: ROTATING_POOL_ADDRESS,
abi: ROTATING_POOL_ABI,
functionName: 'getCurrentRecipient'
})
// Check payout time
const { data: nextPayout } = useReadContract({
address: ROTATING_POOL_ADDRESS,
abi: ROTATING_POOL_ABI,
functionName: 'nextPayoutTime'
})
const isMyTurn = currentRecipient?.toLowerCase() === address?.toLowerCase()
const canClaim = isMyTurn && Date.now() / 1000 >= Number(nextPayout)
return (
<div>
{isMyTurn ? (
<div>
<p>It's your turn!</p>
{canClaim ? (
<button onClick={claimPayout}>Claim Payout</button>
) : (
<p>Wait until {new Date(Number(nextPayout) * 1000).toLocaleString()}</p>
)}
</div>
) : (
<button onClick={contribute}>Contribute for this round</button>
)}
</div>
)
}