ERC-8195: Task Market Protocol

Abstract

ERC-8195 defines the Task Market Protocol (TMP), a standard interface for on-chain task
coordination supporting five procurement modes: Bounty, Claim, Pitch, Benchmark,
and Auction.

ERC-8195 is actor-agnostic: requesters and workers may be humans, autonomous AI agents, IoT
devices, or any combination thereof. A human may post a task for an AI agent to complete, an
AI agent may post a task for a human specialist, two agents may transact entirely without human
involvement, or two humans may use the protocol as a trustless escrow β€” the contract treats all
participants identically. This symmetry is grounded in ERC-8004, which provides a single
identity primitive shared by both human and machine actors.

ERC-8195 enables any frontend, aggregator, or autonomous agent to interact with any compliant task
market contract without implementation-specific knowledge. ERC-8195 is built on PGTR (ERC-8194),
which allows keyless actors to authorize task actions via on-chain payment receipts rather than
private-key signatures. ERC-8195 integrates ERC-8004 for actor-agnostic on-chain identity,
reputation, and automated benchmark validation.

Tasks are identified by bytes32 IDs generated deterministically by the contract using the
requester’s address and a monotonic nonce, enabling backends to pre-compute task IDs before
transaction inclusion. Procurement modes are identified by bytes4 selectors computed from a
canonical string, making new modes detectable by tooling without contract upgrades.

Motivation

On-chain task markets have emerged independently across multiple ecosystems with incompatible
interfaces. The absence of a standard means:

  1. Fragmentation β€” Workers must track multiple incompatible contracts; aggregators must write
    custom adapters.
  2. Actor-specific designs β€” Existing markets are designed for either humans or agents, not
    both. Human-facing markets require wallets and UIs; agent-facing markets require custom APIs.
    Neither is natively composable with the other.
  3. No keyless actor support β€” Existing designs require participants to hold private keys and
    pay gas, excluding lightweight AI agents, IoT devices, and human users who prefer payment-only
    authorization.
  4. No automated evaluation β€” Benchmark-style tasks (prove you achieved metric X) require
    trusted off-chain evaluators with no on-chain finality.
  5. Missing portable identity β€” Reputation accrued on one market is not portable to another,
    and there is no shared identity layer between human and machine participants.

ERC-8195 addresses all five gaps: a common interface, actor-agnostic design grounded in ERC-8004
identity, PGTR-based keyless authorization, ERC-8004 Validation Registry integration for
automated benchmarks, and ERC-8004 Reputation Registry for portable reputation across all
actor types.

Relationship to ERC-8183

ERC-8183 (Agentic Commerce, Feb 2026) establishes a minimal single-evaluator, single-flow job
coordination standard. ERC-8195 addresses a broader design space:

Dimension ERC-8183 ERC-8195
Actor model Agent-focused Actor-agnostic (human ↔ agent, any combination)
Keyless actors ERC-2771 (signature-based) PGTR (payment-receipt-based)
Coordination modes 1 linear flow 5 modes
Trustless evaluation Optional Benchmark + ERC-8004 Validation Registry
ERC-8004 integration Recommended Normative (all 3 registries)
Staking None Claim mode stake/forfeit
Multi-forwarder Single mapping(address => bool)

Every workflow ERC-8183 supports is expressible in ERC-8195 (Bounty mode, requester-as-evaluator).
The standards are complementary; minimal deployments may prefer ERC-8183, while multi-mode or
agent-native deployments should use ERC-8195.

Relationship to ERC-8001

ERC-8001 (if finalized) defines a coordination envelope and bounded delegation layer that could
complement ERC-8195’s lifecycle interface. ERC-8195 does not take a normative dependency on
ERC-8001: the coordination gaps ERC-8001 addresses (mode-specific negotiation artefacts, bounded
authority) are covered within ERC-8195 by Appendix A’s canonical payload schemas and by PGTR’s
receipt-scoped authorization model respectively. Implementations MAY use ERC-8001-compatible
coordination payloads; this is not required for ERC-8195 compliance.

Specification

The key words β€œMUST”, β€œMUST NOT”, β€œREQUIRED”, β€œSHALL”, β€œSHOULD”, β€œSHOULD NOT”, β€œRECOMMENDED”,
β€œMAY”, and β€œOPTIONAL” are interpreted as described in RFC 2119.


Part I: Core Interface (ITMP)

Mode Constants

ERC-8195 modes are identified by bytes4 constants computed as the first 4 bytes of the Keccak256
hash of the canonical mode name string. Using selectors rather than enum values allows new modes
to be deployed and detected by tooling without modifying existing contracts.

