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

# Guide: submit a private TWAP order

> Build a TWAP order, encrypt it with ECIES, sign it, and submit to the TWAP service.

This guide walks through submitting a TWAP order to the Tachyon TWAP service. The service is separate from the relayer, it has its own HTTP API and its own ECIES public key.

<Warning>
  The TypeScript SDK is WIP. The flow below shows the raw steps you can implement today; the SDK will wrap them.
</Warning>

## Prerequisites

* A wallet on the source chain (any wallet that can sign EIP-191 `personal_sign` messages)
* The source asset approved to the TWAP service's enclave address (see `GET /address` below)
* The `eciesEncrypt` helper and endpoint URLs from [endpoints and helpers](/reference/endpoints-and-helpers)

Base URL for the TWAP service is `https://twap.tachyon.pe` (replace `${TWAP_URL}` in the snippets below).

## 1. Fetch the TWAP service's ECIES public key

```ts theme={null}
const res = await fetch(`${TWAP_URL}/ecies-pubkey`);
const { publicKey } = await res.json(); // hex-encoded uncompressed pubkey
```

This pubkey is distinct from the relayer's ECIES pubkey. Orders must be encrypted to this key so only the TWAP service (inside the TEE) can decrypt them.

## 2. Fetch the TEE wallet address (for approval)

```ts theme={null}
const addrRes = await fetch(`${TWAP_URL}/address`);
const { address: teeAddress } = await addrRes.json();
```

The user must `approve` their source asset to this address so the service can pull funds on order creation.

## 3. Build the order

```ts theme={null}
const order = {
  user: userAddress,
  assetIn:  USDC_BASE,
  assetOut: USDC_HORIZEN,
  chainIn:  "base_mainnet",
  chainOut: "horizen_mainnet",
  sourceChainId: 8453,
  destChainId:   26514,
  totalAmountIn:   "1000000000000", // 1M USDC (6 decimals)
  amountPerPeriod:  "50000000000",  // 50k per tranche → 20 tranches
  period: 600,                       // 10 minutes between tranches
  startTime: Math.floor(Date.now() / 1000),
  endTime:   Math.floor(Date.now() / 1000) + 4 * 3600,
  recipients: ["0xRecipient..."],
  amounts:    ["999000000000"],      // min-out on destination (≈1% slippage)
};
```

Constraints enforced by the service:

* `period` ≥ 5 seconds
* `endTime` > `startTime`
* `recipients.length === amounts.length`, both > 0
* `amountPerPeriod` > 0
* `totalAmountIn` ≥ `amountPerPeriod`
* `chainIn` / `chainOut` must match the service's configured chain names

## 4. Encrypt the order

Serialize to JSON and encrypt with the service's ECIES pubkey:

```ts theme={null}
const plaintext = Buffer.from(JSON.stringify(order));
const encrypted = await eciesEncrypt(publicKey, plaintext); // library-specific
const encryptedHex = encrypted.toString("hex");
```

## 5. Sign the matching personal-sign message

Pick any canonical message, the service verifies that the personal-sign recovery matches `order.user`. A common pattern:

```ts theme={null}
const message = `TACHYON_TWAP:${order.user}:${order.startTime}`;
const signature = await wallet.signMessage(message);
```

## 6. Submit

```ts theme={null}
const createRes = await fetch(`${TWAP_URL}/orders`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    encrypted: encryptedHex,
    signature,
    message,
  }),
});

const { orderId, order: created } = await createRes.json();
```

The service:

1. Verifies the signature recovers to `order.user`
2. Decrypts the order
3. Pulls `totalAmountIn` in a single on-chain `transferFrom` from `order.user` to its TEE wallet
4. Schedules the first tranche (either immediately if `startTime` has passed, or at `startTime`)

## 7. Poll order status

```ts theme={null}
const res = await fetch(`${TWAP_URL}/orders/${orderId}`);
const { order } = await res.json();

console.log(order.status);          // "active" | "completed" | "cancelled"
console.log(order.tranchesFilled, "/", order.totalTranches);
console.log(order.amountSpent);
console.log(order.intentIds);       // BridgeIntent IDs, one per fired tranche
```

Each tranche is a `BridgeIntent` on the source chain bridge contract. Track individual tranche settlement through the [relayer's `GET /intent-details/:intentId`](/api/relayer) using each `intentId` from `order.intentIds`.

## 8. Cancel (optional)

```ts theme={null}
await fetch(`${TWAP_URL}/orders/${orderId}/cancel`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ signature, message }), // same recovery requirement
});
```

No further tranches fire. Tranches already fired continue to their own BridgeIntent settlement; they are not reversed.

## Solver reward and expected output

Per tranche, the service:

* Deducts **5% of `amountPerPeriod`** as the solver reward (included as `reward` on the `BridgeIntent`)
* Sets `expectedAmountB` on the `BridgeIntent` to the sum of `order.amounts[]` (your minimum acceptable output on destination)
* Sets auction duration to `period / 2` (minimum 20 seconds)

These are not currently configurable per-order.

## Recipients on destination

Recipients are stored encrypted in the relayer via [`POST /store-recipients`](/api/relayer) after each `createIntent` call. The winning solver fetches them via [`GET /get-recipients/:intentId`](/api/relayer) (which only returns the decrypted list to the auction winner) and delivers to those addresses on the destination chain.

<Card title="Open the hosted TWAP app" icon="globe" href="https://twap.tachyon.pe/">
  Walk through this flow without writing code.
</Card>
