<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[I Need Some.one Blog]]></title><description><![CDATA[I Need Some.one Blog]]></description><link>https://ineedsomeone.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 27 Oct 2026 05:43:47 GMT</lastBuildDate><atom:link href="https://ineedsomeone.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building a pull-payment escrow on Base for small, real-world tasks]]></title><description><![CDATA[How I Need Some.one holds USDC until proof arrives: signed deal terms, pull payouts, passkey wallets, sponsored gas and a two-signature dispute path.]]></description><link>https://ineedsomeone.hashnode.dev/building-a-pull-payment-escrow-on-base-for-small-real-world-tasks</link><guid isPermaLink="true">https://ineedsomeone.hashnode.dev/building-a-pull-payment-escrow-on-base-for-small-real-world-tasks</guid><category><![CDATA[Blockchain]]></category><category><![CDATA[Solidity]]></category><category><![CDATA[Web3]]></category><category><![CDATA[Ethereum]]></category><category><![CDATA[buildinpublic]]></category><dc:creator><![CDATA[Farzad]]></dc:creator><pubDate>Thu, 22 Oct 2026 20:59:03 GMT</pubDate><content:encoded><![CDATA[<p>I'm Farzad, and for the past while I've been building <a href="https://ineedsome.one">I Need Some.one</a> on my own, self-funded. It's a marketplace for small tasks that need a person in a specific place: check whether a shop actually has something in stock, view an apartment before you rent it, call a supplier in the local language, pick up documents.</p>
<p>You post the task with the place, the deadline, the price and the proof you want (photos, video, a receipt, a location check). People nearby make offers, and you pick one based on their public profile, ratings and reviews. The payment waits in escrow and is released when the proof arrives and you approve it.</p>
<p>It's live on Base mainnet with real USDC. Posting is free. It's also pre-revenue and early, so this post isn't a launch announcement. It's a write-up of the design, because I'd like people who know this space to poke holes in it.</p>
<h2>The problem: paying a stranger who is far away</h2>
<p>The hard part of this product isn't matching. It's trust. If I'm in Istanbul and need someone in Lisbon to look at a flat, neither of us wants to go first. I don't want to pay before I see anything, and they don't want to do the work and then hope I pay.</p>
<p>The usual answer is that the platform holds the money. I wanted it somewhere both sides can check, under rules nobody (including me) can quietly change. So payments go into a public smart contract on Base, in Circle's native USDC.</p>
<h2>Signed terms, funded by the requester</h2>
<p>Each deal's terms (price, fee rate, milestones, deadlines, review period, grace period, dispute timeout) are signed by the platform's deal signer as EIP-712 typed data. The requester then funds the escrow against those signed terms. Hard bounds for what can be signed are constants in the contract.</p>
<p>Splitting it this way means the platform decides <em>what a valid deal looks like</em>, but the deal signer can't move money. The requester signs and funds every escrow. The signer is a dedicated key held in AWS KMS, separate from the admin role.</p>
<p>Funding is one transaction: a <code>multicall</code> that wraps a USDC permit (<code>selfPermit</code>) and the create/propose/accept call. One detail I had to deal with: if someone front-runs the permit and submits it first, funding still has to work as long as the allowance is in place. So <code>selfPermit</code> tolerates a permit that has already been used.</p>
<h2>Pull payments, not push</h2>
<p>The contract never sends USDC to a user in the middle of a settlement. When a milestone settles, it credits an internal balance: <code>owed[token][account]</code>. Funds leave the contract only through <code>withdrawFor</code> / <code>withdrawForMany</code>, which always pay <em>that account</em>, or through <code>sweepExcess</code>, which can only move surplus above all liabilities to the treasury.</p>
<p>Why bother:</p>
<ul>
<li><strong>Settlement logic stays simple.</strong> Approving, claiming, refunding and resolving disputes are all just accounting. No external token call happens in the middle of a state change.</li>
<li><strong>A blocked transfer doesn't break a deal.</strong> USDC has an issuer blocklist. If a transfer to one address fails, the deal still settles and the credit simply stays owed.</li>
<li><strong>Payouts can be batched and sponsored.</strong> Because <code>withdrawFor</code> is permissionless and can only pay the rightful owner, a platform relayer can call it for users. That relayer is a hot wallet that holds only gas, and the contract treats it as untrusted for funds.</li>
</ul>
<p>The rules I wrote down for the contract and test against include: total liabilities equal the sum of owed credits plus locked amounts; the contract's balance always covers them; each milestone settles exactly once; and money only ever goes to the stored requester, the helper or the treasury.</p>
<h2>The deal lifecycle</h2>
<p>In short:</p>
<ol>
<li>The requester funds a listing. For a fixed-price request that happens at publish. For one open to offers, it happens when they choose an offer.</li>
<li>The helper accepts the exact proposal (by nonce).</li>
<li>The helper submits work. The requester approves, asks for a revision, or disputes.</li>
<li>If the requester goes silent, the helper can claim after the review window. On the site that's 1 or 2 days.</li>
<li>If nothing was submitted by the deadline plus a grace period, the requester can reclaim what hasn't been paid. Submission is allowed up to deadline + grace, inclusive, and reclaim starts strictly after, so the two windows never overlap.</li>
</ol>
<p>Deals can have multiple milestones, and a milestone that's been paid stays paid.</p>
<p>The contract is non-upgradeable. That cuts both ways. Nobody, including me, can swap the logic under a live deal. But if I missed something, I can't patch it in place. It's why I've spent more time on specs and tests than on UI.</p>
<p>Pausing is deliberately one-sided. It blocks <em>new</em> commitments (create, propose, accept, additions) but never blocks an exit, a settlement or a payout. If something goes wrong, I can stop new money coming in without trapping the money already there.</p>
<h2>Disputes: a trusted reviewer, on purpose</h2>
<p>This is the part I expect the most pushback on. Disputes are decided by a neutral reviewer, and the arbitrator role is a 2-of-2 Safe, so every ruling needs two signatures. That's a trusted arbitrator, not a decentralised court.</p>
<p>I considered token-voting and juror-style schemes, but for a $40 store check they add cost, delay and complexity. Instead I tried to limit how much depends on the arbitrator:</p>
<ul>
<li><strong>Mutual split first.</strong> Either side can offer a split of the current milestone, and the other accepts the exact terms. No dispute fee, and it works even during a dispute.</li>
<li><strong>Narrow dispute windows.</strong> The requester can dispute a submission only within its review window. Either party can dispute work in progress until deadline + grace.</li>
<li><strong>A timeout default.</strong> If no ruling arrives within the signed dispute timeout, <em>anyone</em> can apply the default. A standing submission is paid as claimed, and without one the milestone is refunded. Funds can't sit frozen forever because a reviewer went quiet.</li>
</ul>
<p>Evidence lives off-chain: every message and file in a deal is kept, and proof photos can include challenge codes so they can't just be reused from somewhere else.</p>
<h2>Passkey wallets and sponsored gas</h2>
<p>Most people who need an apartment viewed don't own crypto, and shouldn't have to. So:</p>
<ul>
<li>A wallet is created at sign-up and protected by a passkey: Face ID, a fingerprint or the device PIN. No seed phrase. I don't hold users' keys, and the private key can be exported.</li>
<li>You can add USDC with a card or send it from crypto you already hold.</li>
<li>Network fees are covered by the platform. Users never need ETH.</li>
</ul>
<p>The pull-payment design is what makes sponsoring the payout side cheap and safe. A relayer that only holds gas can call payouts for anyone without being able to redirect a cent.</p>
<h2>Fees and limits</h2>
<p>The requester pays exactly the agreed price. The 10% platform fee comes out of the helper's side only. When a task includes an item budget (buying something on the requester's behalf), the fee applies only to the reward part, not the item cost. Deals start at $5. New accounts can go up to $200 per deal, and trusted accounts up to $2,000.</p>
<h2>Honest limitations</h2>
<ul>
<li>It's early. Whether a task gets picked up depends on who happens to be nearby.</li>
<li>A visit from a stranger is not a professional inspection.</li>
<li>The arbitrator is trusted, as described above.</li>
<li>The contract is non-upgradeable, so any bug in it stays there. Please treat this as early software.</li>
</ul>
<h2>Feedback wanted</h2>
<p>If you work on escrow, account abstraction or dispute design, I'd really like to hear where you think this breaks. And if you'd like to earn USDC doing short tasks where you live, you can sign up as a helper.</p>
<ul>
<li>Site: <a href="https://ineedsome.one">ineedsome.one</a> (how it works: <a href="https://ineedsome.one/faq">/faq</a>)</li>
<li>Escrow contract on Base: <a href="https://basescan.org/address/0x4C0CcF43144392B831C46cb68E8e80f76304BCbC"><code>0x4C0CcF43144392B831C46cb68E8e80f76304BCbC</code></a></li>
<li>Contact: <a href="mailto:support@ineedsome.one">support@ineedsome.one</a></li>
</ul>
]]></content:encoded></item></channel></rss>