bytes4 constant TMP_BOUNTY    = bytes4(keccak256("TMP.mode.bounty"));
bytes4 constant TMP_CLAIM     = bytes4(keccak256("TMP.mode.claim"));
bytes4 constant TMP_PITCH     = bytes4(keccak256("TMP.mode.pitch"));
bytes4 constant TMP_BENCHMARK = bytes4(keccak256("TMP.mode.benchmark"));
bytes4 constant TMP_AUCTION   = bytes4(keccak256("TMP.mode.auction"));

See Appendix B for exact 32-bit values.

Task Status

enum TaskStatus {
    Open,            // Task is accepting work
    Claimed,         // A worker has locked the task (Claim / Auction modes)
    WorkerSelected,  // Requester has selected a worker (Pitch mode)
    PendingApproval, // Work submitted, awaiting requester acceptance (Bounty / Benchmark modes)
    Accepted,        // Work accepted; payment released to worker
    Expired,         // Task expired without acceptance; reward refunded to requester
    Cancelled        // Task cancelled by requester before work began
}

Disputed is intentionally absent from the core enum. Dispute resolution is an optional extension
defined in ITMPDispute. Core protocol implementations are not required to support disputes.

Data Structures

/// @notice Canonical task representation returned by getTask().
struct Task {
    bytes32    id;
    address    requester;
    uint256    reward;
    uint256    expiryTime;
    bytes4     mode;
    TaskStatus status;
    address    worker;
    bytes32    deliverable;
    bytes32    contentHash;
    string     contentURI;
}

/// @notice Cumulative statistics for a worker address.
struct WorkerStats {
    uint256 tasksCompleted;
    uint256 tasksAttempted;
    uint256 totalEarned;
    uint256 avgRating;
    uint256 ratingCount;
}

ITMP Interface

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "./IPGTRForwarder.sol";

/// @title ITMP β€” Task Market Protocol Core Interface
/// @notice An ERC-8195-compliant contract MUST implement all functions and events in this interface
///         and MUST return true from supportsInterface(type(ITMP).interfaceId).
interface ITMP is IERC165 {

    // ─── Events ────────────────────────────────────────────────────────────────

    /// @notice Emitted when a new task is created.
    event TaskCreated(
        bytes32 indexed taskId,
        address indexed requester,
        uint256         reward,
        bytes4  indexed mode,
        uint256         expiryTime
    );

    /// @notice Emitted when a worker submits a deliverable hash.
    event TaskSubmitted(
        bytes32 indexed taskId,
        address indexed worker,
        bytes32         deliverable
    );

    /// @notice Emitted when the requester accepts a submission and payment is released.
    event TaskCompleted(
        bytes32 indexed taskId,
        address indexed worker,
        uint256         reward
    );

    /// @notice Emitted when a task expires and reward is returned to the requester.
    event TaskExpired(bytes32 indexed taskId, address indexed requester, uint256 reward);

    /// @notice Emitted when a requester rates a completed task.
    event TaskRated(
        bytes32 indexed taskId,
        address indexed worker,
        uint8           rating,
        uint256         raterAgentId   // ERC-8004 agent ID of the requester (0 if unregistered)
    );

    // ─── Task Lifecycle ─────────────────────────────────────────────────────────

    /// @notice Create a new task and escrow the reward.
    function createTask(
        address requester,
        uint256 reward,
        uint256 duration,
        bytes4  mode,
        uint256 pitchDeadline,
        uint256 bidDeadline
    ) external returns (bytes32 taskId);

    /// @notice Record a deliverable hash submitted by a worker.
    function submitWork(bytes32 taskId, address worker, bytes32 deliverable) external;

    /// @notice Accept a worker's submission and release the escrowed reward.
    function acceptSubmission(bytes32 taskId, address requester, address worker) external;

    /// @notice Rate a completed task and record feedback in the ERC-8004 Reputation Registry.
    function rateTask(
        bytes32 taskId,
        uint8   rating,
        uint256 workerAgentId,
        uint256 raterAgentId,
        string  calldata feedbackURI,
        bytes32 feedbackHash
    ) external;

    /// @notice Refund the escrowed reward to the requester after expiry.
    function refundExpired(bytes32 taskId) external;

    // ─── View Functions ─────────────────────────────────────────────────────────

    /// @notice Returns full task information.
    function getTask(bytes32 taskId) external view returns (Task memory);
    /// @notice Returns cumulative statistics for a worker address.
    function getWorkerStats(address worker) external view returns (WorkerStats memory);
    /// @notice Returns the current requester nonce used for task ID generation.
    function requesterNonce(address requester) external view returns (uint256);
    /// @notice Returns true if addr is a trusted PGTR forwarder.
    function isTrustedForwarder(address addr) external view returns (bool);
}

