Skip to content

Getting Started

This guide walks you through the complete setup process: creating a proxy wallet, authenticating with L1/L2, and submitting orders.

Overview

All orderbook write operations (placing orders, canceling orders, etc.) require:

  1. Proxy Wallet - A smart contract wallet created from your EOA
  2. L2 Authentication - API credentials for authenticated requests
Complete Flow:
  1. Create Proxy Wallet - Set up a proxy wallet from your EOA (required for all users)
  2. Approve Tokens - Approve token spending allowances (required before trading)
  3. L1 Authentication - Get a nonce and authenticate with wallet signature
  4. Generate API Key - Use L1 credentials to generate L2 API key and secret
  5. L2 Authentication - Use API key and secret to sign requests with HMAC
  6. Submit Orders - Use L2 credentials to place and manage orders
Important:
  • Before starting authentication, make sure you have created a proxy wallet. See the Proxy Wallet Guide for detailed instructions.
  • Before placing orders, you must approve token spending. See the Token Approvals Guide for detailed instructions.

Step 0: Create Proxy Wallet (Required)

Before authenticating, you must create a proxy wallet from your EOA. The proxy wallet address will be used as the maker in order creation.

Quick Start:
import { getOrCreateProxyWallet } from './proxy-wallet';
 
const eoaAddress = '0x...' as `0x${string}`;
const proxyWalletAddress = await getOrCreateProxyWallet(eoaAddress);
console.log('Proxy wallet:', proxyWalletAddress);

For detailed instructions, see the Proxy Wallet Creation Guide.

Important Address Usage:
  • Authentication (prob_address): Always use your EOA address
  • Order maker field: Use your proxy wallet address
  • Order signer field: Use your EOA address

Step 0.5: Approve Token Spending (Required)

After creating your proxy wallet, you must approve token spending allowances before you can place orders.

Quick Start:
import { approveAllTokens } from './token-approvals';
 
await approveAllTokens(eoaAddress, proxyWalletAddress, account);

For detailed instructions, see the Token Approvals Guide.

Required Approvals:
  • USDT → CTF Token contract (for splitting/merging)
  • USDT → CTF Exchange (for trading)
  • CTF Tokens → CTF Exchange (for trading)

Step 1: L1 Authentication

L1 authentication uses EIP-712 signature verification with your wallet.

1.1 Get a Nonce

First, request a nonce from the API:

curl "https://api.probable.markets/public/api/v1/auth/nonce"
Response:
{
  "nonce": "abc123xyz",
  "issuedAt": "2025-01-15T10:30:00Z"
}

1.2 Sign the Authentication Message

Create an EIP-712 signature using your wallet. The message structure should include:

  • Your wallet address
  • The nonce from step 1.1
  • Current timestamp
  • Domain: prob.vbgf.cc
Example (JavaScript with ethers.js):
import { ethers } from 'ethers';
 
const domain = {
  name: 'Prob',
  version: '1',
  chainId: 56, // BSC mainnet
  verifyingContract: '0x...' // Your contract address
};
 
const types = {
  Message: [
    { name: 'account', type: 'address' },
    { name: 'namespace', type: 'string' },
    { name: 'chainId', type: 'string' },
    { name: 'address', type: 'address' }
  ]
};
 
const message = {
  account: eoaAddress, // Use EOA address for authentication
  namespace: 'default',
  chainId: '56',
  address: eoaAddress // Use EOA address
};
 
const signature = await signer._signTypedData(domain, types, message);

1.3 Login with Signature

Submit the login request with your signature:

curl -X POST "https://api.probable.markets/public/api/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "identity": {
      "account": "0x1234...",
      "namespace": "default",
      "chainId": "56",
      "address": "0x1234..."
    },
    "message": "Sign in to Prob...",
    "signature": "0xabc123...",
    "nonce": "abc123xyz",
    "issuedAt": "2025-01-15T10:30:00Z",
    "domain": "prob.vbgf.cc"
  }'

Step 2: Generate L2 API Key

Once you're authenticated with L1, you can generate an API key for L2 authentication.

2.1 Prepare L1 Headers

For L1-authenticated requests, you need these headers:

  • prob_address - Your EOA address (signed by EOA)
  • prob_signature - EIP-712 signature signed by EOA
  • prob_timestamp - Current Unix timestamp
  • prob_nonce - Nonce from step 1.1

2.2 Generate API Key

curl -X POST "https://api.probable.markets/public/api/v1/auth/api-key/56" \
  -H "prob_address: 0xEOA_ADDRESS..." \
  -H "prob_signature: 0xabc123..." \
  -H "prob_timestamp: 1705312200" \
  -H "prob_nonce: abc123xyz"

Note: Use your EOA address in prob_address for authentication. The signature must be signed by the EOA.

Response:
{
  "apiKey": "pk_live_abc123xyz",
  "secret": "sk_live_def456uvw",
  "passphrase": "my-passphrase"
}

⚠️ Important: Store the secret and passphrase securely. They cannot be retrieved later. You'll need these for all L2-authenticated requests.


Step 3: L2 Authentication (HMAC Signing)

L2 authentication uses HMAC-SHA256 to sign requests with your API key and secret.

3.1 Create HMAC Signature

The signature is created by hashing a combination of:

  • Timestamp
  • HTTP method (GET, POST, etc.)
  • Request path
  • Request body (if any)
Example (JavaScript):
import crypto from 'crypto';
 
function createL2Signature(timestamp, method, path, body, secret, passphrase) {
  const message = `${timestamp}${method}${path}${body ? JSON.stringify(body) : ''}`;
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(message);
  return hmac.digest('hex');
}
 
