VORQ Docs
Reference

Signing

EIP-712 domains, type strings, typehashes and the USDC payment authorization used by the VORQ contracts.

Every client and provider action carries an EIP-712 signature from the acting party. reclaim and the curation functions are the only unsigned entry points. Freshness and replay rules are on Security model.

Domains

Each contract has its own domain:

EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)
ContractnameversionchainIdverifyingContractTypes verified
JobRegistryVORQ Jobs2block.chainid at deploythe JobRegistryOrder, Claim, Settle, Fail, Cancel
ProviderRegistryVORQ Providers2block.chainid at deploythe ProviderRegistryRequestCapacity, SetIdentity
AskRegistryVORQ Asks2block.chainid at deploythe AskRegistryAskSnapshot

DOMAIN_SEPARATOR is computed once in the constructor. Each contract exposes EIP712_NAME, EIP712_VERSION, DOMAIN_SEPARATOR and ERC-5267 eip712Domain() (fields = 0x0f, no salt, no extensions). Read the domain from the contract and check that the separator rebuilt from eip712Domain() equals DOMAIN_SEPARATOR; a mismatch means a wrong address or chain. Published separators are on Deployments.

Digest and signature format

digest = keccak256(0x19 0x01 || DOMAIN_SEPARATOR || hashStruct(message))
sig    = r || s || v     // 65 bytes, v = 27 or 28

Any other length or v recovers address(0), which every entry point refuses. s is not range-checked; sign with low s.

Type strings

Members are encoded in declaration order. The strings below are byte-exact (no spaces after commas).

JobRegistry (VORQ Jobs)

Order(bytes32 c,uint32 modelId,uint32 slaSecs,uint128 rateIn,uint128 rateOut,uint32 unitsIn,uint32 unitsOut,uint32 designated,uint64 expiresAt)
Claim(bytes32 jobId,uint64 issuedAt)
Settle(bytes32 jobId,uint32 completionTok,uint64 issuedAt)
Fail(bytes32 jobId,uint64 issuedAt)
Cancel(bytes32 jobId,uint64 issuedAt)
TypeSigned by
Orderclient; the recovered signer must equal the owner argument of post
Claimany registered operator; resolved to a provider id
Settle, Failthe operator of the provider that claimed the job
Cancelthe job owner

Order fields:

FieldTypeMeaning
cbytes32Payload commitment; jobId = keccak256(abi.encodePacked(owner, c)). Fresh per job.
modelIduint32Catalog model id; must exist and be enabled at post.
slaSecsuint32Must be an allowed SLA at post.
rateInuint128Atomic units per input unit × RATE_SCALE.
rateOutuint128Atomic units per output unit × RATE_SCALE.
unitsInuint32Input units, charged in full.
unitsOutuint32Maximum output units.
designateduint32Provider id, or 0 for any provider.
expiresAtuint64Unix seconds; at most MAX_EXPIRY (86,400 s) ahead at post.

Not signed: the Solidity Order struct's tenth member taskCid, the authSig argument of post, and the resultCid argument of submitAndSettle. See unsigned parameters.

ProviderRegistry (VORQ Providers)

RequestCapacity(uint32 n,uint64 issuedAt)
SetIdentity(bytes32 boxKey,bytes evidence,uint64 issuedAt)

Signed by the provider operator. evidence is dynamic and is encoded as keccak256(evidence).

AskRegistry (VORQ Asks)

Ask(uint32 modelId,uint32 sla,uint128 rateIn,uint128 rateOut)
AskSnapshot(uint32 providerId,uint64 signedAt,Ask[] quotes)Ask(uint32 modelId,uint32 sla,uint128 rateIn,uint128 rateOut)

Signed by the operator of providerId. The second line is the full encoded type hashed into SNAPSHOT_TYPEHASH. quotes is encoded as keccak256 of the concatenated hashStruct(Ask) values in array order, so reordering quotes changes the digest.

Typehashes

TypeConstantValue
OrderJobRegistry.ORDER_TYPEHASH0x3e16d11d9c120ac2d4b3537e27a5e2f4f865f70a23849726350cf94b6dc5e90c
ClaimJobRegistry.CLAIM_TYPEHASH0x9fb2253ddb001029db8b11481f856ed57697534c3cb8d823bd30a9dd3ff5ce0f
SettleJobRegistry.SETTLE_TYPEHASH0x85d2b9b4329dab43c509568a439b97db47deb525379a4774834969a4aeb5b756
FailJobRegistry.FAIL_TYPEHASH0x7b55bc6e850c33643ce0bcd3046c87851a9659ecb6d541612cdb4bef5a8d1269
CancelJobRegistry.CANCEL_TYPEHASH0x571302868b9c6729294600c0b1e1dcd58dfe44e82f479f1f68c7b68669562a94
RequestCapacityProviderRegistry.REQUEST_CAPACITY_TYPEHASH0x1cc140b451b7b8bb60ac573bbe29ca7b37d6d80863e1015939a6b91d18586075
SetIdentityProviderRegistry.SET_IDENTITY_TYPEHASH0x69fa5ea756b6031e7f884ca04caa57f17c175a6415125c3246d07776881afd1d
AskAskRegistry.ASK_TYPEHASH0x4fadd8d1027fd2cdf6dc25ac6cb3d6ec9086a535c3f8e1c0e5cb80bb9acb2ef4
AskSnapshotAskRegistry.SNAPSHOT_TYPEHASH0xfa55cbb0125320b1af05486ce48f1e655c7143a975aa0442af5801ceac80236e
EIP712Domain—0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f

Payment authorization (EIP-3009)

Escrow is pulled with the payment token's receiveWithAuthorization. The client signs it before post, post stores it, and claim executes it once. It is signed on the token's domain:

domain = { name: token.name(), version: token.version(), chainId, verifyingContract: <token address> }
ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)

Typehash: 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8.

FieldValue
fromthe order owner
tothe JobRegistry address
valuecap + floor(cap · feeBps / 10000) + gasFee; see payment authorization amount
validAfter0
validBeforeorder.expiresAt + 1
noncejobId
  • A wrong value surfaces as a token revert inside claim, not at post.
  • Only the payee (to == msg.sender) can execute a receive authorization, so the signature in public post calldata can be spent only by the JobRegistry.
  • validBefore is expiresAt + 1 because the token requires block.timestamp < validBefore while claim accepts block.timestamp == expiresAt.
  • nonce = jobId makes the authorization single-use and bound to one job. The nonce does not include the registry address.
  • A stored authorization cannot be replaced.
  • Read the token's name and version on chain. For Base Sepolia USDC they are USDC and 2.

Test vectors

vectors/signing-v3.json holds domains, typed data, digests, signers and signatures for every type above plus the payment authorization, computed at the local fork addresses on chain id 84532 with Anvil's public dev keys. The file also carries vectors for off-chain VORQ message types that no contract verifies. Verify an EIP-712 signature walks through one case.

On this page