Part II: Mode State Machines (Normative)

The following state diagrams define all valid state transitions for each mode.
* denotes an off-chain action relayed by a PGTR forwarder; submitWork* records the deliverable
hash on-chain but does NOT change status in Claim/Pitch/Auction modes.

Bounty Mode

Any worker may submit. First accepted submission wins.

Open
  --[submitWork]--> PendingApproval
  --[expire]------> Expired

PendingApproval
  --[acceptSubmission]--> Accepted
  --[expire]-----------> Expired (deliverable retained)

Claim Mode

Workers lock the task by staking. Stake is forfeited on failure.

Open
  --[claimTask*]---> Claimed
  --[expire]-------> Expired

Claimed
  --[submitWork*]---> Claimed  (deliverable hash recorded; status unchanged)
  --[acceptSubmission]--> Accepted
  --[forfeitClaim*]---> Open   (stake forfeited; task re-opens)
  --[expire]---------> Expired (stake returned to worker if configured)

Pitch Mode

Requester selects a worker from pitches before work begins.

Open
  --[submitPitch*]----> Open         (pitch recorded off-chain; status unchanged)
  --[selectWorker]----> WorkerSelected
  --[expire]----------> Expired

WorkerSelected
  --[submitWork*]----> WorkerSelected (deliverable hash recorded; status unchanged)
  --[acceptSubmission]--> Accepted
  --[expire]----------> Expired

Benchmark Mode

Work is validated automatically by the ERC-8004 Validation Registry.

Open
  --[submitWork]--> PendingApproval
  --[expire]------> Expired

PendingApproval
  --[acceptSubmission (via Validation Registry)]--> Accepted
  --[expire]-----> Expired (deliverable retained)

In Benchmark mode, evaluatorFor(taskId) returns the ERC-8004 Validation Registry address.
The forwarder MUST submit the proof to the Validation Registry off-chain; the Registry calls
acceptSubmission on the ERC-8195 contract if the proof is valid.

Auction Mode

Workers submit sealed bids; lowest bidder wins the right to do the work.

Open
  --[submitBid*]-------> Open        (bid recorded; status unchanged)
  --[selectLowestBidder]--> Claimed   (winning bidder locked)
  --[expire]-----------> Expired

Claimed (winner locked)
  --[submitWork*]----> Claimed       (deliverable hash recorded; status unchanged)
  --[acceptSubmission]--> Accepted
  --[expire]----------> Expired

Note: selectLowestBidder performs an O(n) scan over all bids. Implementations SHOULD
maintain a running minimum during submitBid to enable O(1) selection.


Part III: Task ID Generation

Task IDs MUST be generated by the contract as:

taskId = keccak256(abi.encode(
    block.chainid,
    address(this),
    requester,
    requesterNonce[requester]++
));

This scheme guarantees:

  • Uniqueness β€” chain ID + contract address + requester + monotonic nonce cannot collide.
  • Pre-computability β€” backends can predict the ID by reading requesterNonce[requester]
    before submitting the transaction.
  • No off-chain randomness β€” no server-side random bytes required.
  • Cross-chain disambiguation β€” the chain ID prevents the same ID appearing on two chains.

Part IV: Deliverable Anchoring

The submitWork function MUST accept a bytes32 deliverable parameter and store it on-chain
as part of the task record. The deliverable hash provides a tamper-evident anchor for:

  • IPFS CIDs β€” keccak256 of the CID bytes
  • File content β€” keccak256 of the raw file
  • ZK commitments β€” commitment value from a zero-knowledge proof
  • Merkle roots β€” root of a structured proof tree

The deliverable field MUST be zero for tasks where no submission has been recorded.
Once set, the deliverable field MUST NOT be overwritten by a subsequent submitWork call.


Part V: ERC-8004 Integration (Normative)

ERC-8004 is the actor-agnostic identity layer for ERC-8195. Both human and machine participants
(requesters and workers) are represented as ERC-8004 actors. This shared identity primitive
enables human-to-agent, agent-to-human, agent-to-agent, and human-to-human task interactions
to be treated uniformly by the protocol and by reputation indexers.

ERC-8195 contracts that support reputation MUST integrate with all three ERC-8004 registries:

Identity Registry

Requesters and workers are ERC-8004 actors β€” human or machine. The workerAgentId parameter
in rateTask MUST correspond to a valid actor ID in the ERC-8004 Identity Registry. Any
participant that registers an ERC-8004 identity (human or AI agent) participates in the same
reputation graph.

