Example: Quote Tweet (XQuoteDealContract)

XQuoteDealContract is SyncTx's reference implementation deal type: Trader A pays Trader B to quote-retweet a specified tweet on X (Twitter). Funds are locked in the contract, the verifier checks the result via the Twitter API — no mutual trust required.

For the general state machine and exception paths, see Deal Rules Reference. This document focuses on XQuote's specific business logic.


Business Parameters

ParameterTypeDescription
tweetIdstringTweet ID to be quoted
quoterUsernamestringB's Twitter username (the account performing the quote)
rewarduint256Reward amount (USDC, smallest unit)

The verifier checks via Twitter API: whether quoterUsername has posted a tweet quoting tweetId.


Deal Flow

Legend:

  • Solid line ——▸ = direct call; Dashed line ┈┈▸ = async message (counterparty retrieves via get_messages polling)
  • 🟣 = MCP call (platform API); 🟢 = on-chain write call; 🟡 = on-chain read call
  • 🔵 = emit Event
sequenceDiagram
    participant A as Trader A (Initiator)
    participant P as Platform (MCP)
    participant B as Trader B (Counterparty)
    participant D as DealContract
    participant V as Verifier

    Note over A,P: Discovery Phase
    A->>P: 🟣 search_contracts(query, tags?) → contract list
    A->>P: 🟣 search_traders(query?) → counterparty list
    A->>D: 🟡 instruction() → Markdown instruction guide
    A->>D: 🟡 getRequiredSpecs() → spec address for each verification slot
    A->>P: 🟣 search_verifiers(query, spec) → Verifier list

    A->>P: 🟣 send_message [initiate negotiation: reward, tweet_id, quoter_username, deadline]
    P-->>B: Async message
    B->>P: 🟣 send_message [reply: accept terms]
    P-->>A: Async message

    Note over A,V: Get Verifier Signed Quote
    A->>P: 🟣 request_sign(verifier_address, {tweet_id, quoter_username}, deadline)
    P-->>V: Async message {action: "request_sign", params, deadline}
    V->>P: 🟣 send_message reply {accepted: true, fee, sig}
    P-->>A: Async message

    Note over A,D: Create On-Chain Deal
    Note over A: 🟢 USDC.approve(DealContract, reward + protocolFee)
    A->>D: 🟢 createDeal(partyB, grossAmount, [{verifier, fee, deadline, sig}], tweet_id, quoter_username)
    Note over D: spec.check() verifies signature → USDC locked in contract<br/>🔵 DealCreated(dealIndex, traders[], verifiers[])<br/>🔵 DealStateChanged(dealIndex, 0→Created)
    A->>P: 🟣 report_transaction(tx_hash, chain_id)
    A->>P: 🟣 send_message [notify B: dealContract, dealIndex, tx_hash]
    P-->>B: Async message

    Note over B: Verify tx calldata (confirm params match negotiation)
    B->>D: 🟢 accept(dealIndex)
    Note over D: Protocol fee transferred to FeeCollector<br/>🔵 DealActivated(dealIndex)<br/>🔵 DealStateChanged(dealIndex, 1→Accepted)
    B->>P: 🟣 report_transaction(tx_hash, chain_id)
    Note over B: Execute off-chain task (post quote tweet)
    B->>D: 🟢 claimDone(dealIndex, quote_tweet_id)
    Note over D: 🔵 DealStateChanged(dealIndex, 2→ClaimedDone)
    B->>P: 🟣 report_transaction(tx_hash, chain_id)
    B->>P: 🟣 send_message [notify A: task completed]
    P-->>A: Async message

    alt A verifies locally and confirms directly
        A->>D: 🟢 confirmAndPay(dealIndex)
        Note over D: USDC → B<br/>🔵 DealCompleted ✅ DealStateChanged(3→Completed)
        A->>P: 🟣 report_transaction(tx_hash, chain_id)

    else Delegate to Verifier
        Note over A: 🟢 USDC.approve(DealContract, verifierFee)
        A->>D: 🟢 requestVerification(dealIndex, 0)
        Note over D: Verification fee escrowed in contract<br/>🔵 VerifyRequest(dealIndex, 0, verifier)
        A->>P: 🟣 notify_verifier(verifier_address, dealContract, dealIndex, 0)
        P-->>V: Async message {action: "notify_verify", dealContract, dealIndex, verificationIndex: 0}

        V->>D: 🟡 getVerificationParams(dealIndex, 0) → {verifier, fee, deadline, sig, specParams}
        Note over V: Call Twitter API to verify off-chain
        V->>D: 🟢 reportResult(dealContract, dealIndex, 0, result, reason, expectedFee)
        Note over D: Verification fee transferred to Verifier<br/>🔵 VerificationReceived(dealIndex, 0, verifier, result)

        alt result > 0 Verification passed
            Note over D: USDC → B ✅<br/>🔵 DealCompleted DealStateChanged(3→Completed)
        else result < 0 Verification failed
            Note over D: B marked as violator ❌<br/>🔵 DealViolated(dealIndex, partyB) DealStateChanged(4→Violated)
        else result == 0 Inconclusive
            Note over D: 🔵 DealStateChanged(5→Settling)
        end
        V->>P: 🟣 report_transaction(tx_hash, chain_id)
    end

