How Duty3 Works
A Duty3 raffle follows a deterministic lifecycle: creation → ticket sale → VRF draw → payout. Every step is enforced by immutable smart contracts on Arbitrum.
The Raffle Lifecycle
1. Creation
A creator calls createRaffle() on the Factory contract, providing:
- Prize token — WETH, WBTC, or USDC
- Prize amount — locked immediately into the vault
- Ticket price — fixed price in the payment token
- Max tickets — the total supply of tickets
- Duration — deadline in seconds from now
- Winner count — 1 for single winner, N for multi-winner split
- Creator tag — optional label (e.g.
"AI"for agent-created raffles)
The Factory deploys a new Vault contract and transfers the prize from the creator's wallet into it. The prize cannot be withdrawn by anyone until VRF selects a winner.
2. Ticket Sale (OPEN state)
Once deployed, the vault enters OPEN state. Participants call buyTickets(quantity) on the vault after approving the payment token. Each ticket receives a sequential ID starting from 0. The vault tracks:
- Total tickets sold vs. max tickets
- Each participant's ticket IDs
- Total spent per address
Ticket prices are determined by getPrice(userAddress). Users holding ≥ 1,000 $DUTY3 automatically receive a 10% discount — no coupon codes, no manual claims.
3. Draw Trigger
The raffle closes when either condition is met:
- All tickets are sold (100% fill)
- The deadline timestamp passes
At that point, the vault transitions to CALCULATING state and sends a VRF request to Chainlink. The request is fulfilled by Chainlink's oracle network — typically within 1-3 blocks on Arbitrum (~0.5-1 second).
4. Winner Selection (Chainlink VRF)
Chainlink VRF delivers a cryptographically random number to the vault contract. The contract uses this number to select the winning ticket ID:
winningTicketId = vrfRandomWord % totalTicketsSoldFor multi-winner raffles, multiple unique IDs are derived from the same VRF word. The winning address is the holder of the winning ticket ID.
5. Payout (COMPLETED state)
Immediately upon VRF fulfillment, the vault transfers the prize to the winner(s) and emits a WinnerPicked event. A platform fee (in basis points, set by the Factory) is deducted from the ticket revenue and sent to the protocol treasury. The creator receives the remaining ticket revenue.
Vault States
| State | Value | Description |
|---|---|---|
| OPEN | 0 | Accepting ticket purchases |
| CALCULATING | 1 | VRF request sent, awaiting fulfillment |
| COMPLETED | 2 | Winner paid, raffle closed |
| REFUND | 3 | Raffle cancelled — tickets can be refunded |
Fee Structure
Duty3 charges a platform fee expressed in basis points (bps). The fee is deducted from ticket revenue collected by the vault at the time of payout. Prize funds are never touched — the winner always receives 100% of the locked prize.
- Platform fee: read from
Factory.platformFeeBps() - Applied to: ticket sale revenue (not the prize)
- Paid to: protocol treasury address
Refund Mechanism
If a raffle fails to complete (e.g. VRF never fulfills, or an admin emergency abort is triggered), the vault can enter REFUND state. Participants call claimRefund() to recover their ticket payments. The creator's prize is returned to them as well.