Reputation Registry

When rateTask is called, the contract MUST call:

IReputationRegistry.giveFeedback(
    workerAgentId,
    int128(rating),
    0,                     // valueDecimals
    "tmp.task.rating",     // tag1 β€” canonical ERC-8195 tag
    _modeName(task.mode),  // tag2 β€” e.g., "tmp.mode.bounty"
    feedbackURI,
    feedbackHash
);

The canonical tag "tmp.task.rating" MUST be used for tag1. Indexers and reputation aggregators
SHOULD filter on this tag to identify ERC-8195-sourced feedback. The mode name in tag2 allows
mode-specific reputation segmentation (e.g., separate scores for benchmark vs. bounty work).

The raterAgentId parameter MUST be emitted in TaskRated so indexers can attribute feedback
to a specific ERC-8004 actor. IReputationRegistry.giveFeedback records the worker’s reputation;
rater identity is anchored on-chain via the event. Implementations MUST pass 0 for raterAgentId
when the requester has not registered an ERC-8004 identity.

Validation Registry (Benchmark Mode Only)

In Benchmark mode, the forwarder MUST:

  1. Submit the worker’s proof to the ERC-8004 Validation Registry.
  2. The Validation Registry evaluates the proof against the task’s metricTarget.
  3. If valid, the Registry’s callback triggers acceptSubmission on the ERC-8195 contract.

The ERC-8195 contract MUST NOT accept acceptSubmission calls from arbitrary addresses in Benchmark
mode β€” only from the Validation Registry (returned by evaluatorFor(taskId)).


Part VI: Optional Extension Interfaces

ERC-8195 implementations MAY implement the following optional extension interfaces. Callers SHOULD
check for these via ERC-165 before calling extension functions.

ITMPMode

interface ITMPMode is IERC165 {
    /// @notice Returns the address responsible for evaluating task completion.
    ///         Bounty/Claim: returns requester
    ///         Benchmark:    returns ERC-8004 Validation Registry
    ///         Pitch/Auction: returns requester (or designated arbitrator)
    function evaluatorFor(bytes32 taskId) external view returns (address evaluator);
}

ITMPFees

interface ITMPFees is IERC165 {
    /// @notice Platform fee in basis points deducted from reward on task completion.
    function defaultFeeBps() external view returns (uint16);
    /// @notice Address that receives platform fees.
    function feeRecipient() external view returns (address);
    /// @notice Cumulative fees collected since deployment.
    function totalFeesCollected() external view returns (uint256);
    /// @notice Effective fee amount that will be deducted for a given reward.
    function feeForTask(uint256 reward) external view returns (uint256);
}

ITMPReputation

interface ITMPReputation is IERC165 {
    /// @notice The ERC-8004 Reputation Registry used for feedback submission.
    function reputationRegistry() external view returns (address);

    event ReputationRegistryUpdated(address indexed registry);
}

ITMPDispute

interface ITMPDispute is IERC165 {
    enum DisputeStatus { None, Open, Resolved }

    /// @notice Returns dispute status for a task.
    function disputeStatus(bytes32 taskId) external view returns (DisputeStatus);
    /// @notice Returns the arbitrator for a task (zero if no dispute).
    function arbitratorFor(bytes32 taskId) external view returns (address);

    /// @notice Open a dispute for a task in PendingApproval or Accepted state.
    function openDispute(bytes32 taskId, address initiator, string calldata reason) external;

    /// @notice Resolve a dispute. Only callable by arbitratorFor(taskId).
    ///         workerShare + requesterShare MUST equal 100.
    function resolveDispute(bytes32 taskId, uint8 workerShare, uint8 requesterShare) external;

    event DisputeOpened(bytes32 indexed taskId, address indexed initiator);
    event DisputeResolved(bytes32 indexed taskId, uint8 workerShare, uint8 requesterShare);
}

Normative requirement: ITMPDispute implementations MUST NOT interfere with refundExpired.
A task that has reached its expiryTime MUST be refundable even if a dispute is open. This
invariant protects requesters from griefing via spurious dispute opens.


Part VII: Fund Recovery Invariant (Normative)

refundExpired MUST:

  1. Be callable by any address (permissionless).
  2. Succeed whenever block.timestamp > task.expiryTime and task.status is Open, Claimed,
    or WorkerSelected.
  3. NOT be blocked by any hook, extension contract, or dispute state.
  4. Transfer the full escrowed reward to task.requester.
  5. Emit TaskExpired.

This invariant MUST hold unconditionally. Implementations MUST verify it in their test suite.


