# Payout Receiver ## Summary ACP currently treats the provider as both the operational actor and the payment recipient. This document discusses separating those roles so a job can have: ```text provider = actor authorized to perform job actions payoutReceiver = address that receives provider-side payments ``` If no separate receiver is configured, `payoutReceiver` should default to `provider`. The goal is not to standardize one fee model. The goal is to give ACP a minimal payout extension point so integrations can choose whether fees are handled natively by ACP or downstream by a receiver contract. ## Why This Matters ### 1. Wallet Segregation Some integrations need separate wallets for operations and fund management. The provider wallet may be an agent, operator, hot wallet, or automation account that submits work and calls `settle`. The payout receiver may be a treasury, multisig, smart account, accounting wallet, or payment router. This keeps operational authority separate from fund custody. ### 2. Custom Fee Disbursement Some integrations may want fee logic outside ACP. In this model: ```text ACP platform fee = 0 ACP evaluator fee = 0 ACP pays provider net amount to payoutReceiver payoutReceiver performs custom downstream disbursement ``` The receiver contract can then split funds across provider treasury, evaluator, platform, affiliates, or other parties according to its own rules. The selected fee and disbursement policy should be explicit to clients before they authorize settlement or completion. ### 3. Cleaner Than Hook-Based Disbursement Hook-based payment splitting can require `approve` and `transferFrom`, which adds allowance state, extra approvals, and payment logic outside the core escrow flow. A payout receiver is simpler: ACP sends funds once to the configured receiver, then the receiver splits funds it already controls. This keeps ACP flexible: it can either apply native platform and evaluator fees before paying the receiver, or set ACP-level fees to `0` and let the receiver contract own the downstream routing policy. ## Sequence ```mermaid sequenceDiagram participant C as Client participant AC as ACP participant P as Provider participant E as Evaluator participant R as PayoutReceiver / IDisburser C->>AC: createJob(provider, ..., payoutReceiver) P->>AC: setBudget(jobId, token, amount, "0x") C->>AC: fund(jobId, amount, "0x") P->>AC: submit(jobId, deliverable, "0x") E->>AC: complete(jobId, reason, data) Note over AC: compute provider net amount AC->>R: transfer token amount AC->>R: onDisbursement(jobId, complete.selector, token, amount, data) Note over R: custom accounting / fee split R->>E: disburse evaluator fee R->>P: disburse provider job reward ``` If `payoutReceiver == address(0)`, ACP pays the provider directly and does not call `onDisbursement`. ## Expected ACP Behavior Provider authorization should not change: - only the provider can submit work - only the provider can call `settle` - evaluator approval rules remain unchanged Payment disbursement changes: - `settle` pays the provider net amount to `payoutReceiver` - `complete` pays the provider net remainder to `payoutReceiver` - refunds still go to the client - platform and evaluator fee recipients are unchanged when ACP fees are enabled ## Payout Address The user specifies an optional payout receiver address: ```text payoutReceiver ``` If `payoutReceiver == address(0)`, ACP pays the provider. If `payoutReceiver != address(0)`, ACP pays the payout receiver. ## Receiver Callback If a payout receiver is specified, ACP should notify the receiver after provider-side funds are transferred. The receiver should implement: ```solidity interface IDisburser { function onDisbursement( uint256 jobId, bytes4 selector, address token, uint256 amount, bytes calldata data ) external; } ``` Parameters: - `jobId` identifies the job. This is sufficient for the receiver to query ACP for job details. - `selector` identifies the ACP function that triggered the disbursement, such as `settle` or `complete`. - `token` identifies the payment token transferred to the receiver. - `amount` is the provider-side net amount transferred to the receiver. - `data` provides arbitrary additional information for receiver-side processing. Callback rule: - if `payoutReceiver == address(0)`, ACP pays the provider and does not call `onDisbursement` - if `payoutReceiver != address(0)`, ACP pays the receiver and calls `onDisbursement` This lets a receiver contract trigger custom accounting, fee splits, routing, or downstream settlement after it receives funds. ## Why `IDisburser` A receiver contract could also be implemented as the job hook, but that couples lifecycle gating with payout processing. Keeping `IDisburser` separate gives each extension point a clearer responsibility: hooks gate or observe job actions, while the payout receiver handles funds after disbursement.