Skip to content

Smart contracts ​

work2own V1 is two Solidity contracts (compiler 0.8.37) on Robinhood Chain mainnet, chain ID 4663.

Both contracts are source-verified on Blockscout, so anyone can read the deployed code: Work2OwnEscrow and StockPayout.

Addresses ​

NameAddress
Work2OwnEscrow0xB20703EB380d40601fC57b05a1965e6131Df422b
StockPayout0xEC5fe4d31d35FCECD7FC9b3D6A1B6eDAA1493d8d
Admin (deployer)0x5Dd2868AC5039fFb7597146363ba4EA03Ce8D562
Operator0x0eF95c4aA8095De41332646eD433dc183c99231c
Arbiter and treasury0xf4af5Cc55D7dcDAEb958B7582d7e9d5E758D2Aeb

External contracts used:

NameAddress
USDG (6 decimals)0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
Robinhood stock token registry0xe10b6f6B275de231345c20D14Ab812db62151b00
Uniswap SwapRouter020xCaf681a66D020601342297493863E78C959E5cb2
Uniswap QuoterV2 (used off-chain for quotes)0x33e885eD0Ec9bF04EcfB19341582aADCb4c8A9E7

Stock tokens have 18 decimals. In the contracts, a payout token of 0x0000000000000000000000000000000000000000 means USDG.

Roles ​

RoleContractHeld byCan do
DEFAULT_ADMIN_ROLEBothAdminPause and unpause the escrow; set the fee (max 5%), the maximum deposit and the treasury; add, change or disable payout tokens; set the slippage (max 5%) and the per-payout cap for quote-protected tokens; rescue tokens sent by mistake (never USDG from the escrow); grant and revoke all roles
OPERATOR_ROLEEscrowOperatorVerify or reject automatic quests; run pending payouts with a price quote
ARBITER_ROLEEscrowArbiterSettle disputed gigs
ESCROW_ROLEStockPayoutEscrowCall swapAndDeliver

Work2OwnEscrow ​

Constants ​

ConstantValue
SLOT_RESERVATION24 hours
REVIEW_WINDOW7 days
PAYOUT_FALLBACK_DELAY3 days
MAX_FEE_BPS500 (5%)
BPS_DENOMINATOR10,000

Quest functions ​

FunctionWhoRules
createCampaign(rewardPerSlot, slots, deadline, manualReview)AnyoneReward and slots above 0, deadline in the future, reward × slots ≤ maxDeposit. Takes reward × slots into the escrow and the fee to the treasury
claimSlot(campaignId, payoutToken)Anyone but the employerQuest open (not closed, before deadline), a slot free, token enabled or USDG, and this wallet never claimed this quest
releaseExpiredSlot(campaignId, worker)AnyoneReservation older than 24 hours, or deadline passed. The claim becomes Expired
submitQuest(campaignId, proofHash)The reserving workerWithin 24 hours of reserving and before the deadline
approveQuest(campaignId, worker)EmployerManual quests only
verifyQuest(campaignId, worker, quotedMinOut)OperatorAutomatic quests only
releaseQuest(campaignId, worker)AnyoneManual quests, at least 7 days after submission
rejectQuest(campaignId, worker)Employer within 7 days (manual), or operator (automatic)Frees the slot
closeCampaign(campaignId)AnyoneAfter the deadline, no submission waiting. Refunds reward × (slots − completed) to the employer

Gig functions ​

FunctionWhoRules
createGig(worker, budget, deadline)AnyoneWorker is not the caller, budget above 0 and ≤ maxDeposit, deadline in the future. Takes the budget into the escrow and the fee to the treasury
submitGig(gigId, deliverableHash, payoutToken)The named workerGig funded, before the deadline, token enabled or USDG
approveGig(gigId)EmployerGig delivered
releaseGig(gigId)AnyoneAt least 7 days after delivery
rejectGig(gigId, reasonHash)EmployerWithin 7 days of delivery. The gig becomes Disputed
resolveDispute(gigId, workerShareBps)ArbiterGig disputed. Worker gets budget × share / 10,000; the rest is refunded to the employer
cancelGig(gigId)EmployerGig not delivered and deadline passed. Refunds the full budget

Payout and refund functions ​

FunctionWhoRules
retryPayout(payoutId, quotedMinOut)The payout's worker, or the operatorPayout pending
changePayoutToken(payoutId, newPayoutToken, quotedMinOut)The payout's workerPayout pending for at least 3 days. Switches the token (or to USDG) and tries again at once
withdrawRefund()Anyone with a pending refundSends the caller's own pending refund to the caller

Pause ​

Pause stops every function that moves USDG or finalizes work. These keep working while paused, so a pause never uses up a deadline or a review window: claimSlot, releaseExpiredSlot, submitQuest, rejectQuest, submitGig, rejectGig.

Views ​

getCampaign, getClaim, getGig, getPayout, pendingRefunds(account), feeBps, maxDeposit, treasury, campaignCount, gigCount, payoutCount, paused.

Statuses: claims are None, Reserved, Submitted, Completed, Rejected or Expired. Gigs are None, Funded, Submitted, Disputed, Completed, Cancelled or Resolved. Payouts are None, Pending or Paid.

Events ​

CampaignCreated, SlotClaimed, SlotReleased, QuestSubmitted, QuestCompleted, QuestRejected, CampaignClosed, GigCreated, GigSubmitted, GigCompleted, GigRejected, GigCancelled, DisputeResolved, PayoutCreated, PayoutPaid, PayoutDeferred (with the revert reason), PayoutTokenChanged, RefundDeferred, RefundWithdrawn, TreasuryUpdated, FeeUpdated, MaxDepositUpdated, TokenRescued, Paused, Unpaused.

StockPayout ​

FunctionWhoWhat it does
swapAndDeliver(stockToken, recipient, usdgAmount, quotedMinOut)Escrow onlyRuns the safety checks, swaps USDG for the stock token on the token's Uniswap V3 pool with the worker as recipient, and checks the delivered amount
previewOracleMinOut(stockToken, usdgAmount)ViewThe minimum an oracle-protected payout would accept right now
tokenConfig(stockToken)ViewEnabled flag, tier, decimals, pool fee, price feed and maximum price age
slippageBps(), tierQuoteMaxUsdg()ViewCurrent slippage and per-payout cap for quote-protected tokens
setToken, disableToken, setSlippageBps, setTierQuoteMaxUsdg, rescueTokenAdminConfiguration

The checks and the price formula are explained in Stock payouts.

StockPayout never keeps stock tokens and holds USDG only inside a single swap transaction.

work2own is an independent project. It is not affiliated with, endorsed by or sponsored by Robinhood Markets, Inc. Nothing here is investment advice.