TrustLance is a freelance marketplace where every escrow is protected by proof-of-personhood.
TrustLance is a trustless freelance escrow marketplace deployed on 0G Chain, an EVM-compatible AI Layer 1. It solves two fundamental problems with crypto freelance payments: silent transaction failures during fund release, and price volatility between deposit and delivery. When a client creates a job, they deposit any ERC-20 token or native ETH. The contract calls Uniswap's Universal Router inline to swap the deposit to USDC before locking it, so freelancers always receive stable value regardless of what the client paid with. The client sets a minimum USDC floor for slippage protection, and the swap uses a single-hop V3 path with configurable pool fee tiers. When the client approves delivered work, funds are not transferred immediately. Instead the contract calls KeeperHub's registerTask() to register an on-chain automation task. KeeperHub monitors the task and calls the contract's _executeRelease() function with SLA-backed execution guarantees, automatic retry on gas spikes, and a full audit trail stored on-chain via taskId. The KeeperHub executor address is an immutable on the contract, so no other account can trigger the release. Dispute resolution requires no arbitration. If the client disappears after a freelancer accepts the job, a 30-day timelock allows the freelancer to self-reclaim the full escrowed amount by calling reclaimAfterTimeout(). The entire protocol charges 0% fees at launch. The frontend is built in Next.js 16 with wagmi and RainbowKit connected to 0G Galileo Testnet (chain ID 16602). All job state is read live from the chain. The contract is on 0G Chainscan at 0xfE75E4472359968f5EEd507021616Ee8Fb6BD540.
The smart contract is written in Solidity 0.8.24 and deployed on 0G Galileo Testnet using Hardhat v3 with the viem toolbox. The core escrow logic is about 300 lines with full CEI (Checks-Effects-Interactions) pattern, ReentrancyGuard on every token-touching function, SafeERC20 for all transfers, a protocol fee hard-capped at 5%, and an emergency pause that halts new jobs without affecting active ones. The Uniswap integration lives entirely inside the contract. When a client deposits ETH, the contract encodes a two-command sequence for the Universal Router: CMD_WRAP_ETH (0x0b) followed by CMD_V3_SWAP_EXACT_IN (0x00), sending ETH as msg.value and receiving USDC directly to the contract address. For ERC-20 deposits, it grants Permit2 a scoped allowance for exactly the deposit amount, then calls the router with payerIsUser=true so Permit2 pulls from the contract rather than the caller. The V3 path is encoded as abi.encodePacked(tokenIn, fee, tokenOut) — a uint24 fee packed directly between two addresses. Getting this encoding right required reading the Universal Router Solidity source rather than the documentation, since the docs do not make the uint24 packing explicit and a wrong type produces a silent zero-output failure rather than a revert. The KeeperHub integration uses a two-step separation between approval intent and execution. When a client approves work, approveWork() calls keeperHub.registerTask() with the ABI-encoded calldata for _executeRelease(jobId), a one-hour deadline, and a 200k gas limit. The returned taskId is stored in the Job struct and indexed via a taskToJob mapping so the frontend can track it. The _executeRelease function is gated by an onlyKeeper modifier that checks msg.sender against an immutable keeper address set at deploy time — only the KeeperHub executor EOA can call it. Because KeeperHub is not yet deployed on 0G testnet, we wrote a MockKeeperHub that mirrors the exact interface, including an executeTask() function that calls the target contract with the stored calldata from the registered task. This gave us full end-to-end test coverage of the entire flow including the keeper execution step. The frontend is Next.js 16.2 with Tailwind v4, shadcn/ui, and lucide-react. All chain interactions go through eight custom hooks in hooks/escrow/ built on wagmi v2 and viem. The hooks use useReadContracts to batch-read the latest 30 jobs in a single RPC call, useChainId with an isSupportedChain guard so no hook fires on the wrong network, and useWaitForTransactionReceipt to track confirmation state. The UnsupportedChainBanner component detects when the connected wallet is not on 0G testnet and offers a one-click switch via useSwitchChain. The USDC approve-then-create flow is handled in two sequential steps in the PostJob page — the button label changes based on whether the current allowance covers the deposit amount, making the two-transaction flow obvious to the user. One notably hacky thing worth mentioning: because 0G testnet has no real USDC, the PostJob page includes a "Mint 1,000 USDC" button that calls the mint() function on our MockERC20 directly from the UI, functioning as an inline testnet faucet. This made the hackathon demo dramatically smoother since judges can fund themselves without leaving the app.

