| id | Title | Status | Author | Description | Discussions to | Created | | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | | OIP-254 | Overtime Live Parlay Positioning AMM | Draft | Danijel | Enable multi-leg live betting (parlays) on Overtime V2 via Chainlink-verified leg-by-leg execution | https://discord.com/invite/overtime-io| 2026-01-21 | ## Simple Summary This OIP proposes adding **Live Betting Parlays** to Overtime V2. Users will be able to compose a parlay from multiple live legs and execute it using the existing Live Betting flow, with verification performed by **Chainlink oracles**. This improves UX (single action for multiple legs) and would be one of the first fully on-chain protocols to offer live parlays. ## Abstract Overtime V2 already supports live single-leg betting via `LiveTradingProcessor` + Chainlink verification, resulting in an on-chain ticket created by `SportsAMMV2.tradeLive(...)`. This OIP extends the same architecture to support **parlay live trades**, by: - Introducing a **parlay request path** (`requestLiveParlayTrade`) alongside existing single requests. - Verifying legs **one-by-one** off-chain (Chainlink node / adapter), using the same data sources and validation rules as singles. - Returning an approved overall quote and approved per-leg odds to `LiveTradingProcessor`, which executes the full parlay atomically via `tradeLive` using a `TradeData[]` array. Unlike singles, **slippage is checked on the total parlay quote**, not per-leg slippage, because the user experience is based on the overall payout expectation. Implementation is provided in: - https://github.com/thales-markets/contracts-v2/pull/168 ## Motivation Live markets make up the majority of sportsbook volume globally. Overtime V2 already addresses the unique challenges of live trading on-chain (implicit latency, dynamic risk, sport-dependent delays) by using **on-demand Chainlink verification**. Adding live parlays is the natural next step: - **Better UX**: users construct multiple legs and confirm once, rather than repeating the request/verify/execute cycle. - **Core betting primitive**: parlays are a major driver of engagement and volume. - **On-chain differentiation**: live parlays are complex due to delay/volatility and risk; providing them with transparent, on-chain execution is a meaningful protocol milestone. ## Specification ### High-level flow 1. User submits a parlay live trade request to `LiveTradingProcessor.requestLiveParlayTrade(...)` containing: - An array of legs (`gameId`, `sportId`, `typeId`, `line`, `position`, `playerId`, `expectedLegOdd`) - `buyInAmount` - `expectedPayout` (expected overall quote/payout) - `additionalSlippage` (tolerance vs `expectedPayout`) - `collateral` and optional `referrer` 2. `LiveTradingProcessor` validates: - `legs.length > 1` - Live trading enabled for each leg via `riskManager.liveTradingPerSportAndTypeEnabled(leg.sportId, leg.typeId)` 3. `LiveTradingProcessor` builds a Chainlink request using `parlayJobSpecId`, encoding arrays for adapter-friendly parsing: - `gameIds[]`, `sportIds[]`, `typeIds[]`, `lines[]`, `positions[]`, `expectedLegOdds[]`, `playerIds[]` - plus `buyInAmount`, `expectedQuote` (=`expectedPayout`), `additionalSlippage` - metadata fields: `requester`, `collateral` 4. Off-chain (Chainlink node / adapter) processing: - Verifies each leg sequentially (same rules as singles: constraints, data freshness, odds sources/aggregation, sport delays, etc.). - If any leg fails verification, overall decision is deny (`_allow = false`). - If all legs pass, returns: - `_approvedQuote` (overall approved payout/quote) - `_approvedLegOdds[]` (per-leg approved odds for the chosen positions) 5. Chainlink fulfills on-chain via `fulfillLiveTradeParlay(requestId, allow, approvedQuote, approvedLegOdds[])`. 6. On fulfillment, `LiveTradingProcessor` enforces: - Request not already fulfilled - Request not timed out: `timestampPerRequest + maxAllowedExecutionDelay > block.timestamp` - Request is a parlay: `requestIdIsParlay[requestId] == true` - `_approvedLegOdds.length == legsLen` - **Total slippage bound** (scaled by `1e18`): - `(approvedQuote / expectedPayout) <= 1 + additionalSlippage` 7. If `_allow == true`, the contract executes atomically: - Builds `ISportsAMMV2.TradeData[]` of length `legsLen`, one entry per leg, with an `ODDS_LEN` array that only sets the selected `position` to the approved leg odd. - Calls: - `sportsAMM.tradeLive(tradeData, buyInAmount, approvedQuote, requester, referrer, collateral)` - Stores the created ticket address in `requestIdToTicketId[requestId]`. 8. If requester is `freeBetsHolder`, it calls: - `FreeBetsHolder.confirmLiveTrade(requestId, createdTicket, buyInAmount, collateral)` ### Slippage semantics (parlay-specific) Parlay live trades enforce slippage on the **total quote only**: - The user sets `expectedPayout` and `additionalSlippage`. - The contract requires the approved quote to be within that tolerance. - Individual leg slippage is not enforced on-chain; user protection is based on the final approved quote. ### Risk management parity with prematch parlays All existing **prematch parlay risk-management rules** apply equally to live parlays introduced by this OIP. This includes all limits and checks enforced by the existing parlay risk model, such as exposure limits, liquidity checks, maximum risk and payout constraints, and any other rules applied by the protocol’s risk management layer. Live parlays do not introduce a separate or weaker risk model. They reuse the same parlay-level risk evaluation logic as prematch parlays, with the only difference being that odds are verified and supplied dynamically via Chainlink rather than quoted on-chain. If needed, the maximum number of legs for live parlays can be additionally constrained without protocol changes, via frontend-level enforcement and/or Chainlink adapter or job-spec rules. ### Contract/API additions #### New storage (parlays) - `mapping(bytes32 => ILiveTradingProcessor.LiveParlayTradeData) public requestIdToParlayTradeData;` - `mapping(bytes32 => bool) public requestIdIsParlay;` #### New request method - `requestLiveParlayTrade(ILiveTradingProcessor.LiveParlayTradeData calldata _parlay) returns (bytes32 requestId)` #### New fulfill method - `fulfillLiveTradeParlay(bytes32 _requestId, bool _allow, uint _approvedQuote, uint[] calldata _approvedLegOdds)` #### New Chainlink configuration - `bytes32 public parlayJobSpecId;` - `setConfiguration(...)` extended to include `_parlayJobSpecId` #### Events - `LiveParlayTradeRequested(requester, requestCounter, requestId, legsCount, buyInAmount)` - `LiveParlayTradeFulfilled(requester, requestId, allow, legsCount, buyInAmount, approvedQuote)` ### Timeout behavior Parlay requests share the same timeout semantics as singles: - `maxAllowedExecutionDelay` (default 30 seconds) bounds how long fulfillment is valid. - Late fulfill attempts revert with `"Request timed out"` and no trade is executed. ## Rationale ### Reuse of existing architecture This OIP mirrors the existing live singles design (previously introduced via TIP-143): - Same `LiveTradingProcessor` entry point and Chainlink request/fulfill model - Same `SportsAMMV2.tradeLive(...)` execution path, just using multiple `TradeData` entries - Same “no odds stored on-chain” model: odds are supplied by Chainlink response - Same failure model: if verification denies or times out, no ticket is created This minimizes protocol risk and implementation complexity by reusing proven components. ### Why verify legs one-by-one off-chain Leg-by-leg evaluation is both simpler and safer: - Allows early abort if a single leg is invalid (stale, suspended, inconsistent, etc.) - Enables per-leg sport/type specific delay and constraints to be applied consistently - Matches adapter implementations that already operate on one market at a time, while still producing an aggregated parlay decision ### Why enforce total slippage only Parlay UX is driven by the final payout. Enforcing per-leg slippage increases complexity and can create confusing failures (e.g., one leg drifted slightly while overall payout remains acceptable). The protocol still enforces strong user protection via the final approved quote bound. ### Future extensibility: live system bets This OIP intentionally enables future extensions of live betting beyond simple parlays. By introducing parlay-aware live request handling, leg-by-leg off-chain verification, and atomic execution of multiple live legs, the protocol gains the foundational primitives required to support **live system bets** (such as Trixie, Yankee, or similar combinations) in the future. While live system bets are not implemented as part of this proposal, this OIP does not introduce any architectural constraints that would prevent them from being added later using the same verification, risk-management, and execution model. ## Test Cases Suggested coverage (in addition to adapter tests): ### On-chain (unit tests) 1. `requestLiveParlayTrade` reverts if `legs.length <= 1` 2. `requestLiveParlayTrade` reverts if any leg is not live-enabled 3. `fulfillLiveTradeParlay` reverts if the request is not marked as parlay 4. `fulfillLiveTradeParlay` reverts if request is timed out 5. `fulfillLiveTradeParlay` reverts if `_approvedLegOdds.length != legsLen` 6. `fulfillLiveTradeParlay` reverts if total slippage is too high vs `expectedPayout` 7. `fulfillLiveTradeParlay` executes `tradeLive` and stores `requestIdToTicketId` 8. `freeBetsHolder` path calls `confirmLiveTrade(...)` ### Off-chain (adapter) tests 1. All legs pass → `_allow = true`, returns valid `_approvedQuote` and `_approvedLegOdds[]` 2. Any leg fails → `_allow = false` 3. Stale data / time constraint breach on any leg → deny 4. Expected odds sanity checks per leg 5. Approved quote aggregation correctness (as per odds model used in the adapter) ## Implementation - https://github.com/thales-markets/contracts-v2/pull/168 ## Copyright Copyright and related rights waived via CC0.