Skip to main content
The TWAP service is a separate Tachyon product from the relayer. It has its own base URL, its own ECIES public key, and its own TEE wallet. Each TWAP order pulls funds upfront and drips them as BridgeIntents on a fixed schedule.
Base URL: https://twap.tachyon.pe. Testnet and mainnet share the same host; select the network via the chain IDs you pass in each order.
Authentication: no API key. Order creation and cancellation are signature-protected (EIP-191 personal_sign recovery must match the user field of the order).

Conventions

  • All bodies are JSON.
  • Amounts are decimal strings in the asset’s base units.
  • Chain IDs are numeric (e.g., 8453 for Base, 26514 for Horizen).
  • ChainIn / chainOut are string names that must match the service’s configured chain names (e.g., "base_mainnet", "horizen_mainnet").

Read endpoints

GET /ecies-pubkey

Returns the TWAP service’s ECIES public key. Encrypt order params with this before submitting. 200 response:
{
  "success": true,
  "publicKey": "04abc..."
}
This key is distinct from the relayer’s ECIES key, TWAP orders must be encrypted to this key specifically.

GET /address

Returns the TWAP service’s TEE wallet address. The user must approve their source asset to this address for at least totalAmountIn before submitting an order. 200 response:
{
  "success": true,
  "address": "0xTEE..."
}

GET /orders/:orderId

Returns the current state of an order. 200 response:
{
  "success": true,
  "order": {
    "id": "twap-0xAbCd1234-1712345678901234567",
    "user": "0xUser...",
    "assetIn":  "0xToken...",
    "assetOut": "0xToken...",
    "chainIn":  "base_mainnet",
    "chainOut": "horizen_mainnet",
    "sourceChainId": 8453,
    "destChainId":   26514,
    "totalAmountIn":   "1000000000000",
    "amountPerPeriod":  "50000000000",
    "period": 600,
    "startTime": 1712345678,
    "endTime":   1712360078,
    "recipients": ["0xRecipient..."],
    "amounts":    ["999000000000"],
    "status": "active",
    "tranchesFilled": 7,
    "totalTranches":  20,
    "amountSpent": "350000000000",
    "intentIds":  ["42", "43", "44", "45", "46", "47", "48"],
    "createdAt": "2026-04-05T09:00:00Z",
    "lastExecutedAt": "2026-04-05T10:10:00Z"
  }
}
statusactive, completed, cancelled. Each entry in intentIds is a BridgeIntent ID on the source-chain bridge, trace per-tranche settlement via GET /intent-details/:intentId on the relayer.

GET /orders?user=0x...

Lists all orders for a user (active and historical). 200 response:
{
  "success": true,
  "orders": [ /* same shape as GET /orders/:orderId's `order` */ ]
}

Write endpoints

POST /orders

Submit a new TWAP order. The body contains the ECIES-encrypted order payload plus a personal-sign signature that recovers to the user field inside the decrypted payload. Body:
{
  "encrypted": "0x...",
  "signature": "0x...",
  "message":   "TACHYON_TWAP:0xUser...:1712345678"
}
  • encrypted, hex-encoded ECIES ciphertext over TWAPOrderParams JSON, encrypted with the key from GET /ecies-pubkey.
  • signature, EIP-191 personal_sign of message.
  • message, any string; recovery from (message, signature) must equal order.user inside the encrypted payload.
TWAPOrderParams (the decrypted plaintext):
interface TWAPOrderParams {
  user: string;
  assetIn: string;
  assetOut: string;
  chainIn: string;
  chainOut: string;
  sourceChainId: number;
  destChainId: number;
  totalAmountIn: string;
  amountPerPeriod: string;
  period: number;          // seconds, minimum 5
  startTime: number;       // unix seconds
  endTime: number;         // unix seconds, > startTime
  recipients: string[];    // non-empty, same length as amounts
  amounts: string[];
  permitSignature?: string;
}
Service-side actions on acceptance:
  1. Verify signature recovers to order.user
  2. Decrypt the order and validate fields
  3. Pull totalAmountIn in a single transferFrom(order.user → TEE wallet) on sourceChainId
  4. Start the periodic executor (fires first tranche immediately if startTime has passed, else waits)
200 response:
{
  "success": true,
  "orderId": "twap-0xAbCd1234-1712345678901234567",
  "order":   { /* same shape as GET /orders/:orderId */ }
}
Errors:
  • 400, invalid signature, signature/user mismatch, decryption failure, validation failure, or transferFrom revert (user hasn’t approved enough, insufficient balance, etc.)

POST /orders/:orderId/cancel

Cancel an active order. The caller must re-prove ownership with a signature recoverable to order.user. Body:
{
  "signature": "0x...",
  "message":   "TACHYON_TWAP_CANCEL:<orderId>"
}
200 response:
{
  "success": true
}
Tranches already fired continue their own BridgeIntent settlement; they are not reversed. No further tranches are scheduled.

Order execution details

Each tranche fired by the service:
  • Approves the source-chain bridge contract for amountPerPeriod
  • Calls createIntent on the bridge with:
    • amountA = amountPerPeriod - reward
    • reward = 5% of amountPerPeriod (solver incentive)
    • expectedAmountB = sum of order.amounts[]
    • auctionDuration = period / 2 (minimum 20 seconds)
  • Stores the encrypted recipient bundle against the new intentId via the relayer’s POST /store-recipients
Recipients are never posted on-chain in plaintext.

Limits and behaviour

  • Minimum period: 5 seconds
  • Maximum period: none, but endTime caps total runtime
  • First tranche fires at startTime (or immediately if startTime is in the past)
  • Subsequent tranches fire every period seconds
  • Execution stops on whichever comes first: tranchesFilled >= totalTranches, endTime passed, or explicit cancel
  • 5% per-tranche reward and auctionDuration = period/2 are not currently user-configurable

Privacy note

The initial transferFrom(user → TEE) is a single on-chain transfer, visible on the source chain. This reveals that the user moved some amount to the TEE but not the TWAP pattern (no cadence or per-tranche amounts visible), and individual tranches settle to fresh stealth addresses on the destination, unlinkable to each other or to the user.

Product page

Why Private TWAP, what’s confidential, and where it fits.

Integration guide

Step-by-step walkthrough with encryption + signing examples.