Part VIII: Indexer Event Requirements

All state transitions MUST emit events with sufficient data to reconstruct full task history
from a stateless event scan. Specifically:

  • TaskCreated MUST include all parameters needed to reconstruct the initial task state.
  • TaskSubmitted MUST include the deliverable hash.
  • TaskCompleted MUST include worker address and reward amount.
  • TaskExpired MUST include requester address and refunded reward amount.
  • TaskRated MUST be emitted by rateTask and MUST include worker, rating, and raterAgentId.
  • ForwarderUpdated(address indexed forwarder, bool trusted) MUST be emitted on every
    addForwarder and removeForwarder call.

Rationale

bytes4 mode selectors vs. enum

An enum encodes modes as consecutive integers. Adding a new mode requires modifying the contract
(breaking existing ABIs). bytes4 selectors are computed from canonical strings, so:

  1. New modes have globally unique identifiers derivable without reading contract storage.
  2. The selector itself carries semantic meaning (readable as a string).
  3. Aggregators can detect supported modes via supportsInterface pattern extensions without
    on-chain enumeration.

Contract-generated task IDs

Client-supplied task IDs (random bytes32) create a race condition: two clients may submit the same
ID, and the second will fail on-chain after paying gas. They also require the client to manage
entropy, which is non-trivial for lightweight agents. Contract-generated IDs using a monotonic
nonce eliminate both problems while remaining pre-computable.

submitWork as a separate step

Separating submitWork from acceptSubmission provides an on-chain audit trail:

  • The deliverable hash is anchored before the requester evaluates it.
  • ZK proofs, IPFS CIDs, and other content-addressed references are immutably recorded.
  • Disputes have an on-chain record of exactly what was submitted and when.

Off-chain submission in Claim/Pitch/Auction modes

In Claim, Pitch, and Auction modes, the worker is already locked to the task (no competitive
race). submitWork in these modes records the deliverable hash on-chain but does not change
status, because the relevant gate (acceptance) is an explicit requester action. Emitting
TaskSubmitted regardless of mode provides consistent indexer behavior.

PGTR over ERC-2771

See ERC-8194 Β§Relationship to ERC-2771.

Backwards Compatibility

This ERC introduces a new interface. Existing task market contracts are unaffected unless they
choose to implement ITMP. Contracts may add supportsInterface(type(ITMP).interfaceId) as an
additive upgrade, provided the underlying function signatures match the ITMP interface.

Reference Implementation

See daydreamsai/taskmarket-contracts for the reference implementation (src/TaskMarket.sol) and compliance test suite (test/ITMP.t.sol). The test suite covers all 5 mode state machines, ERC-165 interface detection, deliverable anchoring, the fund recovery invariant, and multi-forwarder add/remove.

Appendix A: Canonical Task Metadata JSON Schema

ERC-8195 tasks MAY reference extended metadata at contentURI. The following JSON schema defines the
canonical format. Indexers and frontends SHOULD use this schema for interoperability.

{
  "$schema": "https://json-schema.org/draft/2020-12",
  "title": "ERC-8195 Task Metadata",
  "type": "object",
  "required": ["title", "description", "mode", "reward"],
  "properties": {
    "title": {
      "type": "string",
      "description": "Short human-readable task title"
    },
    "description": {
      "type": "string",
      "description": "Full task description"
    },
    "mode": {
      "type": "string",
      "enum": ["bounty", "claim", "pitch", "benchmark", "auction"],
      "description": "Procurement mode"
    },
    "reward": {
      "type": "string",
      "description": "Reward amount in USDC base units (string to avoid precision loss)"
    },
    "tags": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Searchable tags"
    },
    "benchmark": {
      "type": "object",
      "description": "Present only for benchmark mode tasks",
      "properties": {
        "metricDescription": { "type": "string" },
        "metricTarget": { "type": "string" },
        "validatorAddress": {
          "type": "string",
          "description": "ERC-8004 Validation Registry address"
        },
        "proofType": {
          "type": "string",
          "description": "Expected proof format (e.g., 'zk-snark', 'benchmark-result-json')"
        }
      },
      "required": ["metricDescription", "metricTarget", "validatorAddress", "proofType"]
    },
    "auction": {
      "type": "object",
      "description": "Present only for auction mode tasks",
      "properties": {
        "auctionType": {
          "type": "string",
          "enum": ["dutch", "english", "reverse_dutch", "reverse_english"]
        },
        "maxPrice": { "type": "string" },
        "floorPrice": { "type": "string" },
        "startPrice": { "type": "string" },
        "bidDeadline": {
          "type": "integer",
          "description": "Unix timestamp when bidding closes"
        }
      }
    },
    "createdAt": {
      "type": "integer",
      "description": "Unix timestamp of task creation"
    },
    "chainId": {
      "type": "integer",
      "description": "EVM chain ID where the task contract is deployed"
    },
    "contractAddress": {
      "type": "string",
      "description": "ERC-8195 contract address (checksummed)"
    }
  }
}

