The Ably Websocket API is being deprecated on July 1, 2026. See the migration guide →
import { Contract, JsonRpcProvider, MaxUint256, Wallet } from "ethers";
async function approveOrderFill() {
const privateKey = process.env.SX_PRIVATE_KEY;
const tokenAddress = process.env.TOKEN_ADDRESS;
// get the following from https://api.sx.bet/metadata
const tokenTransferProxyAddress = process.env.TOKEN_TRANSFER_PROXY_ADDRESS;
const chainId = Number(process.env.CHAIN_ID); // Mainnet — use 79479957 for testnet
const wallet = new Wallet(
privateKey,
new JsonRpcProvider(process.env.RPC_URL) // find this under the 'references' section
);
const tokenContract = new Contract(
tokenAddress,
[
{
inputs: [
{ internalType: "address", name: "usr", type: "address" },
{ internalType: "uint256", name: "wad", type: "uint256" },
],
name: "approve",
outputs: [{ internalType: "bool", name: "", type: "bool" }],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [{ internalType: "address", name: "owner", type: "address" }],
name: "nonces",
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "name",
outputs: [{ internalType: "string", name: "", type: "string" }],
stateMutability: "view",
type: "function",
},
],
wallet
);
const nonce = await tokenContract.nonces(wallet.address);
const tokenName = await tokenContract.name();
const domain = {
name: tokenName,
version: "1",
chainId,
verifyingContract: tokenAddress,
};
const types = {
Permit: [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
{ name: "value", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
],
};
const deadline = Math.floor(Date.now() / 1000) + 7200; // 2 hours
const value = {
owner: wallet.address,
spender: tokenTransferProxyAddress,
value: MaxUint256,
nonce,
deadline,
};
const approveProxySignature = await wallet.signTypedData(domain, types, value);
const apiPayload = {
owner: wallet.address,
spender: tokenTransferProxyAddress,
tokenAddress,
value: MaxUint256.toString(),
deadline,
signature: approveProxySignature,
};
const response = await fetch("https://api.sx.bet/orders/approve", { // Mainnet — use https://api.toronto.sx.bet for testnet
method: "POST",
body: JSON.stringify(apiPayload),
headers: { "Content-Type": "application/json" },
});
}{
"status": "success",
"data": {
"hash": "0x840763ae29b7a6adfa0e315afa47be30cdebd5b793d179dc07dc8fc4f0034965"
}
}Approve token spending via EIP-2612 permit for SX Bet order execution.
import { Contract, JsonRpcProvider, MaxUint256, Wallet } from "ethers";
async function approveOrderFill() {
const privateKey = process.env.SX_PRIVATE_KEY;
const tokenAddress = process.env.TOKEN_ADDRESS;
// get the following from https://api.sx.bet/metadata
const tokenTransferProxyAddress = process.env.TOKEN_TRANSFER_PROXY_ADDRESS;
const chainId = Number(process.env.CHAIN_ID); // Mainnet — use 79479957 for testnet
const wallet = new Wallet(
privateKey,
new JsonRpcProvider(process.env.RPC_URL) // find this under the 'references' section
);
const tokenContract = new Contract(
tokenAddress,
[
{
inputs: [
{ internalType: "address", name: "usr", type: "address" },
{ internalType: "uint256", name: "wad", type: "uint256" },
],
name: "approve",
outputs: [{ internalType: "bool", name: "", type: "bool" }],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [{ internalType: "address", name: "owner", type: "address" }],
name: "nonces",
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "name",
outputs: [{ internalType: "string", name: "", type: "string" }],
stateMutability: "view",
type: "function",
},
],
wallet
);
const nonce = await tokenContract.nonces(wallet.address);
const tokenName = await tokenContract.name();
const domain = {
name: tokenName,
version: "1",
chainId,
verifyingContract: tokenAddress,
};
const types = {
Permit: [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
{ name: "value", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
],
};
const deadline = Math.floor(Date.now() / 1000) + 7200; // 2 hours
const value = {
owner: wallet.address,
spender: tokenTransferProxyAddress,
value: MaxUint256,
nonce,
deadline,
};
const approveProxySignature = await wallet.signTypedData(domain, types, value);
const apiPayload = {
owner: wallet.address,
spender: tokenTransferProxyAddress,
tokenAddress,
value: MaxUint256.toString(),
deadline,
signature: approveProxySignature,
};
const response = await fetch("https://api.sx.bet/orders/approve", { // Mainnet — use https://api.toronto.sx.bet for testnet
method: "POST",
body: JSON.stringify(apiPayload),
headers: { "Content-Type": "application/json" },
});
}{
"status": "success",
"data": {
"hash": "0x840763ae29b7a6adfa0e315afa47be30cdebd5b793d179dc07dc8fc4f0034965"
}
}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.
value to be spent by spender on behalf of owner for token transfers that occur as part of the Filling orders flow according to Ethereum’s EIP-2612 Permit Extension. Note that deadline field here is only used during signature verification and that the value set will be the spender’s allowance until changed or revoked.Address of the taker granting approval to TokenTransferProxy for filling orders on their behalf
The address of the account which will be able to spend token amounts on behalf of the owner. In this case, the spender should be TokenTransferProxy address
The token address to grant approval for
The token amount to grant approval for, in Ethereum units
The deadline as a UNIX timestamp format used in signature verification
Your wallet signature over the payload. See the example of how to compute this.