const timestamp = Math.floor(Date.now() / 1000).toString();
const method = 'POST';
const path = '/public/api/v1/order/56';
const body = { /* your order data */ };
 
const signature = createL2Signature(
  timestamp,
  method,
  path,
  body,
  apiSecret,
  passphrase
);

3.2 Prepare L2 Headers

For L2-authenticated requests, include these headers:

  • prob_address - Your EOA address (same as L1)
  • prob_signature - HMAC signature (from step 3.1)
  • prob_timestamp - Current Unix timestamp
  • prob_api_key - Your API key
  • prob_passphrase - Your passphrase

Step 4: Submit an Order

Now you can use L2 credentials to place orders.

4.1 Prepare Order Data

{
  "deferExec": false,
  "order": {
    "salt": "1234567890",
    "maker": "0xPROXY_WALLET...",
    "signer": "0xEOA_ADDRESS...",
    "taker": "0x0000...",
    "tokenId": "0xabc123...",
    "makerAmount": "1000000000000000000",
    "takerAmount": "500000000000000000",
    "side": "BUY",
    "expiration": "1735689600",
    "nonce": "0",
    "feeRateBps": "175",
    "signatureType": 0,
    "signature": "0xdef456..."
  },
  "owner": "0xPROXY_WALLET...",
  "orderType": "GTC"
}

4.2 Create HMAC Signature

const timestamp = Math.floor(Date.now() / 1000).toString();
const method = 'POST';
const path = '/public/api/v1/order/56';
const body = JSON.stringify(orderData);
 
const signature = createL2Signature(
  timestamp,
  method,
  path,
  body,
  apiSecret,
  passphrase
);

4.3 Submit Order

curl -X POST "https://api.probable.markets/public/api/v1/order/56" \
  -H "Content-Type: application/json" \
  -H "prob_address: 0xEOA_ADDRESS..." \
  -H "prob_signature: 0xhmac_signature..." \
  -H "prob_timestamp: 1705312200" \
  -H "prob_api_key: pk_live_abc123xyz" \
  -H "prob_passphrase: my-passphrase" \
  -d '{
    "deferExec": false,
    "order": {
      "maker": "0xPROXY_WALLET...",
      "signer": "0xEOA_ADDRESS...",
      ...
    },
    "owner": "0xPROXY_WALLET...",
    "orderType": "GTC"
  }'
Note:
  • prob_address header: Use your EOA address
  • Order maker field: Use your proxy wallet address
  • Order signer field: Use your EOA address
  • Order owner field: Use your proxy wallet address
Response:
{
  "orderId": 12345,
  "symbol": "BTC-USD",
  "side": "BUY",
  "status": "NEW",
  ...
}

Complete Example

Here's a complete example in JavaScript:

import crypto from 'crypto';
import axios from 'axios';
 
const BASE_URL = 'https://api.probable.markets';
const eoaAddress = '0x...'; // Your EOA address
const proxyWalletAddress = await getOrCreateProxyWallet(eoaAddress); // Get or create proxy wallet
const chainId = 56;
 
// Step 1: Get nonce
const { data: nonceData } = await axios.get(`${BASE_URL}/public/api/v1/auth/nonce`);
const nonce = nonceData.nonce;
 
// Step 2: Sign with wallet (using ethers.js)
const signature = await signer._signTypedData(domain, types, message);
 
// Step 3: Login (use EOA address for authentication)
await axios.post(`${BASE_URL}/public/api/v1/auth/login`, {
  identity: { account: eoaAddress, namespace: 'default', chainId: '56', address: eoaAddress },
  message: 'Sign in to Prob...',
  signature,
  nonce,
  issuedAt: new Date().toISOString(),
  domain: 'prob.vbgf.cc'
});
 
// Step 4: Generate API key (with L1 headers)
const timestamp = Math.floor(Date.now() / 1000).toString();
const { data: apiKeyData } = await axios.post(
  `${BASE_URL}/public/api/v1/auth/api-key/${chainId}`,
  {},
  {
    headers: {
      prob_address: eoaAddress, // Use EOA address for authentication
      prob_signature: signature,
      prob_timestamp: timestamp,
      prob_nonce: nonce
    }
  }
);
 
const { apiKey, secret, passphrase } = apiKeyData;
 
// Step 5: Create L2 signature function
function createL2Signature(timestamp, method, path, body, secret, passphrase) {
  const message = `${timestamp}${method}${path}${body ? JSON.stringify(body) : ''}`;
  return crypto.createHmac('sha256', secret).update(message).digest('hex');
}
 
// Step 6: Place order (with L2 headers)
const orderData = {
  deferExec: false,
  order: { 
    /* order details */
    maker: proxyWalletAddress, // Use proxy wallet address
    signer: eoaAddress, // Use EOA address (signs the order)
    // ... other order fields
  },
  owner: proxyWalletAddress, // Use proxy wallet address
  orderType: 'GTC'
};
 
const orderTimestamp = Math.floor(Date.now() / 1000).toString();
const orderPath = `/public/api/v1/order/${chainId}`;
const orderSignature = createL2Signature(
  orderTimestamp,
  'POST',
  orderPath,
  orderData,
  secret,
  passphrase
);
 
const { data: orderResponse } = await axios.post(
  `${BASE_URL}${orderPath}`,
  orderData,
  {
    headers: {
      'Content-Type': 'application/json',
      prob_address: eoaAddress, // Use EOA address for authentication
      prob_signature: orderSignature,
      prob_timestamp: orderTimestamp,
      prob_api_key: apiKey,
      prob_passphrase: passphrase
    }
  }
);
 
console.log('Order placed:', orderResponse);

Next Steps