The Ably Websocket API is being deprecated on July 1, 2026. See the migration guide →
import { Wallet, ZeroAddress, ZeroHash, randomBytes, hexlify } from "ethers";
async function fillOrder() {
// get the following from https://api.sx.bet/metadata
const EIP712FillHasherAddress = process.env.EIP712_FILL_HASHER_ADDRESS;
const chainId = Number(process.env.CHAIN_ID); // Mainnet — use 79479957 for testnet
const domainVersion = process.env.DOMAIN_VERSION;
const wallet = new Wallet(process.env.SX_PRIVATE_KEY);
const takerAddress = wallet.address;
const stakeWei = "50000000"; // 50 USDC
const marketHash = "0x0246b760b06009ece42d08e706563de1967e7f1b4799d0f559244e3f80bbc496"; // Liverpool vs Arsenal
const baseToken = "0x6629Ce1Cf35Cc1329ebB4F63202F3f197b3F050B"; // Mainnet — see References for testnet address
const desiredOdds = "83000000000000000000"; // ~1.20 decimal odds
const oddsSlippage = 5; // 5% slippage, so worst decimal odds ~1.14
const isTakerBettingOutcomeOne = true; // taker is betting that team 1 wins
const fillSalt = BigInt(hexlify(randomBytes(32))).toString();
const domain = {
name: "SX Bet",
version: domainVersion,
chainId,
verifyingContract: EIP712FillHasherAddress,
};
const types = {
Details: [
{ name: "action", type: "string" },
{ name: "market", type: "string" },
{ name: "betting", type: "string" },
{ name: "stake", type: "string" },
{ name: "worstOdds", type: "string" },
{ name: "worstReturning", type: "string" },
{ name: "fills", type: "FillObject" },
],
FillObject: [
{ name: "stakeWei", type: "string" },
{ name: "marketHash", type: "string" },
{ name: "baseToken", type: "string" },
{ name: "desiredOdds", type: "string" },
{ name: "oddsSlippage", type: "uint256" },
{ name: "isTakerBettingOutcomeOne", type: "bool" },
{ name: "fillSalt", type: "uint256" },
{ name: "beneficiary", type: "address" },
{ name: "beneficiaryType", type: "uint8" },
{ name: "cashOutTarget", type: "bytes32" },
],
};
const message = {
action: "N/A",
betting: "N/A",
stake: "N/A",
worstOdds: "N/A",
worstReturning: "N/A",
market: marketHash,
fills: {
stakeWei,
marketHash,
baseToken,
desiredOdds,
oddsSlippage,
isTakerBettingOutcomeOne,
fillSalt,
beneficiary: ZeroAddress,
beneficiaryType: 0,
cashOutTarget: ZeroHash,
},
};
const signature = await wallet.signTypedData(domain, types, message);
const apiPayload = {
market: marketHash,
baseToken,
isTakerBettingOutcomeOne,
stakeWei,
desiredOdds,
oddsSlippage,
taker: takerAddress,
takerSig: signature,
fillSalt,
};
const response = await fetch(`https://api.sx.bet/orders/fill/v2`, { // Mainnet — use https://api.toronto.sx.bet for testnet
method: "POST",
body: JSON.stringify(apiPayload),
headers: { "Content-Type": "application/json" },
});
}{
"status": "success",
"data": {
"fillHash": "0x840763ae29b7a6adfa0e315afa47be30cdebd5b793d179dc07dc8fc4f0034965",
"isPartialFill": false,
"totalFilled": "50000000",
"averageOdds": "73000000000000000000"
}
}Fill an existing order on the SX Bet orderbook as a taker, with built-in betting delay and odds matching.
import { Wallet, ZeroAddress, ZeroHash, randomBytes, hexlify } from "ethers";
async function fillOrder() {
// get the following from https://api.sx.bet/metadata
const EIP712FillHasherAddress = process.env.EIP712_FILL_HASHER_ADDRESS;
const chainId = Number(process.env.CHAIN_ID); // Mainnet — use 79479957 for testnet
const domainVersion = process.env.DOMAIN_VERSION;
const wallet = new Wallet(process.env.SX_PRIVATE_KEY);
const takerAddress = wallet.address;
const stakeWei = "50000000"; // 50 USDC
const marketHash = "0x0246b760b06009ece42d08e706563de1967e7f1b4799d0f559244e3f80bbc496"; // Liverpool vs Arsenal
const baseToken = "0x6629Ce1Cf35Cc1329ebB4F63202F3f197b3F050B"; // Mainnet — see References for testnet address
const desiredOdds = "83000000000000000000"; // ~1.20 decimal odds
const oddsSlippage = 5; // 5% slippage, so worst decimal odds ~1.14
const isTakerBettingOutcomeOne = true; // taker is betting that team 1 wins
const fillSalt = BigInt(hexlify(randomBytes(32))).toString();
const domain = {
name: "SX Bet",
version: domainVersion,
chainId,
verifyingContract: EIP712FillHasherAddress,
};
const types = {
Details: [
{ name: "action", type: "string" },
{ name: "market", type: "string" },
{ name: "betting", type: "string" },
{ name: "stake", type: "string" },
{ name: "worstOdds", type: "string" },
{ name: "worstReturning", type: "string" },
{ name: "fills", type: "FillObject" },
],
FillObject: [
{ name: "stakeWei", type: "string" },
{ name: "marketHash", type: "string" },
{ name: "baseToken", type: "string" },
{ name: "desiredOdds", type: "string" },
{ name: "oddsSlippage", type: "uint256" },
{ name: "isTakerBettingOutcomeOne", type: "bool" },
{ name: "fillSalt", type: "uint256" },
{ name: "beneficiary", type: "address" },
{ name: "beneficiaryType", type: "uint8" },
{ name: "cashOutTarget", type: "bytes32" },
],
};
const message = {
action: "N/A",
betting: "N/A",
stake: "N/A",
worstOdds: "N/A",
worstReturning: "N/A",
market: marketHash,
fills: {
stakeWei,
marketHash,
baseToken,
desiredOdds,
oddsSlippage,
isTakerBettingOutcomeOne,
fillSalt,
beneficiary: ZeroAddress,
beneficiaryType: 0,
cashOutTarget: ZeroHash,
},
};
const signature = await wallet.signTypedData(domain, types, message);
const apiPayload = {
market: marketHash,
baseToken,
isTakerBettingOutcomeOne,
stakeWei,
desiredOdds,
oddsSlippage,
taker: takerAddress,
takerSig: signature,
fillSalt,
};
const response = await fetch(`https://api.sx.bet/orders/fill/v2`, { // Mainnet — use https://api.toronto.sx.bet for testnet
method: "POST",
body: JSON.stringify(apiPayload),
headers: { "Content-Type": "application/json" },
});
}{
"status": "success",
"data": {
"fillHash": "0x840763ae29b7a6adfa0e315afa47be30cdebd5b793d179dc07dc8fc4f0034965",
"isPartialFill": false,
"totalFilled": "50000000",
"averageOdds": "73000000000000000000"
}
}Documentation Index
Fetch the complete documentation index at: https://docs.sx.bet/llms.txt
Use this file to discover all available pages before exploring further.
POST /orders/* endpoints share a combined limit of 5,500 requests/min. See Rate Limits.200 response means the API accepted your fill — the trade starts as PENDING and updates to SUCCESS or FAILED once confirmed on-chain. See Trade status for details.The market hash of the market you are filling an order on
The address of the ERC-20 token representing the currency of the fill. Token addresses vary by network — see the addresses field in GET /metadata or the References page for the correct address.
Whether or not taker is betting outcome 1 (team 1 wins), if false then taker is betting outcome 2 (team 2 wins)
The stake amount for this bet in wei units - see Unit Conversion. Minimum 1 USDC.
The worst taker odds acceptable for filling, used as an anchor when applying oddsSlippage - note that any order found with taker odds better than the desiredOdds can still fill if found at the time of order matching
An integer between 0-100 representing the percentage of tolerance that is acceptable based on desiredOdds
Random 32 byte string to identify this fill. Must match the fillSalt used when computing the signing payload
Address of the taker taking the bet
Your wallet signature over the fill payload.