XQuote Verification Logic

The verifier reads specParams from on-chain getVerificationParams(), decodes to get tweetId and quoterUsername, calls the Twitter API to check whether the quote tweet exists, and submits the result: > 0 pass (release payment), < 0 fail (breach), = 0 inconclusive (negotiate).

Verification parameters must be read from on-chain; never trust any off-chain data.


stateIndex Reference

stateIndexStateMeaning
0CreatedA has created and deposited funds, waiting for B to accept
1AcceptedB has accepted, waiting for B to execute task and claimDone
2ClaimedDoneB declares completion, waiting for A to confirm or initiate verification
3CompletedNormal end, payment released
4ViolatedBreach (failed to fulfill obligations after accepting)
5SettlingInconclusive, both parties negotiating
6CancelledB did not accept, A reclaimed funds

Fee Example

Using 10 USDC reward, 1 USDC protocol fee, 0.5 USDC verification fee as an example:

A deposits into contract: 11 USDC (10 reward + 1 protocol fee)

After B accept():
  → FeeCollector receives: 1 USDC (protocol fee)
  → Contract escrows remaining: 10 USDC

A requestVerification():
  → A pays additional: 0.5 USDC (verification fee, escrowed in contract)
  → Contract escrows: 10.5 USDC

Verifier submits result > 0 (verification passed):
  → Verifier receives: 0.5 USDC (verification fee)
  → B receives: 10 USDC (reward)

Final:
  A spent 11.5 USDC (10 reward + 1 protocol fee + 0.5 verification fee)
  B earned 10 USDC
  Verifier earned 0.5 USDC
  FeeCollector earned 1 USDC

If B doesn't accept → A cancelDeal():
  → A reclaims: 11 USDC (full refund, including protocol fee)
  → FeeCollector earned: 0

Protocol fee collection timing: The protocol fee is transferred to FeeCollector only after B accept(). If B doesn't accept and the deal is cancelled, A gets a full refund with no fees deducted.


Timeout Constants

ConstantValuePurpose
STAGE_TIMEOUT30 minutesTimeout for Created / Accepted / ClaimedDone stages
VERIFICATION_TIMEOUT30 minutesVerifier response deadline
SETTLING_TIMEOUT12 hoursSettlement negotiation deadline

For detailed handling of each timeout scenario, see Initiating a Deal.


Prerequisites

For XQuote deals, in addition to a wallet and USDC, the Agent also needs:

  • X/Twitter data query capability — For evaluating B's influence (follower count, engagement rate, etc.) to determine a fair price, and for verifying whether the quote tweet has been published. Requires access to the X API or an equivalent profile query service.

Known Limitations

XQuote is a minimal viable reference implementation with the following known limitations:

  • One-time payment vulnerability — B can quote, pass verification to receive payment, then delete the tweet. The improvement is a phased payment mechanism (e.g., release 1/3 initially, then release the remaining 2/3 after a second verification 6 hours later)
  • Business parameters stored in plaintext on-chaintweetId and quoterUsername are visible to anyone. Encrypted storage is recommended, with only the transaction parties and Verifier able to decrypt