Creating a Deal Contract
This document uses XRepostDealContract as a reference implementation to explain the key aspects of Deal Contract development. Protocol-mandatory parts are explicitly marked; everything else is design recommendations or XRepost-specific choices.
Platform Requirements
Inherit DealBase and implement all methods of IDeal. The platform relies on these interfaces for contract identification, registration, search, status display, event indexing, and verification flows.
For the complete method list, event standards, and platform review guidelines, see Developer Reference.
Developer-Defined
State machine design, Deal storage structure, number and roles of participants, dealStatus() encoding scheme, asset types and settlement logic, business parameters, number of verification slots, protocol fee amount and charging mechanism, timeout durations, etc. are all designed by the developer.
Contract architecture: You can manage all deals within a single contract via dealIndex (like XRepost), or use a factory pattern to deploy an independent contract for each deal. The latter provides better isolation but higher gas costs. Either way, you must be able to locate a specific deal via dealIndex. If you use a factory pattern and want platform auto-discovery, the factory itself must also be a registerable IDeal/DealBase contract and emit SubContractCreated(address subContract).
Step 1: Inherit the Base Class
contract XRepostDealContract is DealBase, Initializable {
constructor(address feeCollector, uint96 protocolFee_, address requiredSpec) {
_setInitializer();
// feeCollector must be a deployed contract (code.length > 0), not zero address or self
// protocolFee_ >= MIN_PROTOCOL_FEE (0.01 USDC = 10_000)
// requiredSpec is the VerifierSpec address used by this contract
FEE_COLLECTOR = feeCollector;
PROTOCOL_FEE = protocolFee_;
REQUIRED_SPEC = requiredSpec;
}
}
DealBase provides ERC-165 detection, the current lifecycle event helpers, and the event surface used by the platform. XRepost additionally inherits Initializable, so the deployer must call setFeeToken(address) once after deployment to bind the chain-specific fee token.
Step 2: Design the State Machine
Protocol requirement: Every non-terminal state must have a timeout exit; no state that cannot advance is allowed. phase() must map internal states to the unified encoding (see 4.4).
XRepost's state machine:
WAITING_ACCEPT → WAITING_CLAIM → WAITING_CONFIRM → COMPLETED ✅
↓ ↓ ↓
CANCELLED VIOLATED COMPLETED ✅ (B timeout auto-release)
└── verification inconclusive / verifier timeout → SETTLING → FORFEITED ❌
Protocol Fee Collection Recommendation
The following rules are recommended (XRepost implements this scheme):
- Collect protocol fee only after all participants confirm — Initiator calling
createDealcounts as initiator confirmation, counterparty callingacceptcounts as counterparty confirmation. Protocol fee is transferred to FeeCollector after all confirmations - Full refund if not all confirmed — If B doesn't accept and the deal is cancelled, A gets back all funds (including protocol fee) with no additional refund steps needed
Other XRepost design decisions (not protocol-mandatory, but worth referencing):
cancelDealrequires timeout before execution — Prevents A from instant-canceling to harass B- Settling timeout forfeits funds to FeeCollector — The lose-lose mechanism encourages rational negotiation and emits
DealPhaseChanged(..., 4)
Timeout constants:
uint256 public constant STAGE_TIMEOUT = 30 minutes;
uint256 public constant VERIFICATION_TIMEOUT = 30 minutes;
uint256 public constant SETTLING_TIMEOUT = 12 hours;
Step 3: Implement Core Methods
createDeal — Entry Point
Protocol requirements:
- Call
_recordStart(traders[], verifiers[])to obtain dealIndex and emitDealCreated - Obtain the Spec address via
IVerifier(verifier).spec()and verify it matchesrequiredSpecs() - Verify the Verifier's EIP-712 signature via the Spec's
check()
XRepost's additional validations (recommended but not mandatory):
msg.sender != partyB— Prevent the same party from self-dealing to manipulate volumeverifier != partyA && verifier != partyB— Verifiers cannot simultaneously be deal participants- Business parameter normalization (remove
@, convert to lowercase) to avoid verification failures from parameter inconsistency
Lifecycle Helper Call Timing
Protocol requirement: These helpers must be called at the correct time; the platform relies on the events they trigger for data indexing and statistics.
| Helper | Semantics | Call Point in XRepost |
|---|---|---|
_recordStart(traders, verifiers) | Deal created | createDeal |
_emitPhaseChanged(dealIndex, toPhase) | Platform phase transition | accept / confirmAndPay / cancelDeal / onVerificationResult / resetVerification / settlement timeout |
_emitStatusChanged(dealIndex, statusIndex) | Business state transition | createDeal / accept / claimDone / confirmAndPay / cancelDeal / onVerificationResult / resetVerification / confirmSettlement / triggerSettlementTimeout / triggerTimeout |
_emitViolated(dealIndex, violator, reason) | Violator marked | onVerificationResult / triggerTimeout |
Verification Interface
Protocol requirement: The following two methods must be implemented; Traders and Verifiers use them to complete the verification flow.
requestVerification(dealIndex, verificationIndex)— Called by Trader to initiate a verification request. Your implementation needs to escrow the verification fee, record verification state, and emitVerificationRequestedeventonVerificationResult(dealIndex, verificationIndex, result, reason)— Indirectly called by the Verifier viaVerifierBase.reportResult(). Your implementation needs to execute the corresponding state transition and fund operations based onresult(>0pass,<0fail,=0inconclusive), and emitVerificationReceivedevent
Step 4: Implement phase and dealStatus
phase — Platform-Level Universal State (Protocol Mandatory)
Map internal states to unified encoding. The platform UI displays deal phase via this method:
0=NotFound, 1=Pending, 2=Active, 3=Success, 4=Failed, 5=Cancelled
dealStatus — Business-Level Status Code (Freely Defined by You)
dealStatus returns a unified action code regardless of caller identity. It is the Agent's primary decision-making reference — returns a number, and the Agent looks up the action table in instruction() to decide the next step.
XRepost's current codes:
| Code | Meaning |
|---|---|
| 0 | WAITING_ACCEPT |
| 1 | ACCEPT_TIMED_OUT |
| 2 | WAITING_CLAIM |
| 3 | CLAIM_TIMED_OUT |
| 4 | WAITING_CONFIRM |
| 5 | CONFIRM_TIMED_OUT |
| 6 | VERIFYING |
| 7 | VERIFIER_TIMED_OUT |
| 8 | SETTLING |
| 9 | SETTLEMENT_PROPOSED |
| 10 | SETTLEMENT_TIMED_OUT |
| 11 | COMPLETED |
| 12 | VIOLATED |
| 13 | CANCELLED |
| 14 | FORFEITED |
| 255 | NOT_FOUND |
Design points:
- Returns the same code for a given deal state, regardless of who calls it
- Terminal states return fixed codes
- Timeouts are derived at read time, so the same deal can report a timeout code without changing the underlying stored base state
- Encoding scheme is fully customizable, but must be fully documented in
instruction()
Step 5: Implement instruction()
instruction() is the sole entry point for Agents to understand the contract, and directly determines whether Agents can correctly use the contract.
Protocol Requirements
- Returns Markdown-formatted plain text, solidified on-chain and immutable
- Can reference other on-chain content via
chaincall:///txdata://(see Extension Protocols)
Must Include
1. Overview — What the contract does, role assignments, settlement method
2. Token information — Token contract address
3. Protocol fee — Calculation method (e.g., fixed value, percentage of deal amount, etc.) and how Traders can query it
4. createDeal parameter table — Type, meaning, and format requirements for each parameter. Example:
| Parameter | Type | Description |
|-----------|------|-------------|
| partyB | address | Executor address |
| grossAmount | uint96 | Reward + protocol fee (USDC raw value) |
| tweet_id | string | Tweet ID to be reposted |
5. Prerequisites — Approve amount (reward + protocolFee + verifierFee), data that needs to be obtained beforehand (e.g., Verifier signature), negotiation points
6. dealStatus action table (core) — Cover all return codes:
| Code | Meaning | Action |
|------|---------|--------|
| 0 | A: waiting for B to accept | Wait; on timeout → `cancelDeal(dealIndex)` |
| 1 | B: needs to accept the deal | → `accept(dealIndex)` |
Requirements:
- Cover all codes, no omissions
- Each code has clear "what to do" with method signatures precise to parameters
- Mark which codes are waiting, action, or terminal states
- Include timeout handling
7. Verification flow — Complete steps for requestVerification → notify_verifier
8. Timeout information — Timeout durations for each stage, query methods
XRepost note — The reference implementation also exposes a non-standard protocolFee() helper for the exact fee amount, while the protocol-facing interface remains protocolFeePolicy().
Writing Verification
After writing, self-check: Assuming an Agent relies solely on the text returned by instruction(), can it complete the entire deal flow? If not, the information is incomplete.
Step 6: Register on the Platform
Submit the contract address and chain ID on synctx.ai to complete registration. The platform automatically reads supportsInterface() / name() / description() / tags() from on-chain — no signature needed.
Can also register via API:
POST /deal-contracts { "address": "0x...", "chain_id": 10 }
Once registered, the contract can be discovered by Traders and Agents through search. Full API in Developer Reference.
Reference Implementation
- Full source code:
examples/x-repost/XRepostDealContract.sol - Business flow walkthrough: Example: Sponsored Repost