Appendix A.2: Benchmark Proof Envelope

Implementations MAY use the following envelope format when submitting a benchmark proof to an
external Validation Registry. No normative requirement to submit to a Validation Registry is
imposed by this ERC; the schema is provided for interoperability among implementations that
choose to integrate one.

{
  "$schema": "https://json-schema.org/draft/2020-12",
  "title": "ERC-8195 Benchmark Proof Envelope",
  "type": "object",
  "required": ["taskId", "contractAddress", "chainId", "worker", "deliverable", "metricTarget", "proofType", "proof", "submittedAt"],
  "properties": {
    "taskId":          { "type": "string"  },
    "contractAddress": { "type": "string"  },
    "chainId":         { "type": "integer" },
    "worker":          { "type": "string"  },
    "deliverable":     { "type": "string"  },
    "metricTarget":    { "type": "string"  },
    "proofType":       { "type": "string", "examples": ["zk-snark", "benchmark-result-json", "merkle-proof"] },
    "proof":           { "type": "object"  },
    "submittedAt":     { "type": "integer" }
  }
}

Appendix A.3: Mode Coordination Payload Schemas

The following schemas define canonical off-chain payloads for mode-specific coordination actions.
Forwarder backends SHOULD use these schemas for interoperability. These payloads are transmitted
off-chain; only the resulting on-chain state (deliverable hash, bid record, claim record) is
normative.

Pitch Payload (submitted when submitPitch* is relayed):

{
  "title": "ERC-8195 Pitch Payload",
  "type": "object",
  "required": ["taskId", "worker", "pitchText", "submittedAt"],
  "properties": {
    "taskId":         { "type": "string"  },
    "worker":         { "type": "string"  },
    "pitchText":      { "type": "string"  },
    "attachmentURI":  { "type": "string"  },
    "attachmentHash": { "type": "string"  },
    "submittedAt":    { "type": "integer" }
  }
}

Bid Payload (submitted when submitBid* is relayed):

{
  "title": "ERC-8195 Bid Payload",
  "type": "object",
  "required": ["taskId", "worker", "bidAmount", "submittedAt"],
  "properties": {
    "taskId":         { "type": "string"  },
    "worker":         { "type": "string"  },
    "bidAmount":      { "type": "string"  },
    "commitmentHash": { "type": "string"  },
    "submittedAt":    { "type": "integer" }
  }
}

Claim Payload (submitted when claimTask* is relayed):

{
  "title": "ERC-8195 Claim Payload",
  "type": "object",
  "required": ["taskId", "worker", "stakeAmount", "submittedAt"],
  "properties": {
    "taskId":      { "type": "string"  },
    "worker":      { "type": "string"  },
    "stakeAmount": { "type": "string"  },
    "submittedAt": { "type": "integer" }
  }
}

Appendix B: Mode Selector Values

The canonical bytes4 mode selectors, computed as bytes4(keccak256("<string>")):

Mode Input String bytes4 Value
Bounty "TMP.mode.bounty" bytes4(keccak256("TMP.mode.bounty"))
Claim "TMP.mode.claim" bytes4(keccak256("TMP.mode.claim"))
Pitch "TMP.mode.pitch" bytes4(keccak256("TMP.mode.pitch"))
Benchmark "TMP.mode.benchmark" bytes4(keccak256("TMP.mode.benchmark"))
Auction "TMP.mode.auction" bytes4(keccak256("TMP.mode.auction"))

Appendix C: ERC-165 Interface IDs

Interface Solidity expression
IERC165 0x01ffc9a7 (standard)
ITMP type(ITMP).interfaceId
IPGTRForwarder type(IPGTRForwarder).interfaceId
ITMPMode type(ITMPMode).interfaceId
ITMPFees type(ITMPFees).interfaceId
ITMPReputation type(ITMPReputation).interfaceId
ITMPDispute type(ITMPDispute).interfaceId

All non-standard IDs are computed at compile time by Solidity as the XOR of function selectors.
To verify: cast interface <address> | grep interfaceId after deploying the reference implementation.

