Decentralized prediction markets powered by Uniswap v4 and QUSD
Qlick enables users to create prediction markets, propose outcomes, trade positions, and resolve markets based on real-world events using automated market makers.
- Market Creation: Anyone can create prediction markets with custom parameters
- Proposal System: Users deposit tokens to propose potential outcomes
- Automated Trading: Integrated with Uniswap v4 for seamless trading
- Price Discovery: Automatically selects highest-priced proposal at deadline
- Oracle Integration: Flexible resolver system for outcome verification
- QUSD Integration: Virtual USD token (QUSD) for consistent liquidity pairs
- Secure Token Mechanics: Decision tokens (YES/NO) with granular access control
- Architecture
- Installation
- Usage
- Testing
- Deployment
- Contract Overview
- Security
- Development
- Production Considerations
See ARCHITECTURE.md for detailed system design.
1. CREATE MARKET
β
2. USERS DEPOSIT β CREATE PROPOSALS β MINT YES/NO TOKENS
β
3. TRADING (Uniswap v4 pools track prices)
β
4. DEADLINE PASSES β GRADUATION (highest YES price selected)
β
5. ORACLE VERIFICATION β RESOLUTION
β
6. WINNERS REDEEM REWARDS
| Contract | Purpose |
|---|---|
Market.sol |
Core engine managing lifecycle |
QUSD.sol |
Virtual USD token (ERC20) |
DecisionToken.sol |
YES/NO position tokens |
MarketUtilsSwapHook.sol |
Uniswap v4 hook for price tracking |
SimpleResolver.sol |
Oracle resolver (dev/testing) |
- Foundry
- Solidity ^0.8.26
- Git
# Clone the repository
git clone <your-repo-url>
cd contracts
# Install dependencies
forge install
# Build contracts
forge buildThe project uses:
- OpenZeppelin Contracts (access control, ERC20, security)
- Uniswap v4 Core (pool management)
- Uniswap v4 Periphery (position management)
- Uniswap Hooks (BaseHook implementation)
// 1. Deploy a market token (e.g., USDC)
// 2. Deploy a resolver
// 3. Create the market
uint256 marketId = market.createMarket(
marketToken, // ERC20 token for deposits
1000e18, // Minimum deposit to create proposals
block.timestamp + 7 days, // Deadline
resolverAddress // Oracle resolver
);// 1. Deposit tokens
market.depositToMarket(marketId, 1000e18);
// 2. Create a proposal
uint256 proposalId = market.createProposal(
marketId,
"Proposal description"
);// Mint YES/NO token pairs + QUSD
market.mintYesNo(proposalId, 100e18);
// Trade on Uniswap v4 pools
// YES/QUSD pool and NO/QUSD pool
// Redeem pairs back to market tokens
market.redeemYesNo(proposalId, 100e18);// After deadline, graduate the market
market.graduateMarket(marketId);
// Set outcome in resolver (owner only in SimpleResolver)
resolver.setOutcome(proposalId, true); // true = YES wins
// Resolve the market
market.resolveMarket(marketId, true, "");// After resolution, winners claim
market.redeemRewards(marketId);The project includes comprehensive test coverage:
# Run all tests
forge test
# Run with verbosity
forge test -vvv
# Run specific test file
forge test --match-path test/Market.t.sol
# Run with gas reporting
forge test --gas-report
# Run with coverage
forge coveragetest/Market.t.sol- Core market functionality (15+ tests)test/tokens/QUSD.t.sol- QUSD token teststest/tokens/DecisionToken.t.sol- Decision token teststest/resolvers/SimpleResolver.t.sol- Resolver tests
- β Market creation and configuration
- β Deposit mechanics
- β Proposal creation with token minting
- β YES/NO token minting and redemption
- β Market graduation
- β Oracle resolution
- β Reward redemption
- β Edge cases (insufficient deposits, wrong outcomes, etc.)
- β Multiple proposals per market
- β Access control
# Set environment variables
cp .env.example .env
# Edit .env with your values
# Deploy with mocks (for testing)
forge script script/DeployMarket.s.sol --rpc-url <RPC_URL> --broadcast
# Deploy to testnet
forge script script/DeployMarket.s.sol \
--rpc-url $RPC_URL \
--broadcast \
--verifyPRIVATE_KEY=<your-private-key>
USE_MOCKS=true # Set to false for production with real Uniswap v4
POOL_MANAGER=<uniswap-pool-manager-address>
POSITION_MANAGER=<uniswap-position-manager-address>- Deploy QUSD token
- Deploy DecisionToken
- Deploy Market contract
- Deploy MarketUtilsSwapHook
- Set Market as minter for QUSD and DecisionToken
- Deploy resolver(s)
The deployment script handles this automatically.
Main contract managing the prediction market lifecycle.
Key Functions:
createMarket()- Create a new marketdepositToMarket()- Deposit tokens to participatecreateProposal()- Create outcome proposalmintYesNo()/redeemYesNo()- Mint/redeem token pairsgraduateMarket()- Select winning proposalresolveMarket()- Verify and finalize outcomeredeemRewards()- Claim winnings
Virtual USD token used for liquidity pairs.
Features:
- ERC20-compliant
- Mint/burn by Market contract only
- Standard transfers enabled
Multi-dimensional token for YES/NO positions.
Structure:
- Balance:
account => proposalId => tokenType => amount - Types: YES or NO
- Mint/burn/transfer by Market contract only
Uniswap v4 hook for price tracking.
Hooks:
beforeSwap()- Validate market stateafterSwap()- Update price tracking
Functions:
setOutcome()- Owner sets verified outcomeverifyResolution()- Called by Market to verify proof
- QUSD & DecisionToken: Only Market contract can mint/burn
- Market Operations: Public but state-gated
- Resolver: Owner-controlled (in SimpleResolver)
- ReentrancyGuard on all token operations
- SafeERC20 for external token transfers
- Zero address validation
- Deadline enforcement
- Market status checks
- Proof verification
- SimpleResolver is centralized - Replace with decentralized oracle in production
- Pool initialization is simplified - Needs full Uniswap v4 integration
- Price tracking is estimated - Production needs accurate tick calculation
Do not use in production without:
- Professional security audit
- Comprehensive testing on testnet
- Decentralized resolver implementation
- Full Uniswap v4 integration
contracts/
βββ src/
β βββ Market.sol # Core market engine
β βββ MarketUtilsSwapHook.sol # Uniswap v4 hook
β βββ tokens/
β β βββ QUSD.sol # Virtual USD token
β β βββ DecisionToken.sol # YES/NO tokens
β βββ resolvers/
β β βββ SimpleResolver.sol # Dev resolver
β βββ interfaces/
β β βββ IMarket.sol
β β βββ IMarketResolver.sol
β β βββ IQUSD.sol
β β βββ IDecisionToken.sol
β βββ common/
β β βββ MarketData.sol # Structs and enums
β βββ utils/
β βββ Id.sol # ID generator
βββ test/
β βββ Market.t.sol
β βββ tokens/
β βββ resolvers/
β βββ mocks/
βββ script/
β βββ DeployMarket.s.sol
βββ ARCHITECTURE.md
forge buildforge fmt# Check for issues
forge test --show-progress
# Static analysis (if slither installed)
slither .Replace SimpleResolver with:
Option A: Chainlink Oracles
contract ChainlinkResolver is IMarketResolver {
AggregatorV3Interface public priceFeed;
function verifyResolution(uint256 proposalId, bool yesOrNo, bytes calldata proof)
external view override
{
// Fetch and verify Chainlink data
}
}Option B: UMA DVM
contract UMAResolver is IMarketResolver {
OptimisticOracleV3Interface public oracle;
function verifyResolution(uint256 proposalId, bool yesOrNo, bytes calldata proof)
external view override
{
// Query UMA dispute resolution
}
}Option C: Signature Verification
contract SignatureResolver is IMarketResolver {
address public trustedSigner;
function verifyResolution(uint256 proposalId, bool yesOrNo, bytes calldata proof)
external view override
{
// Verify ECDSA signature from trusted oracle
(bytes32 r, bytes32 s, uint8 v) = abi.decode(proof, (bytes32, bytes32, uint8));
// Validate signature...
}
}Current implementation has placeholder pool initialization. Production needs:
- Proper Currency wrapping for DecisionTokens
- Real pool initialization with correct parameters
- Position NFT management
- Liquidity tracking and removal
- Fee handling
- Dynamic Fees: Adjust based on volatility
- Liquidity Incentives: Reward liquidity providers
- Slippage Protection: Add max slippage parameters
- Market Maker Rewards: Incentivize proposal creators
- Timelock: Add delays for critical changes
- Multi-sig: Require multiple signers for sensitive operations
- Upgradeability: Consider proxy patterns
- Emergency Pause: Circuit breaker for emergencies
- Batch operations where possible
- Use
calldatainstead ofmemoryfor read-only data - Pack storage variables efficiently
- Consider EIP-1167 minimal proxies for repeated deployments
MIT License - see LICENSE
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
For questions and support:
- Open an issue on GitHub
- Check ARCHITECTURE.md for detailed design
- Review test files for usage examples
- Full Uniswap v4 integration
- Decentralized resolver implementations
- Frontend interface
- Subgraph for event indexing
- Multi-chain deployment
- Governance token
- Advanced market types (scalar, categorical)
- Market maker incentives
- Professional security audit
Built with β€οΈ using Foundry and Uniswap v4