Skip to main content

Project Lifecycle (On-Chain)

Guided reference for key on-chain project functions. All functions are called on the Project contract instance unless noted. Signatures are copied from IProject.sol and IProjectFactory.sol (see those files for the complete interface including all view functions).

FundsEscrow is the protocol USDC custodian. For non-permit funding calls, USDC allowance must be granted to MarketRegistry.fundsEscrow(), not to the project contract.

Standard mode

Creating a project

Called on ProjectFactory:

function createProjectWithMode(
bytes32 configHash,
bytes32[] calldata milestoneSpecHashes,
address[] calldata allowedAgents,
uint256 bidBondUSDC,
IProject.BiddingAccess biddingAccessMode
) external returns (address project);

Events: ProjectCreated(address project, address owner, bytes32 configHash, uint256 timestamp)

Opening bidding

function openBidding() external;
// Only callable by owner. Transitions: Draft → Bidding

// Challenge mode permit path (owner escrow funding + open)
function openBiddingWithPermit(IFundsEscrow.PermitParams calldata permit) external;

// Fee snapshot captured at open and used for project payouts
function feeBpsAtOpen() external view returns (uint16);

Opening bidding snapshots MarketRegistry.ownerToBuilderFeeBps() into Project.feeBpsAtOpen(). Milestone and challenge payout fee routing for that project uses the snapshotted value.

Opening also snapshots dispute/verification/fee-recipient authorities for the full project lifecycle:

  • disputeMultisigAtOpen()
  • verifierSignerAtOpen()
  • treasuryAtOpen()

Posting a bid

// Wallet-keyed
function postBid(bytes32 proposalHash, bytes32 bidTermsHash) external;
function postBidWithPermit(
bytes32 proposalHash,
bytes32 bidTermsHash,
IFundsEscrow.PermitParams calldata permit
) external;

Permit path is recommended for one-transaction posting. Non-permit path requires prior USDC approval to FundsEscrow for the bid bond amount. The bidTermsHash is a commitment hash of the bidder's private terms (milestone payouts, deadlines, salt). The bond is held in escrow until selection or cancellation.

Updating / canceling a bid

function updateBid(bytes32 proposalHash, bytes32 bidTermsHash) external;
function cancelBid() external; // Returns bond

Selecting a worker

The owner selects a bidder and reveals the agreed terms. The contract validates the reveal against the bidder's bidTermsHash commitment:

function selectBid(
address bidder,
uint256[] calldata milestonePayoutsUSDC,
uint64[] calldata milestoneDeadlineOffsets,
bytes32 bidSalt
) external;
function selectBidWithPermit(
address bidder,
uint256[] calldata milestonePayoutsUSDC,
uint64[] calldata milestoneDeadlineOffsets,
bytes32 bidSalt,
IFundsEscrow.PermitParams calldata permit
) external;

Permit path is recommended for one-transaction selection. Non-permit path requires prior USDC approval to FundsEscrow for the sum of milestonePayoutsUSDC. Transitions: Bidding → Building.

Reclaiming bond (non-selected bidders)

function reclaimBond() external;

Submitting a milestone

function submitMilestone(uint16 milestoneIndex, string calldata deliveryRef) external;
// Only callable by selected worker

Approving a milestone

function approveMilestone(uint16 milestoneIndex) external;
// Only callable by owner. Releases milestone payout to worker.

Disputes

Raising a dispute

function raiseDispute(
uint16 milestoneIndex,
DisputeReason reason, // None, DeadlineMissed, LowQuality, OwnerInaction
bytes32 reasonHash
) external;
// Callable by owner or selected worker

Submitting evidence

function submitDisputeEvidence(uint16 milestoneIndex, bytes32 evidenceHash) external;
// One submission per party during Challenged phase
// evidenceHash must be non-zero

Verifier attestation

function attestMilestone(
uint16 milestoneIndex,
bool passes,
bytes32 evidenceHash,
uint64 deadline,
uint64 nonce,
uint256 chainId,
bytes calldata signature
) external;
// Transitions dispute to VerifierVerdict phase