Security Considerations

  1. Payment atomicity β€” All payment operations (escrow on create, release on accept, refund on
    expire) MUST be atomic. Partial state updates with external token calls MUST use
    ReentrancyGuard or equivalent.

  2. Forwarder trust β€” The trustedForwarders mapping is a privileged set. Adding a malicious
    forwarder allows arbitrary task creation and work acceptance on behalf of any requester.
    addForwarder MUST be protected by onlyOwner or equivalent governance.

  3. Staking collateral β€” In Claim mode, stake amounts SHOULD be set high enough to make
    griefing (claim-and-abandon) economically unattractive. stakeBps is a percentage of reward;
    implementations SHOULD enforce a minimum absolute stake.

  4. Benchmark oracle trust β€” The Validation Registry in Benchmark mode is trusted to evaluate
    proofs fairly. Requesters SHOULD use audited, reputable registry implementations. The ERC-8195
    contract SHOULD emit the registry address in TaskCreated so requesters can verify it
    before posting a task.

  5. Auction gas β€” selectLowestBidder is O(1) when implementations maintain a running minimum
    during bid submission. Implementations that do not MUST document the gas risk in the frontend.

  6. Expiry clock β€” expiryTime is set to block.timestamp + duration at task creation.
    Block timestamps are manipulable by validators within ~15 seconds. Tasks with very short
    durations (< 60 seconds) SHOULD NOT be used in adversarial contexts.

  7. Re-rating β€” Implementations MUST prevent multiple rateTask calls for the same
    (taskId, requester) pair to avoid reputation inflation.

Copyright

Copyright and related rights waived via CC0.

2 Likes

TMP is much more complete than most task-market proposals because it standardises not just a settlement primitive, but a broader procurement surface with explicit mode-dependent state machines.

That said, I think the spec would benefit from explicitly recognising ERC-8001 as the missing coordination layer between ERC-8004 identity and TMP settlement.

Right now, TMP clearly standardises:

  • escrowed task creation

  • deterministic task IDs

  • deliverable anchoring

  • mode-specific lifecycle transitions

  • expiry/refund invariants

  • optional dispute / fee / reputation extensions

ERC-8004 then supplies actor identity, reputation, and validation registries.

But there is still an architectural gap: how do actors express bounded, machine-readable coordination terms around these transitions?

That gap shows up in several places:

1. Mode semantics are standardised, but coordination payloads are not

TMP standardises the lifecycle surface, but not the structured coordination artefacts that precede or authorise those lifecycle actions.

Examples:

  • In Pitch mode, there is no canonical machine-readable pitch commitment format.

  • In Auction mode, there is no canonical bid commitment / reveal structure at the interface level.

  • In Claim mode, there is no standard way to express acceptance of specific claim conditions, stake conditions, or delegated claim authority.

  • In Benchmark mode, there is no standard coordination object binding taskId, worker, deliverable, proofType, validator, and evaluation target into a single verifiable envelope.

So while the contract ABI is interoperable, the higher-level coordination layer can still fragment off-chain.

2. ERC-8004 identifies actors, but does not fully express coordination intent

ERC-8004 is doing important work here, but it solves a different problem.

It answers: who is the actor?
It does not fully answer: what exact action was authorised, under what constraints, for which task, before which deadline, and via which extension semantics?

That is where ERC-8001 fits naturally.

A clean stack here would be:

  • ERC-8004: actor identity / reputation / validation registries

  • ERC-8001: verifiable coordination envelope + bounded delegation + extension semantics

  • TMP: task lifecycle / procurement mode transitions / escrow settlement

3. Benchmark mode especially needs stronger coordination binding

Benchmark mode is the clearest place where ERC-8001 integration would materially improve the design.

Today the flow is roughly:

  • worker submits work

  • forwarder submits proof to Validation Registry

  • Validation Registry calls acceptSubmission

But the spec should ideally standardise the binding between:

  • taskId

  • requester

  • worker

  • deliverable

  • mode

  • metricTarget

  • proofType

  • validator

  • proof expiry / freshness

Without that, β€œautomated validation” risks becoming a privileged callback pattern rather than a portable coordination primitive.

An ERC-8001 extension payload could make that binding explicit and machine-verifiable across implementations.

4. Trusted forwarders are still a broad trust surface

The spec correctly calls out forwarder trust, but the current model still gives a trusted forwarder a very large authority surface.

ERC-8001 could reduce that by expressing bounded delegation explicitly, for example:

  • this actor may create task X with mode Y and reward cap Z

  • this actor may submit work only for task X

  • this validator may complete only Benchmark tasks matching hash H

  • this relayer may act only before timestamp T

That is a better security posture than treating all forwarded actions as equally trusted once the forwarder is whitelisted.

5. Portable reputation would be stronger with portable coordination provenance

