Project documentation
How the site works, from an active drop on the map to a payment in your wallet. The rules, the contracts and what they actually verify.
The project
Feed Me is a location-based rewards app. Sponsors fund drops at selected McDonald’s restaurants. You visit an active location, claim a randomly selected burger reward and receive its token amount in your wallet. There is no receipt upload or reimbursement request.
| Component | Role |
|---|---|
| The browser | Displays the map, requests GPS access and lets you review wallet signatures and transactions. |
| The verifier server | Checks signed requests, location and quest proofs, selects the reward and signs a short-lived authorization. |
| The on-chain vault | Holds the funded tokens, enforces claim rules, records XP and pays the claimant. |
The map & drops
The world map separates restaurant locations from funded drops. A yellow restaurant marker is a place in the directory. A pulsing red marker means an active, funded drop with claims remaining. Being on the map does not make a restaurant a partner or guarantee a reward.
Each drop has a restaurant, an opening time, an expiry time, a claim limit and its own budget. Availability is read from the configured contract and refreshed regularly. An expired, empty or unavailable drop cannot be claimed. “Near me” centers the map using your location; selecting a restaurant opens its details and directions.
Explore the mapThe claim process
- Locate & signSign in to your account with a linked wallet, or connect your wallet directly. At an active restaurant, allow precise location and complete the anti-bot check. Direct wallet mode also asks you to sign the claim request.
- Verify & authorizeThe server checks eligibility and returns a signed authorization for this wallet, drop, claim index and exact payout. It is valid for at most two minutes, and never beyond the drop’s end or the UTC day boundary.
- Confirm & receiveIn account mode, the payment relay submits the transaction for your permanently linked wallet and covers its network fee. In direct wallet mode, you confirm and pay the fee yourself. The reveal waits for the confirmed payment to your wallet.
One successful burger claim every 12 hours, per wallet. The countdown starts at the confirmed transaction’s block time, across all drops, including XP rewards. Crossing midnight does not reset it. The limit follows the wallet, not a verified personal identity.
An authorization does not reserve stock: another transaction can take the last slot first. If a transaction is pending, the app tracks its hash and, when browser storage is available, restores it after a reload. A timeout is not proof of payment; confirmation on the explorer is what counts.
Rewards & the draw
| Reward | Base amount | Draw weight |
|---|---|---|
| Hamburger | 3 USDG | 45% |
| Cheeseburger | 4 USDG | 30% |
| Double cheeseburger | 5 USDG | 20% |
| Big Mac | 7 USDG | 5% |
The server uses a secret HMAC draw keyed to the network, vault, wallet and next claim index. Refreshing, changing restaurants or rejecting the wallet prompt does not reroll that claim while the draw key stays the same.
These weights are implemented by the server. The contract checks the signed reward and amount, not the randomness or its distribution. This is not a VRF or a publicly verifiable random draw: you trust the verifier for reward selection.
Redeeming 300 available XP guarantees the Big Mac reward instead of the random draw. It still uses one funded slot and respects the 12-hour cooldown. Winning a Big Mac in a normal draw does not spend XP.
Quests, XP & levels
Every confirmed burger claim earns 40 XP, including a loyalty claim. Quests add XP after their proof is verified and you confirm the claimQuest transaction. Completing an action without confirming its quest does not record XP.
| Action to verify | XP | Frequency |
|---|---|---|
| Be at an active, funded drop | 20 | Once per UTC day |
| Hold at least 100 FRY | 25 | Once per UTC day |
| Have 1 confirmed burger claim | 50 | Once per wallet |
| Have 2 confirmed burger claims | 100 | Once per wallet |
Daily quests reset at 00:00 UTC. They do not wait for the burger cooldown. FRY eligibility uses the token balance, not a dollar price: there is no $10 valuation oracle or minimum holding duration. The vault checks the FRY balance and burger milestones again when the quest transaction executes.
Available XP vs lifetime XP
Available XP = lifetime XP − XP already redeemed. Your level follows lifetime XP, so redeeming a burger does not lower it. XP, levels and quest history stay on their original wallet.
| Lifetime XP | Level title |
|---|---|
| 0 | Exit Liquidity |
| 100 | Grill Intern |
| 300 | Diamond Fries |
| 650 | Dip Manager |
| 1,200 | McWhale |
| 2,500 | CEO of the Domac |
Funding & contracts
A sponsor creates a drop by depositing its full token budget into BurgerDropVault. The minimum funding covers every allocated claim at the maximum possible reward, including any already-earned legacy bonus. A simple token transfer to the vault does not create a drop. Each payment reduces that drop’s budget and stock atomically.
After a drop expires, its original sponsor can recover its unused budget through refundDrop. The current vault has no general owner withdrawal, pause switch, proxy upgrade or verifier-key rotation. Its verifier address is fixed at deployment; changing that trust setup requires a new vault.
Public deployment
- Network
- Robinhood Chain · 4663
- Active catalog drops
- —
- Remaining catalog budget
- —
- Historical vault claims
- —
- Historical vault payments
- —
- Last observation (UTC)
- —
- Payout token · USDG
- 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
These figures come from the site’s public API, refreshed every 15 seconds. Catalog budget is the sum of listed drop allocations, not the vault’s entire token balance. Historical totals include earlier claims. Use the contract links to inspect balances, transactions and events independently; this dashboard is not an independent audit.
Account & wallet
Choose Sign in at the top right to create an account with a username and password. Save the recovery code shown once: it is needed if you forget your password. There is no email recovery. You can also choose to use a wallet directly without creating an account.
Link your wallet once from a device where it is available, by signing the account association message. The link is permanent: one wallet per account, and one account per wallet. Then sign in from another phone or computer to claim without connecting the wallet there. Rewards and XP always remain on that same wallet.
| Asset | Purpose |
|---|---|
| ETH | Real ETH on Robinhood Chain pays the network fees for claims and quests. Testnet ETH does not work here. |
| USDG | The mainnet reward token sent by funded drops to your wallet. It is separate from the ETH used for fees. |
| FRY | Optional holding quest. Buying, spending or burning FRY is not required for a burger claim. |
Use the exact wallet approved by the pilot organizer. Connecting another address does not grant access, even if it holds ETH or FRY. The server checks pilot eligibility before authorizing claims or quests.
The wallet association signature does not move funds or grant token-spending permission. It allows authenticated account sessions to claim rewards and redeem that wallet’s loyalty XP through the relay. Protect your password and recovery code; use Sign out all devices if needed. Feed Me never requests your wallet recovery phrase or private key.
Checks & limitations
| Check | Current rule |
|---|---|
| Wallet access | Verified account session or wallet-signed request; the same 12-hour wallet cooldown. |
| Anti-bot check | Single-use Turnstile token for the requested action, plus shared request limits. |
| Connection limit | By default, 3 new burger authorizations per connection over a rolling 12 hours, across all wallets and drops. |
| GPS freshness | At most 30 seconds old; at most 5 seconds of clock lead. |
| GPS accuracy | Positive reported accuracy, no worse than 80 metres. |
| Restaurant distance | 30 m + reported accuracy capped at 25 m: at most 55 m. |
| Request signature | Exact origin and wallet, fresh timestamp, strictly validated fields. |
| On-chain protection | Authorized signer, expiry, single-use claim index, cooldown, stock, budget and XP. |
An issued burger authorization reserves a place in the connection limit even before you send the transaction. Retrying the same wallet claim does not reserve a second place. People sharing Wi-Fi share this limit too.
GPS is reported by the device and can be spoofed. Changing IP addresses or using a VPN can evade the connection limit. These controls reduce abuse without proving a unique person or physical presence. The contract still enforces its 12-hour limit per wallet.
Missing configuration, a failing RPC or inconsistent funding stops authorization instead of generating a simulated payout. A compromised verifier could authorize false location or quest proofs and influence reward selection, within the contract’s enforced payout and claim limits.
Public API
Read endpoints return JSON. Direct-wallet claim and quest endpoints prepare signed authorizations. The account rewards endpoint submits an authorized transaction through the payment relay and returns its hash. A hash alone does not prove payment: wait for its successful receipt.
| Endpoint | Input | Output |
|---|---|---|
GET /api/drops | Optional wallet query | Status, drops, vault totals; profile if requested |
GET /api/quests | wallet | Wallet snapshot and quests |
POST /api/drops/claim | wallet, dropId, position, loyalty, timestamp, signature, turnstileToken | authorization, signature, reward |
POST /api/quests | wallet, quest, timestamp, signature, turnstileToken + quest proof | authorization, signature, xp |
GET /api/member/status | HttpOnly session cookie | Account and linked wallet, or null |
POST /api/member/rewards | Session, kind, proof and Turnstile; no supplied wallet | Transaction hash for the linked wallet |
GET /api/member/rewards | HttpOnly session cookie | Latest saved reward transaction |
Claim and quest POST requests require a single-use Turnstile token and the exact wallet-signed request. Bodies are strict JSON, limited to 12 KiB. The location quest adds position and storeId. Quest 2 is retired and cannot issue a new authorization. Request and GPS timestamps are milliseconds; authorization times are seconds. authorization.payoutAmount uses integer base units; reward.payout, remainingBudget, totalPaid and fryBalance are formatted decimal strings.
GET /api/drops
position = { lat, lng, accuracy, timestamp }
EIP-712 domain
name: Proof of Burger Drops
version: 1
chainId: 4663
verifyingContract: 0x3fafd92fe977E43D0B493E6222D115414f1b9b66
Claim authorization
claimant, dropId, claimIndex, item, payoutAmount,
loyalty, issuedAt, expiresAtAlways inspect status in the JSON: HTTP 200 can still mean unavailable or unconfigured. Errors use an error field: 400 malformed request, 403 eligibility or captcha rejection, 409 state conflict, 413 body too large, 422 verification failure, 429 rate limit, 503 service unavailable. Rate limits use shared storage; the vault also enforces claim and quest reuse rules.
Read the public drop snapshotData & privacy
Accounts store your username, linked public wallet, salted password hash, recovery-code hash and session-token hashes in PostgreSQL. The browser receives an HttpOnly session cookie, valid for up to 30 days. Signing out all devices, recovering your account or changing your password revokes other sessions. The server never stores your wallet private key.
The restaurant directory is an OpenStreetMap snapshot, not an official McDonald’s feed. Coverage and coordinates can be incomplete or outdated. World and street views use OpenFreeMap with MapLibre; satellite imagery uses Esri, with a simpler OpenStreetMap fallback when WebGL is unavailable. Map providers receive tile requests for the area you view.
GPS access is requested when you use location features. A claim sends the GPS reading to the verifier, authenticated by your account session or wallet signature. Wallet addresses, token payments, XP and claim events are public on-chain; drop identifiers can be matched to the public restaurant catalog. Your chosen sharing app controls who can see a shared link or card. These mechanics do not provide anonymous location rewards.
Turnstile runs through Cloudflare. PostgreSQL stores temporary quota records using keyed hashes (HMAC), without storing raw IP addresses in those records. The connection limit groups each IPv4 address or IPv6 /64 network. Claim history remains on-chain. Disconnecting a wallet does not erase that history or reset its cooldown.
Taking part in the pilot
On Robinhood Chain mainnet
Before travelling, have your wallet approved for the pilot and check that a funded drop is open. Use this wallet on Robinhood Chain, chain ID 4663. Direct wallet claims need real ETH for fees; account claims use the funded relay. At the restaurant, enable precise location, complete the anti-bot check and review the signed claim request and transaction. A confirmed claim pays USDG to your wallet; verify it with the explorer link. The pilot is not open to every wallet, and the app does not pay at the restaurant counter for you.
From a local checkout
For contributors with the repository: install Node.js, npm and Foundry (Anvil and Forge), then run the commands below. The local runner starts an isolated Anvil chain on port 8545 and the app on port 3001, with test contracts and funded local accounts.
npm ci
npm run contracts:build
npm run local
# Application and contract checks
npm test
npm run contracts:testPublic deployments must use matching network, vault and token configuration. The verifier key must match the vault’s immutable verifier. VERIFIER_PRIVATE_KEY and CHECK_IN_HMAC_SECRET are server-only secrets; they must never be included in NEXT_PUBLIC variables or browser code. The mainnet pilot also requires a configured list of approved wallets and real funding for every advertised slot. Without this configuration, authorizations remain closed.
Back to the quick guide