When you request aDocumentation Index
Fetch the complete documentation index at: https://docs.adipredictstreet.com/llms.txt
Use this file to discover all available pages before exploring further.
vault.splitPosition or vault.mergePositions
co-signature from the backend, the platform places an off-chain
lock on your vault’s balance for the duration of the signed
deadline:
- Split — locks USDC (
available -> locked) so it can’t be spent twice while the chain tx is in-flight. - Merge — locks per-outcome ERC-1155 shares so a
SELLorder can’t consume the same shares the merge plans to burn.
PositionSplit / PositionsMerged
event, and the lock is drained (consumed by chain) with the
collateral side credited symmetrically. You never see the lock.
When recovery is needed
Three failure shapes leave the lock stuck because the chain side never moved:- You never broadcast — you got the backend signature back but never submitted the tx.
- The tx was dropped — broadcast but the mempool evicted it before mining (low gas, replacement, etc.).
- The tx reverted — landed in a block but failed the contract
check; no
PositionSplit/PositionsMergedevent was emitted.
signed_ops row hits the EXPIRED
state by wall-clock once deadline elapses, but the balance lock
remains until either:
- The backend’s automatic sweeper job tops it up (default behaviour), or
- You call the user-driven recovery endpoint described below (used when the sweeper is intentionally disabled in observation-only mode for a deploy window).
The recovery endpoint
POST /api/dual-signed-ops/{opId}/release-expired
Supported op_kind: SPLIT and MERGE. CONVERT / REDEEM
follow in subsequent releases.
The endpoint runs a chain of safety gates before releasing the lock so
a signed_ops row that did succeed on-chain can never be unlocked
again (which would double-credit the vault):
- Ownership — caller’s
X-User-Walletmust own the op’svault_address(403 vault_owner_mismatch). - Op kind —
SPLITorMERGEonly (400 op_kind_unsupported). - Already terminal — already-refunded rows return an
idempotent
200withrefundAppliedNow: falseso retries are safe. - Age —
now - deadline ≥ 90 s(425 op_too_recent). The 90 s buffer matches the auto-sweeper’s safety window so a tx mined just after the off-chain deadline can never be double-counted. - Chain-watcher liveness — refuses the call if chain-watcher’s
own
/health/chain-syncreports a lag past the local threshold (503 chain_watcher_lagging). Without this gate an un-indexed confirmed split would look like a no-execute and could trigger a double-credit. - Chain-row absence —
LEFT JOINagainst the per-kind events table (chain_watcher.vault_position_split_eventsforSPLIT,chain_watcher.vault_positions_merged_eventsforMERGE) keyed by(vault, conditionId, amount). If a matching row exists the call returns409 op_confirmed_on_chainwith the chainevent_key,tx_hash, andblockNumberin the errordetailsso operators can re-drive the chain-event-router instead of unlocking duplicate funds.
EXPIRED with refunded=true and a
single balance_events row appears with reason
split_user_refund_verified (SPLIT) or merge_user_refund_verified
(MERGE) — distinct from the sweeper’s *_expired_refund so audit
queries can attribute the unlock to the user-driven path.
See the full request / response shape in the
API Reference.