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.
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/me/splits-merges/{opId}/unlock
Supported opKind: SPLIT and MERGE. CONVERT / REDEEM
follow in subsequent releases.
The endpoint runs a chain of safety gates before releasing the lock so
an operation that did succeed on-chain can never be unlocked
again (which would double-credit the vault):
- Ownership — the authenticated wallet must own the op’s
vault (
403 vault_owner_mismatch). - Op kind —
SPLITorMERGEonly (400 op_kind_unsupported). - Already terminal — an op that is already refunded, or already
CONFIRMEDon-chain, returns an idempotent200withrefundAppliedNow: 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. - Indexing caught up — refuses the call while the platform’s
chain indexing is lagging (
503 chain_watcher_lagging; retry later). Without this gate a confirmed split that is not indexed yet would look like one that never executed and could be credited twice. - Not executed on-chain — if a matching
PositionSplit/PositionsMergedevent exists for the vault, condition and amount, the call returns409 op_confirmed_on_chainwith the event’stxHashandblockNumberin the errordetails: the operation succeeded, so there is nothing to unlock.
EXPIRED and its locked balance is
released back to available.
See the full request / response shape under Vault → Release an EXPIRED
split / merge lock in the API Reference.