EIP-2612 Permit
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"
}
}Orders
Approve order fill
Approve token spending via EIP-2612 permit for SX Bet order execution.
POST
/
orders
/
approve
EIP-2612 Permit
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"
}
}If you don’t wish to do this programmatically, you can simply go to sx.bet (or toronto.sx.bet for testnet), place a test bet with the account and token you’ll be using, and you will be good to go.If you want to do it programmatically, see the code sample below.
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.Body
application/json
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.
Last modified on April 15, 2026
⌘I
