How $RIVALS works
$RIVALS is a betting protocol built on Uniswap V4. Bet on the world's biggest matches (World Cup, UFC, Champions League) with ETH, hold the token to bet without selling your bag, and watch every losing pot buy back and burn supply. Fixed 1,000,000 tokens, no team or VC cut.
What it is
$RIVALS is a fixed-supply token that doubles as the engine of a betting venue. One Uniswap V4 hook does three jobs at once.
- A market to buy and sell $RIVALS (1% swap fee).
- A lender: lock $RIVALS, draw ETH at 40% loan-to-value from idle pool reserves.
- A one-tap bet: route that borrowed ETH straight into a match pool.
Under the hood · The contracts involved
$RIVALS is an ERC20 paired with a Uniswap V4 hook (RivalHook) and a pari-mutuel betting contract (BetVault). Bet resolution is read from a sports oracle (Chainlink Sports, with a 3-of-5 multisig fallback). 5% of every losing pool is captured as protocol rake, swapped via an internal V4 trade to $RIVALS, and burned.
How the pieces fit
Three actions, four contracts. A holder can lock $RIVALS, receive 40% of its value in ETH (less a 1% fee), and drop that ETH into a match pool, all in a single transaction.
Under the hood · Architecture diagram
┌──────────────────────────┐
│ Uniswap V4 PoolManager │
└──────────┬───────────────┘
│ hook callbacks
▼
┌──────────────────────────────────┐
│ RivalHook │ ┌──────────────────┐
│ • LDF bands (100 × 30 ETH) │ │ BetVault │
│ • borrow / repay / liquidate │────▶│ • pari-mutuel │
│ • borrowToBet │ │ • oracle read │
│ • rakeBurn (called by vault) │◀────│ • claim/refund │
└──────────────────────────────────┘ └──────┬───────────┘
│ │
▼ ▼
┌──────────────────┐ ┌────────────────────┐
│ $RIVALS (ERC20) │ │ Sports oracle │
│ • 1M fixed │ │ (or multisig fall) │
└──────────────────┘ └────────────────────┘
The price curve
$RIVALS trades against ETH on a standard constant-product curve, with liquidity spread across 100 price bands so the market stays deep as it grows.
Under the hood · The math
V = 20 ETH- virtual reserve so the curve is defined at zero real ETHK = 20,000,000- establishes 1,000,000 $RIVALS at launch- Spot price =
(V + realETH) / realTokens
Liquidity is sliced into 100 bands of 30 ETH each (a Liquidity Distribution Function), covering 0 to 3,000 ETH of cumulative inflow. Each band i holds:
Band 0 (0 to 30 ETH) gets 60% of the supply; later bands taper sharply.
Borrowing against $RIVALS
Lock $RIVALS and borrow ETH against it at 40% of its value. Keep your tokens, get spending power for bets.
Under the hood · What borrow() does step by step
- Pulls collateral and prices it at spot.
- Computes the liquidation ETH level via
√0.6 × (V + spotETH) − Vand maps it to a bound band. - Withdraws ETH from that band's LP (which sits below current spot, so price is unchanged).
- Routes 1% to the fee collector, sends 99% to the user, records the position.
Liquidations sell the collateral via a V4 swap, pay a capped 1% bounty to the caller, and refill the bound band as fresh single-sided ETH LP. No $RIVALS is burned; the band just gets deeper. This is the protocol's self-healing property.
How bets pay out
Every match is a pari-mutuel pool: all the money on the losing sides is split among everyone who backed the winner, minus a 5% fee.
You can stake two ways:
- Direct ETH - minimum stake 0.05 ETH.
- Borrowed ETH via
borrowToBet()- the hook draws ETH from your collateral and forwards it to BetVault atomically. The ETH never touches your wallet.
Under the hood · Resolution and claiming
resolve(matchId)- anyone can call after the match ends. Reads the oracle, setswinningOutcome.claim(matchId, outcome)- winners pullstake + (stake × losersPool × 0.95) / winnersPool.- The first winner-claim triggers the buyback and burn (next section).
settleBetPosition(...)- permissionless settler for borrow-routed bets. Auto-repays debt, returns collateral plus surplus, or banks winnings if the position was liquidated mid-bet.- If the oracle does not report within 48h:
timeoutRefund(matchId)opens and all stakes are refunded at par.
Buyback & burn
5% of every losing pot is used to buy $RIVALS on the protocol's own market and burn it. Every settled match permanently shrinks supply.
Under the hood · The buyback transaction
When the first winner claims, BetVault forwards 5% of the losers' pool to RivalHook.rakeBurn(). Inside an unlockCallback:
1. poolManager.sync(ETH); poolManager.settle{value: rake}();
2. swap(zeroForOne=true, amountSpecified=-rake);
3. poolManager.take(RIVALS, hook, tokensBought);
4. RIVALS.burn(tokensBought);The burn block is marked lastLiquidationBlock; external swaps in the same block are rejected by beforeSwap, which blocks sandwich attempts around the buyback. Internal swaps (where sender == hook) bypass this gate.
Edge case: if nobody bet the winning outcome, anyone can call sweepNoWinners(matchId) to route the entire pot to a burn. Permissionless, one-shot.
Where results come from
Match results are read from a sports oracle, not from the team. The protocol is oracle-agnostic, so the deployment uses whichever feed is wired at launch.
Under the hood · Oracle interface and fallback
Resolution depends on a single interface:
function getOutcomeForFixture(bytes32 feedKey)
external view
returns (uint8 outcome, uint64 settledAt, uint8 statusFlag);
// statusFlag: 0 pending · 1 final · 2 cancelled · 3 abandonedPrimary: a Chainlink Sports Data Streams adapter. Fallback: a 3-of-5 multisig MultisigResolver keyed off a public scores feed, gated by a one-hour challenge window. After 48h with no report, timeoutRefund opens.
Tokenomics
One million tokens, all of it sold into the open market. No team bag, no VC round, no unlock schedule to dump on you.
Under the hood · Bootstrap disclosure
The deployer may call ownerBootstrapBuy() before start() opens public swaps, paying the same 1% swap fee as any buyer. The ETH committed is at the deployer's discretion and is verifiable via the BootstrapBuy(buyer, ethIn, tokensOut) event. This pristine first entry is intentional: it sets a clean starting price with no front-running.
Launch sequence
Launch is a fixed recipe of deploy steps ending in a single atomic bundle that opens trading with no chance to front-run it.
Under the hood · Full deploy steps
- Deploy
FeeCollectorwith a multisig owner. - Deploy the
$RIVALSERC20, mint 1M to the deployer. - Mine a CREATE2 salt encoding the V4 hook permission flags
0x3ACC. - Deploy
RivalHookat the mined address. - Transfer 1M $RIVALS from deployer to hook.
- Deploy
BetVault(hook, oracle, multisig). hook.setBetVault(vault)- one-shot, self-bricks.hook.initializePool(), thenseedBands(0, 50)andseedBands(50, 100).- Register the opening slate of matches in
BetVault. - Final atomic bundle:
ownerBootstrapBuy+start(). Public swaps open. - Later rounds are registered progressively as brackets settle.
- After the final match resolves, ownership of both contracts is renounced.
Risks
Read these before you bet. This is v1 software handling real money.
timeoutRefund is the user-side escape after 48h.start(), that position is just like any other holder's - subject to swap fees and transparent on-chain.Beyond one tournament
The contracts do not expire. $RIVALS is the token; the protocol is the venue. The same lending hook, pari-mutuel pools, and burn engine work for any tournament with a fixed set of resolvable fixtures.
- More fight cards and league seasons - same contracts, same burn.
- Future major tournaments - an extra
BetVaultbound to the same hook and token, sharing the burn. - Bigger brackets - multi-vault architecture as volume grows.
Continuity is a stated commitment, not a contract obligation. If the utility does not carry, the fixed supply still does.
Contracts & references
Once deployed, contract addresses and Etherscan links populate here. For now:
- Source code - github.com/wolfofclaude/rival
- Uniswap V4 hook spec - docs.uniswap.org/v4
- UMA Optimistic Oracle v3 - docs.uma.xyz