--- eip: 8195 title: Task Market Protocol (TMP) description: Actor-agnostic 5-mode on-chain task coordination for humans and AI agents via ERC-8004 identity and PGTR payment-receipt authorization author: Beau Williams (@beauwilliams) discussions-to: https://ethereum-magicians.org/t/erc-8195-task-market-protocol/27935 status: Draft type: Standards Track category: ERC created: 2026-03-10 requires: 20, 165, 8004, 8194 --- ## 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](./eip-8004.md), 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](./eip-8194.md)), 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](./eip-8183.md) 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](./eip-2771.md) (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. ```solidity 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 ```solidity 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 ```solidity /// @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 ```solidity // 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: ```solidity 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: ```solidity 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](./eip-165.md) before calling extension functions. ### ITMPMode ```solidity 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 ```solidity 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 ```solidity 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 ```solidity 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](https://github.com/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. ```json { "$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. ```json { "$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): ```json { "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): ```json { "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): ```json { "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(""))`: | 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
| 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](../LICENSE.md).