> ## Documentation Index
> Fetch the complete documentation index at: https://docs.adipredictstreet.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Orders and vault operations

> Sign orders per market generation, and withdraw, redeem or split / merge from a chosen vault with the vault field.

## Orders

The struct, domain `name` and `version` are the same for both
generations, binary and neg-risk:

```solidity theme={null}
struct Order {
    uint256 salt;
    address maker;
    address signer;
    address taker;
    uint256 tokenId;
    uint256 makerAmount;
    uint256 takerAmount;
    uint256 expiration;
    uint256 feeRateBps;
    uint8   side;             // 0=BUY, 1=SELL
    uint8   signatureType;    // always 1 (VAULT); 0 (EOA) is rejected
}
```

Two values depend on the market's generation
([how to detect it](https://docs.adipredictstreet.com/concepts/vault-gen2/market-generation.md)):

* `maker`, in the struct and in the request body: your vault of that
  generation;
* `domain.verifyingContract`: the market's exchange.

```typescript theme={null}
// marketExchange / marketGeneration: see Detect a market's generation
const exchange = marketExchange(market);
const maker    = vaults.find(v => v.generation === marketGeneration(market))!.vaultAddress;
const domain   = { name: 'PredictStreet', version: '1', chainId: contracts.chainId, verifyingContract: exchange };

const order = {
  salt, maker, signer: eoa.address,
  taker: '0x0000000000000000000000000000000000000000',
  tokenId: BigInt(tokenId),
  makerAmount, takerAmount, expiration: 0n,
  feeRateBps: BigInt(market.feeTakerBps),
  side: 0, signatureType: 1,
};
const signature = await eoa.signTypedData(domain, types, order);   // `types`: the Order type from EIP-712 signing
// POST /api/orders/place with `maker` = the same vault, as on Placing orders
```

Everything else is as in [Placing orders](https://docs.adipredictstreet.com/concepts/trading/placing-orders.md)
and [EIP-712 signing](https://docs.adipredictstreet.com/concepts/trading/eip712-signing.md).

| You send | Answer |
| - | - |
| The other generation's vault as `maker` | `409 vault_generation_mismatch`. `error.details.vaultAddress` is the vault to use; also `marketGeneration`, `exchangeAddress`, `vaults[]`. Re-sign with that maker |
| A generation-2 market before you upgraded | `409 vault_generation_missing`. [Upgrade](https://docs.adipredictstreet.com/concepts/vault-gen2/upgrade.md) first |
| The right maker, signed against the other generation's exchange | `201`, `status: REJECTED`, `code: bad_signature` |
| Body `maker` that is neither the authenticated wallet nor one of your vaults | `401 maker_mismatch` |
| A canary market while your wallet is not allowlisted | `409 vault_gen2_not_enabled` or `400 market_not_tradeable` |
| An order that increases a position after the canary was closed | `409 vault_gen2_canary_closed`. Only position-reducing orders are accepted |

Cancels (`POST /api/orders/cancel`, `cancel-batch`, `cancel-all`) are
keyed by your wallet: they cover orders of both vaults, before and after
the upgrade. `GET /api/orders/open` lists the orders of both vaults; each
order's `maker` shows which vault it rests on.

## The `vault` field

Withdraw, redeem and split / merge / convert take an optional `vault`:
one of your `vaults[].vaultAddress`.

| `vault` | Used vault |
| - | - |
| Omitted, withdraw | The active vault |
| Omitted, redeem or split / merge / convert | The vault that holds the market's tokens |
| Not one of yours | `404 vault_not_found` |
| Malformed | `400 invalid_vault` |

## Withdraw

`POST /api/withdrawals/request` (`withdraw:write` or `vault:write`), the
[usual request](https://docs.adipredictstreet.com/concepts/withdrawals/eip712.md) plus `vault`. `userSig` is
`WithdrawERC20` signed in the domain of that vault:

```typescript theme={null}
const userSig = await eoa.signTypedData(
  { name: 'PredictStreetVault', version: '1', chainId: contracts.chainId, verifyingContract: vault },
  WITHDRAW_ERC20_TYPES,   // the WithdrawERC20 type from WithdrawERC20 EIP-712
  { token: contracts.usdc, to, amount: parseUnits(amount, 6), salt, deadline },
);
await fetch(`${BASE}/api/withdrawals/request`, {
  method: 'POST', headers: auth,
  body: JSON.stringify({ amount, destination: to, salt: salt.toString(), expiry: Number(deadline), userSig, vault, totpCode }),
});
```

| Vault | Allowed `destination` |
| - | - |
| Generation 1 | As today: any address your whitelist allows |
| Generation 2 | Your own wallet, or an address the platform has listed (on chain: `VaultFactoryGen2.isWithdrawDestination(address)`). You cannot list one yourself |

* Any other destination from a generation-2 vault:
  `400 withdrawal_destination_not_allowed` (`details: { destination,
  vaultAddress, generation }`). It is checked before the 2FA code, so
  the code is not spent. Withdraw to your wallet and transfer from there.
* A listed destination can be removed at any time. A refused
  destination is final for that request.
* 2FA and whitelist rules are unchanged. A TOTP code is single-use per
  30-second step: a second withdrawal in the same step needs the next
  code or a backup code.
* `503 withdrawal_unknown`: the withdrawal may have been created. Do not
  sign a new one; a new salt is a second withdrawal. Re-send the same
  body (same `salt`, `expiry`, `userSig`, `totpCode`) for up to about
  60 seconds, then check `GET /api/me/withdrawals`. A stored request
  answers with its withdrawal (`details.withdrawalId`).
* `503 withdrawal_temporarily_unavailable`: nothing was created. Retry
  later, with a new 2FA code if 2FA is on.
* Cancel (`POST /api/me/withdrawals/{id}/cancel`) now also accepts
  `withdraw:write`.

## Redeem

`POST /api/me/positions/redeem` (`positions:write` or `vault:write`),
then poll `GET /api/me/positions/redeem/{jobId}` until `confirmed`.
Redeem a legacy market on the generation-1 vault and a generation-2
market on the generation-2 vault; the payout lands in that vault.
API-key callers sign a `RedeemAuthorization` (the relay is gasless):

```typescript theme={null}
const sig = await eoa.signTypedData(
  { name: 'PredictStreetRedeem', version: '1', chainId: contracts.chainId },   // no verifyingContract
  { RedeemAuthorization: [
      { name: 'owner', type: 'address' }, { name: 'marketId', type: 'string' },
      { name: 'outcomeIndex', type: 'uint8' }, { name: 'deadline', type: 'uint256' } ] },
  { owner: eoa.address, marketId, outcomeIndex, deadline },
);
const { v, r, s } = Signature.from(sig);
const body = { marketId, outcomeIndex, vault, auth: { deadline, v, r, s } };
```

Refusals: `400 redeem_signature_required`, `400 redeem_expired`,
`403 redeem_signer_mismatch`. Sign a fresh authorization.

## Split / merge / convert

The [existing flow](https://docs.adipredictstreet.com/concepts/portfolio/positions.md) with
`vault` added at each step:

1. `POST /api/vault/split-signature` (or `merge-signature`,
   `convert-signature`) with `{ marketId, amount, vault }` and an
   `Idempotency-Key` header. For neg-risk markets, supply `outcomeIndex` (uint8,
   0..254) to specify the sub-question. The answer carries `vaultAddress`, the
   payload and `backendSig`.
2. Sign the typed data in the domain of `vaultAddress`
   (`PredictStreetVault` / `1`). Check it hashes to `digest`.
3. `POST /api/me/positions/op-submit` with
   `{ signedOpId, ownerSig, backendSig, vault }`, then poll
   `GET /api/me/positions/op-submit/{jobId}` until `confirmed`.

## Balances and positions

| Request | Answer |
| - | - |
| `GET /api/me/balances?vaults=1` | Adds `vaults: [{ vaultAddress, generation, active, balances, balanceSource }]` |
| `GET /api/me/balances?vault=0x…` | One vault |
| `GET /api/me/positions` | Positions across both vaults. On a two-vault wallet each position carries `vaultAddress`, `vaultGeneration` and `vaultHoldings[]` |

## Legacy markets after the upgrade

* Quote and trade them as before, with the generation-1 vault as maker
  and the generation-1 exchange as `verifyingContract`.
* Resting generation-1 orders stay live and cancel as usual.
* When a legacy market resolves, redeem with `vault` = the generation-1
  vault, then withdraw or move what is left.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.