The rateTask integration is useful, but the reputation signal would be stronger if coupled to a canonical coordination artefact.

In other words, not just:

  • task accepted

  • requester rated worker

but:

  • task accepted under a specific coordination envelope

  • deliverable hash D was the accepted artefact

  • completion occurred under mode M

  • validator / evaluator E was involved

  • dispute status was N

That makes cross-market reputation much more meaningful and harder to game.

Suggestion

I would recommend adding a short normative or semi-normative section along the lines of:

TMP MAY use ERC-8001-compatible coordination payloads and extensions to standardise mode-specific negotiation, delegated authority, benchmark proof coordination, and other off-chain coordination artefacts associated with TMP lifecycle transitions.

Or more concretely:

  • ERC-8004 answers actor identity

  • ERC-8001 answers coordination semantics and bounded authority

  • TMP answers market lifecycle and settlement

That would make TMP much stronger as a composable protocol stack rather than a standalone market ABI.

TMP is already strong as a task market / procurement / settlement standard.

But for fully interoperable human-agent, agent-agent, and delegated execution workflows, it should not try to carry all coordination semantics itself. Explicit ERC-8001 integration would give it:

  • standard extension semantics

  • bounded delegated execution

  • portable off-chain coordination artefacts

  • stronger benchmark proof binding

  • better cross-market composability

I think that would materially improve the proposal.

Thanks so much for taking the time to look at the proposal and send me some comments. I’ve just had a look and let me brief you on some changes that I’ve made and then also some things that I will leave at this point in time.

On ERC-8001 as a coordination layer:

At this time we’re not taking a normative dependency on ERC-8001. The coordination artefact gaps you identify are real, and we’re addressing them within ERC-8195 by adding canonical per-mode payload schemas (Pitch, Bid, Claim, and BenchmarkProofEnvelope) to Appendix A. This keeps the standard self-contained and avoids a hard dependency on a proposal that is not yet stable. We’ve added a brief non-normative note acknowledging the ERC-8001 design space in the updated spec.

On forwarder trust surface:

This is already handled by PGTR (ERC-8194). The receipt hash binds payer + amount + nonce + expiry + target + selector. Fine-grained delegation (β€œthis actor may act only on task X”) is achievable today by scoping receipts to specific calldata in the forwarder without on-chain state per task. We’d encourage a closer read of ERC-8194 Β§Replay Protection.

On benchmark proof binding: Valid point.

We’ve added a canonical BenchmarkProofEnvelope JSON schema (binding taskId, worker, deliverable, proofType, metricTarget, submittedAt) to Appendix A for interoperability among implementations that integrate a Validation Registry. This is non-normative since the Validation Registry is an ERC-8004 concern, not an ERC-8195 one.

On reputation provenance: Valid.

We’ve added a raterAgentId parameter to rateTask so the rating is attributed to a specific ERC-8004 actor (not just an Ethereum address), and emit it in TaskRated so indexers can correlate feedback to a registered actor. The deliverable hash is already on-chain via submitWork; reputation records can be correlated to it via taskId.

Thank you for the follow-up. I think these revisions materially improve ERC-8195.

The Appendix A additions are especially constructive. Canonical per-mode payload schemas reduce coordination artefact ambiguity, the BenchmarkProofEnvelope gives implementations a concrete interoperability surface for validation-oriented flows, and raterAgentId improves provenance by making reputation attribution actor-aware rather than purely address-bound.

One narrow clarification on ERC-8001: ERC-8001 is already a Final ERC, so this is not really a question of EIP-process maturity or whether the standard is formally β€œnot yet stable.” ERC-8001 is a Standards Track ERC in Final status, and it is intentionally scoped as a minimal multi-party coordination primitive with typed data, lifecycle rules, and verification semantics, while leaving modules such as reputation and other extensions outside the core.

That said, I think the broader direction here is healthy, and I do not view ERC-8195 and ERC-8001 as competing efforts.

They sit at different layers. ERC-8195 specifies the task-market primitive itself: procurement modes, task lifecycle, and settlement-facing semantics. ERC-8001 is a more general coordination layer for multi-party intent formation and acceptance across systems. In that sense, choosing not to take a normative dependency today is best understood as a self-containment and scope-boundary decision, not a statement that ERC-8001 is somehow unfinished. ERC-8001’s own rationale is to explicitly provide the smallest reusable primitive for multi-party coordination and to leave adjacent concerns to modules.

So from my perspective, the current revision strengthens ERC-8195 as a standalone market standard while still leaving room for future composition with ERC-8001 where richer cross-system coordination, delegation, or portable workflow semantics become useful.