Domain
const domain = {
name: 'PredictStreetVault',
version: '1',
chainId: 99999,
verifyingContract: '<YOUR VAULT ADDRESS>',
};
verifyingContract is your vault clone, not the factory. Fetch
via GET /api/me → vaultAddress, or on-chain via
VaultFactory.vaultOf(eoa).
Struct
struct WithdrawERC20 {
address token;
address to;
uint256 amount;
uint256 salt;
uint256 deadline;
}
salt is not a sequential nonce — it’s a one-time uniqueness
field you choose. The vault tracks usedDigests[digest].
Signing example
const types = {
WithdrawERC20: [
{ name: 'token', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' },
{ name: 'salt', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
};
const message = {
token: USDC_ADDRESS,
to: destinationWallet,
amount: BigInt('100000000'),
salt: BigInt(`0x${randomBytes(32).toString('hex')}`),
deadline: BigInt(Math.floor(Date.now() / 1000) + 86400),
};
const signature = await wallet.signTypedData(domain, types, message);
Request
POST /api/withdrawals/request
# Withdrawal endpoints aren't exposed on the partner API today.
Content-Type: application/json
{
"userWallet": "0x...",
"amount": "100",
"destination": "0x...",
"userSignature": "0x...",
"salt": "0x...",
"deadline": 1713800000
}
Response mirrors the current state after the request. Subscribe to
withdrawals.me WebSocket channel for transitions.