Creating a Verifier Spec
A Verifier Spec defines the complete specification for a type of verification — "what this type of transaction needs to verify, how to construct signatures, how to verify them." Immutable after deployment; multiple Verifier instances share the same Spec to form a competitive market.
This document uses the XRepostVerifierSpec contract class as a reference implementation; its on-chain name() is X(Twitter) Repost Verifier Spec.
Relationship Between Spec and Verifier Instance
X(Twitter) Repost Verifier Spec (1)
├── XRepostVerifier A (Instance 1, operated by Team A)
├── XRepostVerifier B (Instance 2, operated by Team B)
└── XRepostVerifier C (Instance 3, auto-operated by an Agent)
- Spec defines the specification — Parameter format, signature structure,
check()verification logic - Instances provide the service — Each independently operates off-chain services, sets own prices, competes for business
- Deal Contract references Spec —
requiredSpecs()returns the Spec address required for each verification slot in the deal flow; Traders use this to search for matching Verifier instances
Step 1: Implement Metadata
contract XRepostVerifierSpec is VerifierSpec {
function name() external pure override returns (string memory) {
return "X(Twitter) Repost Verifier Spec";
}
function version() external pure override returns (string memory) {
return "1.0";
}
function description() external pure override returns (string memory) {
return
"Verifies whether reposter_address's bound X(Twitter) account has reposted a given tweet via native repost(retweet) OR quote tweet. "
"EIP-712 signature check. Result type: 1=pass, -1=fail, 0=inconclusive. "
"request_sign params: {tweet_id, reposter_address}. "
"specParams: abi.encode(tweet_id, reposter_address).";
}
}
Importance of description(): It's not just human-readable documentation — off-chain services need to decode on-chain parameters based on the specParams format declared in the description. It must be precise down to parameter types and order.
Note: The
VerifierSpecbase contract only defines three methods:name(),version(),description().check()is not in the base contract because each Spec's signature parameters differ — you need to define thecheck()signature yourself. Deal Contracts cast the Spec address to your specific type when calling it.
Step 2: Define the EIP-712 Signature Structure
bytes32 public constant VERIFY_TYPEHASH = keccak256(
"Verify(string tweetId,address reposterAddress,uint256 fee,uint256 deadline)"
);
This typehash defines the content structure for the Verifier's pricing signature. The Verifier signs according to this structure during request_sign, and the contract verifies the signature according to this structure during createDeal.
Design Considerations
The signature includes: business parameters (solidifying verification content), fee (solidifying the price), deadline (solidifying the validity period). It does not include dealIndex (the deal hasn't been created at signing time) or partyA/partyB (the Verifier only cares about verification content).
EIP-712 is used instead of plain signatures because structured signatures allow wallets to display semantic signing content rather than a hex string.
Step 3: Implement check()
function check(
address verifierInstance,
string calldata tweet_id,
address reposter_address,
uint256 fee,
uint256 deadline,
bytes calldata sig
) external view returns (address) {
bytes32 structHash = keccak256(abi.encode(
VERIFY_TYPEHASH,
keccak256(bytes(tweet_id)),
reposter_address,
fee,
deadline
));
return _recoverEIP712Signer(verifierInstance, structHash, deadline, sig);
}
Key Design Points
- Call timing —
check()is called by the Deal Contract duringcreateDealto verify the signature was indeed issued by the specified Verifier's signer - DOMAIN_SEPARATOR — Read from
verifierInstancevia_recoverEIP712Signer; each instance has an independent domain to prevent cross-instance signature replay - Return value — Returns the recovered signer address; the Deal Contract compares this against
IVerifier(verifierInstance).signer()to verify authenticity - EIP-2 low-s value —
_recoverEIP712Signerenforces low-s value to prevent signature malleability attacks
Step 4: Define the specParams Format
specParams is the parameter package passed to the Verifier's off-chain service during the verification phase. For XRepost, specParams is identical to check()'s signature parameters:
check() signature parameters (at createDeal stage):
tweet_id, reposter_address
specParams (at verification stage):
abi.encode(tweet_id, reposter_address)
specParams matches the check() signature parameters (both are tweet_id + reposter_address) because XRepost's claimDone does not introduce new fields (unlike XQuote's quote_tweet_id). The verifier uses these to check whether the Twitter account bound to reposter_address has reposted the specified tweet.
Constructing specParams in the Deal Contract
// XRepostDealContract.verificationParams()
specParams = abi.encode(d.tweet_id, d.poster);
Key point: specParams is constructed and returned by the Deal Contract's verificationParams(). The Verifier's off-chain service reads and decodes it from on-chain. The entire chain passes through no off-chain intermediary.
Step 5 (Optional): Create a Sample Verifier Contract Instance and Corresponding Off-Chain Verification Service
The Spec author doesn't necessarily need to operate a Verifier themselves; anyone can deploy an instance based on an existing Spec (see Deployment & Operations). The instance contract is lightweight:
contract XRepostVerifier is VerifierBase {
address public immutable SPEC;
constructor(address specAddress) VerifierBase("XRepostVerifier", "1") {
require(specAddress != address(0), "spec cannot be zero");
SPEC = specAddress;
}
function description() external pure override(VerifierBase) returns (string memory) {
return "Verify reposts on X(Twitter). Matches native repost(retweet) OR quote tweet by the reposter's bound X(Twitter) account. EIP-712 signed. Verifier signatures expire within 1 hour (3600s) of issuance.";
}
function spec() external view override(VerifierBase) returns (address) {
return SPEC;
}
}
Full VerifierBase API in Developer Reference.
The focus for Verifiers is the off-chain verification service implementation; see Deployment & Operations.
Reference Implementation
- Spec contract:
contracts/verifier-specs/XRepostVerifierSpec.sol - Verifier instance:
examples/x-repost-verifier/XRepostVerifier.sol