Risk LabsProposer API

Permit2 signing

A bounded authorization for one request, outcome, and token transfer.

The quickstart and full example assume your bond token is already approved to Permit2 on Polygon: 0x000000000022D473030F116dDEE9F6B43aC78BA3.

Two different permissions

  1. ERC-20 approval: your wallet lets Permit2 spend a chosen amount of the bond token. This requires an on-chain transaction if allowance is insufficient. An existing allowance to the oracle does not count.
  2. Witness signature: you authorize SignedProposer to use that allowance for one exact oracle request and outcome, up to permit.amount, before permit.deadline.

The signature does not broadcast a transaction. Your private key stays in your signer. max_payment must currently be "0"; it is separate from the bond authorization.

Keep control of the authorization

The API has no unrestricted wallet access: Permit2 is the token allowance recipient, and SignedProposer is the spender bound into your signature. The signed terms fix the chain, contracts, bond token, amount ceiling, nonce, deadline, exact request, and proposed outcome. Review them against trusted configuration; do not sign solely because the API returned them. Prefer the required bond amount and a short deadline that satisfies the API's admission requirements.

You can reduce or revoke the bond token's ERC-20 allowance to Permit2, or invalidate an unused Permit2 nonce on-chain. These actions require your wallet and gas, and affect future execution only once effective on-chain; they cannot undo a transfer already executed or reliably stop a transaction that wins the race. An unexecuted authorization also expires at its signed deadline. Stopping your client or status polling does not revoke a signature already submitted.

Sign exactly this structure

Signing-data API calls are optional. You can construct this typed data locally using the current oracle request (including its exact timestamp and ancillary bytes), allowed outcome price, bond token and required amount, and trusted chain and contract configuration. Choose your own permit amount, unused nonce, and deadline within the API requirements, sign locally, and submit the matching HTTP payload directly. The API independently reconstructs and validates the signed witness; it does not require a prior preparation call.

For convenience, the v1 signing-data endpoint returns the complete typed_data. The batch signing-data endpoint returns context for constructing this same structure locally. Its primary type is PermitWitnessTransferFrom; its domain is Permit2, chain 137, and the trusted Permit2 contract. There is no domain version. The contract versions in the HTTP body are separate.

Permit2 witness types
const types = {
  PermitWitnessTransferFrom: [
    { name: 'permitted', type: 'TokenPermissions' },
    { name: 'spender', type: 'address' },
    { name: 'nonce', type: 'uint256' },
    { name: 'deadline', type: 'uint256' },
    { name: 'witness', type: 'Proposal' },
  ],
  TokenPermissions: [
    { name: 'token', type: 'address' },
    { name: 'amount', type: 'uint256' },
  ],
  Proposal: [
    { name: 'oracle', type: 'address' },
    { name: 'requester', type: 'address' },
    { name: 'identifier', type: 'bytes32' },
    { name: 'timestamp', type: 'uint256' },
    { name: 'ancillaryData', type: 'bytes' },
    { name: 'proposedPrice', type: 'int256' },
    { name: 'maxPayment', type: 'uint256' },
  ],
} as const;

message.spender is SignedProposer. domain.verifyingContract is Permit2. message.witness.oracle is the Managed Oracle. These are different roles. The witness identifier is the oracle’s bytes32 identifier, not the question ID used in the URL.

In v1, all Solidity integers arrive as decimal strings. Batch signing context uses a numeric signing.chain_id, and batch admission requires a numeric permit.deadline; amounts, nonces, and prices remain decimal strings. Convert them to bigint for your signing library; do not round amounts or nonces through JavaScript Number. The sample converts the bounded chain ID only after checking it is safe.

Full working example

View, copy, or download the complete Node.js client. It validates the returned typed data against independently configured request and contract values, checks existing allowance and balance, and saves the signed payload before submitting. It supports exact-payload recovery after a lost response.

Nonces and funding

Permit2 uses unordered nonces, not your transaction nonce. For nonce n, check nonceBitmap(owner, n >> 8) and ensure bit n & 255 is clear. Use a unique nonce for each distinct authorization, and never reuse a nonce while another candidate may execute. The example generates a random 256-bit nonce and checks the bitmap.

Multiple candidates share the same wallet balance and allowance. Reserve enough collateral for all requests that could execute; a successful preparation does not reserve funds. Do not reduce allowance or move the bond while awaiting execution.

On this page