D3
Duty3

Smart Contracts

Duty3 is composed of two factory contracts and vault contracts deployed per raffle. All contracts are verified on Arbiscan.

Contract Addresses (Arbitrum One)

Raffle Factory

Deploys new ERC-20 prize vaults. Entry point for createRaffle().

0x7CA34d13ca90A93B62E1cD515E236eDe7921c343

Arbiscan ↗

$DUTY3 Token

Native utility token. Hold 1,000+ for 10% ticket discount.

0x5C716a1194dE2dA092f0fd70dbF42843E8A3fbd3

Arbiscan ↗

Factory ABI (key functions)

// Create a new raffle vault
function createRaffle(
  address _prizeToken,      // WETH / WBTC / USDC address
  uint256 _prizeAmount,     // in token's native decimals
  uint256 _ticketPrice,     // base price per ticket
  uint256 _maxTickets,      // total ticket supply
  uint256 _duration,        // seconds from now
  uint256 _winnerCount,     // 1 for single winner
  string  _creatorTag       // optional label, e.g. "AI"
) returns (address vault)

// Read vaults
function getRafflesPaginated(uint256 offset, uint256 limit)
  view returns (address[])
function getRafflesCount() view returns (uint256)
function platformFeeBps() view returns (uint256)
function minPrizeAmount(address token) view returns (uint256)
function minTicketPrice(address token) view returns (uint256)

// Events
event RaffleCreated(
  address indexed vault,
  address indexed creator,
  address prizeToken,
  uint256 winnerCount
)

Vault ABI (key functions)

// Participation
function buyTickets(uint256 _quantity)
function claimRefund()

// Pricing (respects DUTY3 discount)
function getPrice(address user) view returns (uint256)

// State
function state() view returns (uint8)  // 0=OPEN, 1=CALCULATING, 2=COMPLETED, 3=REFUND

// Raffle info
function prizeToken()       view returns (address)
function paymentToken()     view returns (address)
function prizeAmount()      view returns (uint256)
function baseTicketPrice()  view returns (uint256)
function maxTickets()       view returns (uint256)
function deadline()         view returns (uint256)
function winnerCount()      view returns (uint256)
function creator()          view returns (address)
function creatorTag()       view returns (string)

// Participation info
function getParticipantCount()               view returns (uint256)
function getUserTicketIds(address user)      view returns (uint256[])
function getSoldPercent()                    view returns (uint256)
function userSpent(address)                  view returns (uint256)

// Results
function getWinners()                        view returns (address[])
function getWinningTicketIds()               view returns (uint256[])

// Events
event TicketBought(address indexed buyer, uint256 count, uint256[] ticketIds)
event WinnerPicked(address indexed winner, uint256 ticketId)
event MultiWinnersPicked(address[] winners, uint256[] ticketIds)
event StateChanged(uint8 indexed oldState, uint8 newState)

Supported Tokens

TokenDecimalsAddress (Arbitrum)
WETH180x82aF49447D8a07e3bd95BD0d56f35241523fBab1
WBTC80x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f
USDC60xaf88d065e77c8cC2239327C5EDb3A432268e5831

Integration Example (ethers v6)

import { ethers } from "ethers"

const FACTORY = "0x7CA34d13ca90A93B62E1cD515E236eDe7921c343"
const FACTORY_ABI = [
  "function createRaffle(address,uint256,uint256,uint256,uint256,uint256,string) returns (address)",
  "function getRafflesPaginated(uint256,uint256) view returns (address[])",
]
const VAULT_ABI = [
  "function buyTickets(uint256)",
  "function getPrice(address) view returns (uint256)",
  "function state() view returns (uint8)",
]

const provider = new ethers.JsonRpcProvider("https://arb1.arbitrum.io/rpc")
const signer = new ethers.Wallet(PRIVATE_KEY, provider)
const factory = new ethers.Contract(FACTORY, FACTORY_ABI, signer)

// Get all vaults
const vaults = await factory.getRafflesPaginated(0n, 50n)

// Buy a ticket
const vault = new ethers.Contract(vaults[0], VAULT_ABI, signer)
const price = await vault.getPrice(signer.address)
// Approve WETH first, then:
await vault.buyTickets(1n)