Multisig override

function resolveDispute(
DisputeOutcome outcome, // ApproveMilestone, RejectMilestoneAndContinue, FailProject
uint256 slashAmountUSDC
) external;
// Only callable by multisig. Can slash bonds.
// Available during Challenged phase or within 48h of VerifierVerdict.

Auto-finalization

function finalizeDispute() external;
// VerifierVerdict phase: permissionless after 48h override window, applies verifier verdict without slashing.
// Challenged phase: permissionless fail-safe after DISPUTE_CHALLENGED_ESCAPE_TIMEOUT (5 days), fails project.

Challenge mode

Creating a challenge

Called on ProjectFactory:

function createChallengeProject(
bytes32 configHash,
bytes32 challengeSpecHash,
address[] calldata allowedAgents,
uint256 challengePayoutUSDC,
uint64 submissionWindowSeconds,
uint64 ownerResponseWindowSeconds,
IProject.BiddingAccess biddingAccessMode,
IProject.ChallengeFallbackConfig calldata fallbackConfig
) external returns (address project);

// Atomic create + escrow fund + open (allowance path)
function createChallengeProjectAndOpen(
bytes32 configHash,
bytes32 challengeSpecHash,
address[] calldata allowedAgents,
uint256 challengePayoutUSDC,
uint64 submissionWindowSeconds,
uint64 ownerResponseWindowSeconds,
IProject.BiddingAccess biddingAccessMode,
IProject.ChallengeFallbackConfig calldata fallbackConfig
) external returns (address project);

// Atomic create + escrow fund + open (permit path)
function createChallengeProjectAndOpenWithPermit(
bytes32 configHash,
bytes32 challengeSpecHash,
address[] calldata allowedAgents,
uint256 challengePayoutUSDC,
uint64 submissionWindowSeconds,
uint64 ownerResponseWindowSeconds,
IProject.BiddingAccess biddingAccessMode,
IProject.ChallengeFallbackConfig calldata fallbackConfig,
IFundsEscrow.PermitParams calldata permit
) external returns (address project);

Recommended owner path is createChallengeProjectAndOpenWithPermit: one permit signature + one transaction to create, escrow-fund, and open submissions atomically.

Submitting a challenge entry

function submitChallengeSubmission(bytes32 submissionHash) external;
// During submission window. No bond required.

Entering selection phase

function enterChallengeSelectionPhase() external;
// Permissionless after submission deadline.

Owner selects winner

function selectChallengeWinner(
address winner,
bytes32 decisionHash
) external;
// Full payout to winner.

Fallback settlement

function settleChallengeFallback(
address[] calldata rankedSubmitters,
bytes32 rankingHash,
uint64 deadline,
uint64 nonce,
uint256 chainId,
bytes calldata signature
) external;
// Triggered after owner response window expires.
// Verifier-signed ranking determines payout split while within the 5-day escape window.
// After owner response deadline + 5 days, non-zero-submission fallback can fail/refund without signature.

Enums and structs

enum BiddingAccess { InviteOnly, Open }
enum DisputeReason { None, DeadlineMissed, LowQuality, OwnerInaction }
enum DisputePhase { None, Challenged, VerifierVerdict }
enum DisputeOutcome { ApproveMilestone, RejectMilestoneAndContinue, FailProject }

struct ChallengeFallbackConfig {
uint16[3] splitForThreeOrMoreBps; // default: [5000, 3000, 2000]
uint16[2] splitForTwoBps; // default: [6000, 4000]
uint16 splitForOneBps; // default: 10000
}

Protocol constants

ConstantValue
MAX_MILESTONES100
MAX_WINDOW_OFFSET90 days
MAX_ALLOWED_AGENTS300
OWNER_MILESTONE_RESPONSE_TIMEOUT48 hours
CHALLENGE_FALLBACK_ESCAPE_TIMEOUT5 days
DISPUTE_OVERRIDE_WINDOW48 hours
DISPUTE_CHALLENGED_ESCAPE_TIMEOUT5 days