V2 Contracts
TaskPoolFactory
Creates and manages per-tenant TaskPool + RewardPool pairs via the Beacon proxy pattern.Functions
// Deploy a new pair of pools for a tenant
function createTenantPools(bytes32 tenantId, bytes calldata config)
external onlyOwner returns (address taskPool, address rewardPool)
// Upgrade the TaskPool beacon implementation (affects all tenants)
function upgradeTaskPool(address newImplementation) external onlyOwner
// Upgrade the RewardPool beacon implementation (affects all tenants)
function upgradeRewardPool(address newImplementation) external onlyOwner
// Deactivate a tenant's pools
function deactivateTenant(bytes32 tenantId) external onlyOwner
// Deterministically compute a tenant's TaskPool address (CREATE2)
function computeTaskPoolAddress(bytes32 tenantId) external view returns (address)
// Deterministically compute a tenant's RewardPool address (CREATE2)
function computeRewardPoolAddress(bytes32 tenantId) external view returns (address)
// Get a tenant's pool addresses and status
function getTenantPools(bytes32 tenantId) external view returns (TenantPools memory)
// Total number of tenants with deployed pools
function getTotalTenants() external view returns (uint256)
// Get tenant ID at a specific index
function getTenantIdAt(uint256 index) external view returns (bytes32)
TenantPools Struct
struct TenantPools {
address taskPool; // Beacon proxy address for TaskPool
address rewardPool; // Beacon proxy address for RewardPool
uint256 createdAt; // Block timestamp of pool creation
bool active; // Whether the tenant is active
}
Events
event TenantPoolsCreated(bytes32 indexed tenantId, address taskPool, address rewardPool);
event BeaconUpgraded(string poolType, address newImplementation);
event TenantDeactivated(bytes32 indexed tenantId);
Custom Errors
error TenantAlreadyExists(); // Tenant ID already has deployed pools
error TenantNotFound(); // No pools found for the given tenant ID
TaskPoolImplementation
Per-tenant task escrow and lifecycle management.Functions
// Initialize the pool (called once by factory via proxy)
function initialize(
bytes32 _tenantId,
address _usdc,
address _verificationEngine,
address _solverRegistry,
address _intentRegistry,
bytes calldata _config // Reserved for future use
) external initializer
// Deposit USDC into the merchant's pool balance
function depositFunds(uint256 amount) external
// Withdraw USDC from the merchant's pool balance
function withdrawFunds(uint256 amount) external
// Create a new task with escrowed USDC
function createTask(
address merchant, // Funding merchant address
uint256 amount, // USDC amount (6 decimals)
uint256 durationSeconds, // Task deadline in seconds
bytes32 campaignId, // Associated campaign (bytes32(0) if none)
VerificationMethod verificationMethod, // Consensus, Oracle, or AIEval
bytes calldata acceptanceCriteria // Human/machine-readable completion criteria
) external returns (bytes32 taskId)
// Solver claims an open task
function claimTask(bytes32 taskId) external
// Solver submits proof of completion
function submitTask(bytes32 taskId, bytes32 submissionHash) external
// Settle a verified task (called by VerificationEngine only)
function settleTask(bytes32 taskId, address solver) external
// Cancel an open task, refund merchant
function cancelTask(bytes32 taskId) external
// Set the associated RewardPool
function setRewardPool(address _rewardPool) external
// Pause/unpause the pool
function setPaused(bool _paused) external
// View functions
function getTask(bytes32 taskId) external view returns (Task memory)
function getMerchantBalance(address merchant) external view returns (uint256)
function getTenantId() external view returns (bytes32)
function getTotalTasksCreated() external view returns (uint256)
function getTotalValueEscrowed() external view returns (uint256)
function isPaused() external view returns (bool)
function getRewardPool() external view returns (address)
function getVerificationEngine() external view returns (address)
function getSolverRegistry() external view returns (address)
function getUsdc() external view returns (address)
Task Struct
struct Task {
bytes32 taskId; // Deterministic hash
address merchant; // Funding merchant
address token; // Payment token (USDC)
uint256 amount; // Escrowed amount
uint256 createdAt; // Block timestamp
uint256 deadline; // Expiry timestamp
bytes32 campaignId; // Associated campaign
TaskStatus status; // Current lifecycle status
VerificationMethod verificationMethod; // How completion is verified
address claimedBy; // Solver who claimed (address(0) if unclaimed)
bytes32 submissionHash; // Hash of solver's submission
bytes acceptanceCriteria; // Completion requirements
}
Events
event TaskCreated(
bytes32 indexed taskId,
address indexed merchant,
uint256 amount,
uint256 deadline,
bytes32 campaignId,
VerificationMethod verificationMethod
);
event TaskClaimed(bytes32 indexed taskId, address indexed solver);
event TaskSubmitted(bytes32 indexed taskId, address indexed solver, bytes32 submissionHash);
event TaskSettled(bytes32 indexed taskId, address indexed solver, uint256 amount);
event TaskCancelled(bytes32 indexed taskId);
event FundsDeposited(address indexed merchant, uint256 amount);
event FundsWithdrawn(address indexed merchant, uint256 amount);
Custom Errors
error PoolPaused(); // Pool is paused
error AmountMustBePositive(); // Zero or negative amount
error InvalidDuration(); // Duration outside allowed range
error InsufficientMerchantBalance(); // Merchant doesn't have enough deposited
error TaskNotOpen(); // Task is not in Open status
error TaskExpired(); // Task deadline has passed
error SolverNotEligible(); // Solver not registered or banned
error NotTaskSolver(); // Caller is not the task's solver
error TaskNotClaimed(); // Task hasn't been claimed yet
error TaskNotPendingSettlement(); // Task not awaiting settlement
error SolverMismatch(); // Solver address doesn't match claim
error OnlyVerificationEngine(); // Only VerificationEngine can call
error CannotCancel(); // Task can't be cancelled in current state
error OnlyMerchantOrOwner(); // Only merchant or owner can call
RewardPoolImplementation
Manages solver payout calculation, fee splitting, and reward claims.Functions
// Initialize (called once by factory)
function initialize(
bytes32 _tenantId,
address _usdc,
address _solverRegistry,
address _intentRegistry
) external initializer
// Queue a payout after task settlement (called by TaskPool)
function queuePayout(bytes32 taskId, address solver, uint256 grossAmount) external
// Solver claims all pending rewards
function claimRewards() external
// Admin: set associated TaskPool
function setTaskPool(address _taskPool) external
// Admin: set protocol fee recipient
function setProtocolFeeRecipient(address recipient) external
// Admin: update fee basis points
function setFees(uint256 protocolFeeBps, uint256 solverFeeBps) external
// View functions
function getPendingPayout(address account) external view returns (uint256)
function getPayoutRecord(bytes32 taskId) external view returns (PayoutRecord memory)
function getTenantId() external view returns (bytes32)
function getTotalRewardsDistributed() external view returns (uint256)
function getProtocolFeeBps() external view returns (uint256)
function getSolverFeeBps() external view returns (uint256)
function getTaskPool() external view returns (address)
function getProtocolFeeRecipient() external view returns (address)
PayoutRecord Struct
struct PayoutRecord {
bytes32 taskId; // Associated task
address solver; // Recipient solver
uint256 grossAmount; // Total task amount
uint256 solverFee; // Fee retained by solver (from gross)
uint256 protocolFee; // Fee sent to protocol
uint256 netPayout; // Amount available to solver (gross - protocolFee)
uint256 settledAt; // Block timestamp of settlement
bool claimed; // Whether solver has claimed
}
Events
event RewardQueued(bytes32 indexed taskId, address indexed solver, uint256 netPayout);
event RewardClaimed(address indexed solver, uint256 amount);
event FeesUpdated(uint256 protocolFeeBps, uint256 solverFeeBps);
Custom Errors
error OnlyTaskPool(); // Only the linked TaskPool can call
error AlreadyQueued(); // Payout already queued for this task
error NothingToClaim(); // No pending rewards for caller
error FeesTooHigh(); // Combined fees exceed maximum
SolverRegistry
Global registry of solver profiles, reputation tracking, and eligibility checks.Functions
// Initialize (called once via proxy)
function initialize(address _owner) external initializer
// Register as a solver
function register(bytes calldata metadata) external
// Check if a solver is eligible for a task
function isEligible(address solver, bytes32 taskId) external view returns (bool)
// Accrue rewards to solver's profile (called by authorized RewardPools)
function accrueReward(address solver, uint256 amount) external
// Record a task claim against solver's profile
function recordClaim(address solver) external
// Admin: ban a solver permanently
function banSolver(address solver) external
// Admin: deactivate a solver (reversible)
function deactivateSolver(address solver) external
// Admin: authorize a RewardPool to update solver stats
function authorizeRewardPool(address rewardPool) external
// Admin: revoke RewardPool authorization
function revokeRewardPool(address rewardPool) external
// View functions
function getProfile(address solver) external view returns (SolverProfile memory)
function isBanned(address solver) external view returns (bool)
function getTotalSolvers() external view returns (uint256)
function isAuthorizedRewardPool(address pool) external view returns (bool)
SolverProfile Struct
struct SolverProfile {
address solver; // Solver's address
uint256 registeredAt; // Registration timestamp
uint256 totalTasksClaimed; // Lifetime tasks claimed
uint256 totalTasksCompleted; // Lifetime tasks completed
uint256 totalRewardsEarned; // Lifetime USDC earned (6 decimals)
uint256 reputationScore; // Computed reputation (0-1000)
bool active; // Whether solver is active
bytes metadata; // Encoded metadata (name, type, capabilities)
}
Events
event SolverRegistered(address indexed solver);
event SolverDeactivated(address indexed solver);
event SolverBanned(address indexed solver);
event ReputationUpdated(address indexed solver, uint256 newScore);
event RewardPoolAuthorized(address indexed rewardPool);
event RewardPoolRevoked(address indexed rewardPool);
Custom Errors
error AlreadyRegistered(); // Solver address already registered
error AddressBanned(); // Address is permanently banned
error SolverNotFound(); // No profile for this address
error NotAuthorizedRewardPool(); // Caller is not an authorized RewardPool
VerificationEngine
Routes verification requests to the appropriate handler (consensus, oracle, or AI) and records resolutions.Functions
// Initialize (called once via proxy)
function initialize(address _owner, address _aiOracleAddress) external initializer
// Request verification for a submitted task (called by TaskPools)
function requestVerification(
bytes32 taskId,
address solver,
bytes32 submissionHash,
bytes calldata acceptanceCriteria,
uint8 verificationMethodInt // Cast to VerificationMethod enum
) external
// Resolve a pending verification (called by authorized oracles)
function resolveVerification(bytes32 taskId, bool approved) external
// Admin: add an authorized oracle
function addOracle(address oracle) external
// Admin: remove an oracle
function removeOracle(address oracle) external
// Admin: set the AI evaluation oracle
function setAiOracle(address oracle) external
// Admin: authorize a TaskPool to submit verification requests
function authorizeTaskPool(address taskPool) external
// Admin: revoke TaskPool authorization
function revokeTaskPool(address taskPool) external
// View functions
function getRequest(bytes32 taskId) external view returns (VerificationRequest memory)
function isAuthorizedOracle(address oracle) external view returns (bool)
function isAuthorizedTaskPool(address taskPool) external view returns (bool)
function getAiOracle() external view returns (address)
VerificationRequest Struct
struct VerificationRequest {
bytes32 taskId; // Task being verified
address taskPool; // Originating TaskPool
address solver; // Solver who submitted
bytes32 submissionHash; // Hash of submission content
bytes acceptanceCriteria; // What the submission is evaluated against
VerificationMethod method; // Consensus, Oracle, or AIEval
VerificationStatus status; // Pending, Approved, or Rejected
uint256 requestedAt; // Block timestamp of request
uint256 resolvedAt; // Block timestamp of resolution (0 if pending)
}
Events
event VerificationRequested(
bytes32 indexed taskId,
VerificationMethod method,
address indexed taskPool
);
event VerificationResolved(
bytes32 indexed taskId,
VerificationStatus status,
address indexed resolver
);
Custom Errors
error AlreadyRequested(); // Verification already pending for this task
error NotAuthorizedOracle(); // Caller is not an authorized oracle
error NotPending(); // Verification is not in Pending status
error NotAuthorizedTaskPool(); // Caller is not an authorized TaskPool
IntentRegistryV2
Upgradeable intent ledger with access control. Used by TaskPools to record financial intents.Functions
// Initialize (called once via proxy)
function initialize(address _owner) external initializer
// Register a new intent (called by authorized callers — TaskPools via Factory)
function registerIntent(
bytes32 intentId,
address merchant,
address recipient,
uint256 amount
) external
// Mark an intent as fulfilled
function markFulfilled(bytes32 intentId, address solver) external
// Admin: authorize a caller (e.g., TaskPoolFactory)
function addAuthorizedCaller(address caller) external
// Admin: revoke caller authorization
function removeAuthorizedCaller(address caller) external
// View functions
function getIntent(bytes32 intentId) external view returns (IntentRecord memory)
function getMerchantIntents(address merchant) external view returns (bytes32[] memory)
function getSolverIntents(address solver) external view returns (bytes32[] memory)
function getTotalIntents() external view returns (uint256)
function isAuthorizedCaller(address caller) external view returns (bool)
IntentRecord Struct
struct IntentRecord {
bytes32 intentId; // Unique intent identifier
address merchant; // Funding merchant
address recipient; // Reward recipient
uint256 amount; // USDC amount
uint256 createdAt; // Registration timestamp
uint256 fulfilledAt; // Fulfillment timestamp (0 if pending)
address solver; // Fulfilling solver (address(0) if pending)
bool fulfilled; // Whether the intent has been fulfilled
}
Events
event IntentRegistered(bytes32 indexed intentId, address indexed merchant, uint256 amount);
event IntentFulfilled(bytes32 indexed intentId, address indexed solver);
event CallerAuthorized(address indexed caller);
event CallerRevoked(address indexed caller);
Custom Errors
error NotAuthorized(); // Caller is not authorized
error AlreadyRegistered(); // Intent ID already exists
error AlreadyFulfilled(); // Intent already fulfilled
V1 Contracts
IntentRegistry (V1)
function setOriginSettler(address _originSettler) external
function registerIntent(bytes32 intentId, address merchant, address recipient, uint256 amount) external
function markFulfilled(bytes32 intentId, address solver) external
function getIntent(bytes32 intentId) external view returns (IntentRecord memory)
function getMerchantIntents(address merchant) external view returns (bytes32[] memory)
function getRecipientIntents(address recipient) external view returns (bytes32[] memory)
function getSolverIntents(address solver) external view returns (bytes32[] memory)
function getTotalIntents() external view returns (uint256)
function getAllIntents(uint256 offset, uint256 limit) external view returns (bytes32[] memory)
PodiumOriginSettler (V1)
function depositFunds(address token, uint256 amount) external
function withdrawFunds(address token, uint256 amount) external
function createIntent(address merchant, address recipient, address token, uint256 amount, uint256 duration, bytes32 campaignId) external returns (bytes32)
function lockIntent(bytes32 intentId) external
function markFulfilled(bytes32 intentId, address solver, bytes32 fulfillmentTxHash) external
function cancelIntent(bytes32 intentId) external
function setDestinationSettler(address _destinationSettler) external
function setRegistry(address _registry) external
function getIntent(bytes32 intentId) external view returns (RewardIntent memory)
function getMerchantBalance(address merchant) external view returns (uint256)
PodiumDestinationSettler (V1)
function fulfillIntent(bytes32 intentId, address token, address recipient, uint256 amount) external returns (bytes32)
function verifyProof(bytes32 intentId) external
function claimRewards(address token) external
function setOriginSettler(address _originSettler) external
function getProof(bytes32 intentId) external view returns (FulfillmentProof memory)
function getSolverRewards(address solver) external view returns (uint256)
Enums
// Task lifecycle
enum TaskStatus {
Open, // 0 — Awaiting solver
Claimed, // 1 — Solver committed
SubmittedForVerification, // 2 — Proof submitted, awaiting verification
Verified, // 3 — Verification passed
Settled, // 4 — Payout queued
Expired, // 5 — Deadline passed
Cancelled // 6 — Cancelled by merchant/admin
}
// How task completion is verified
enum VerificationMethod {
Consensus, // 0 — Multi-party agreement
Oracle, // 1 — Single authorized oracle
AIEval // 2 — AI evaluation engine
}
// Verification outcome
enum VerificationStatus {
Pending, // 0 — Awaiting resolution
Approved, // 1 — Task passed verification
Rejected // 2 — Task failed verification
}
// V1 intent lifecycle
enum IntentStatus {
Pending, // 0 — Created, awaiting solver
Locked, // 1 — Solver committed
Fulfilled, // 2 — Settlement complete
Expired, // 3 — Deadline passed
Cancelled // 4 — Cancelled by merchant
}
Proxy Patterns
| Contract | Pattern | Rationale |
|---|---|---|
| SolverRegistry | UUPS (ERC1967) | Singleton — one per deployment |
| VerificationEngine | UUPS (ERC1967) | Singleton — one per deployment |
| IntentRegistryV2 | UUPS (ERC1967) | Singleton — one per deployment |
| TaskPoolImplementation | Beacon | Multi-tenant — all pools share same implementation |
| RewardPoolImplementation | Beacon | Multi-tenant — all pools share same implementation |
| V1 Contracts | None | Non-upgradeable |
Storage Namespacing
V2 contracts use EIP-7201 storage namespacing to prevent storage collisions during upgrades:bytes32 private constant STORAGE_SLOT =
keccak256(abi.encode(uint256(keccak256("podium.storage.TaskPool")) - 1)) & ~bytes32(uint256(0xff));

