FOLLOWDOCS
$FOLLOW · Robinhood Chain · docs

Bagwork, explained.

$FOLLOW is a coin that pays you to post about it. One percent of every trade is collected, converted to ETH, and paid out every 24 hours — in Robinhood ETH — to the people talking about $FOLLOW on X. The more you post and the more people interact with those posts, the bigger your cut.

"Bagwork" is what holders already do for every coin: post, shill, reply, quote, keep it in the feed. Usually it's unpaid and nobody tracks it. We built the tracking and the payout so it isn't, and we put the payout on-chain so nobody — including us — hands out the money by hand.

This page explains how the whole thing works end to end, and how it was built. It's written so that you can check every claim against the contracts and the public epoch data.

How a day works

Bagwork runs in 24-hour epochs. During an epoch, every $FOLLOW trade sends its 1% bagwork tax toward the vault. At the end of the epoch the tracker looks at what everyone posted, scores it, and publishes the result on-chain. Then you claim.

1 · TradeTax collects1% of each buy and sell is swapped to ETH and sent to the BagworkVault.
2 · PostYou post on XAnything mentioning $FOLLOW, #FOLLOW or the contract address counts.
3 · SnapshotEpoch closesTracker pulls the day's posts and everyone's balance at the closing block.
4 · PublishRoot goes on-chainA Merkle root of everyone's shares is published to the vault with the day's ETH.
5 · ClaimYou get paidClaim from the desk. The vault sends Robinhood ETH straight to your wallet.

If you don't claim within 30 days, your unclaimed ETH goes back into the pool for the next epoch. Nothing ever leaves the vault except through a claim.

How posts are tracked

Your posts are only counted if the tracker can tie your X account to a wallet that holds $FOLLOW. That's the whole point of linking (see Linking X). Once you're linked, the tracker finds your posts like this:

At the close of each epoch it runs a search against the X API for posts in that window matching $FOLLOW OR #FOLLOW OR "follow coin", plus the contract address once it's live. Reposts are excluded — a repost is someone else's post. From that result set it keeps only posts whose author is a linked X account and whose text actually mentions $FOLLOW (the search can be fuzzy; the text check isn't).

For each post it records the public metrics X reports at snapshot time: likes, replies, reposts, quotes and views. Those are the raw material for scoring. The full list of scored posts for every epoch is published by the API, so you can see exactly which of your posts counted and what numbers they had.

What counts: original posts and quote-posts that mention $FOLLOW, #FOLLOW, "follow coin", or the CA, from a linked X account, posted inside the epoch window.

What doesn't: reposts, posts from unlinked accounts, posts that don't mention the coin, posts from a wallet under the minimum hold, and anything past your best 20 posts for the epoch.

How posts are scored

Two things drive your payout: post volume (how much you post) and interactions (how much people engage with it). Volume gets you a floor on every post; interactions are where the real score comes from. Here is the exact formula the tracker runs:

post score  = 10 + likes + replies + reposts×3 + quotes×3 + views/200
               replies to other people's posts count at half weight
               each post is capped at 500 so one viral post can't take the whole pool

your score  = sum of your best 20 post scores in the epoch

bag mult    = 1 + log10( your balance ÷ 100,000 )     min 1×, capped at 3×
               100k FOLLOW = 1×   1M = 2×   10M+ = 3×

shares      = your score × bag mult

Why these weights: a like is cheap and common, so it's worth 1. A reply is a conversation, also 1. A repost or a quote puts your post in front of someone else's whole audience, so they're worth 3. Views are the biggest number by far, so they're divided by 200 to keep them from drowning everything else out; a post with 20,000 views still earns 100 from views alone.

The 10-point floor means volume is always worth something: twenty posts with zero engagement still score 200. But twenty posts with real interaction score far more, and one post with 1,000 likes and 300 reposts scores the cap of 500 on its own. The system rewards people who both show up daily and make posts other people actually respond to.

Bag multiplier

Bigger bags earn more per post, but with diminishing returns. The multiplier is logarithmic: going from 100k to 1M doubles your shares; going from 1M to 10M only adds another 1×; and it stops at 3× no matter how much you hold. This keeps the biggest holders from owning every epoch while still making it worth holding a real position.

Your balance is read at the block the epoch closes, not when you post. Buying, posting, and selling inside the same day earns nothing.

A worked example

Illustrative numbers, not real accounts — this shows how the formula behaves, not an actual epoch. Real epochs are published at api.followrh.xyz/epoch/:id.

BagworkerPostsScoreBalanceMultSharesCut of the day's ETH
Bagworker A149211.0M2.0×1,84234.0%
Bagworker B20984120k1.08×1,06319.6%
Bagworker C940212M3.0×1,20622.3%
Bagworker D63061.0M2.0×61211.3%
Bagworker E4448100k1.0×4488.3%
everyone else (37)2444.5%

Notice B posted the most and had the highest raw score, but a small bag kept the multiplier near 1×. C posted less than half as much, but a 12M bag at 3× nearly matched. Both strategies work; doing both wins.

How ETH is paid

Once every linked bagworker has a share count, the tracker builds a Merkle tree of (wallet, shares) pairs and publishes the root to the vault along with the total shares. The vault takes all the ETH that has accumulated since the last epoch and assigns it to that root. From that moment your payout is fixed:

your ETH = epoch ETH × your shares ÷ total shares

Payouts are in Robinhood ETH — native ETH on Robinhood Chain, the same ETH you pay gas with. There's no reward token, no points, no "convert later". You open the bagwork desk, hit Claim, sign one transaction, and the vault sends ETH to your wallet. If you have several unclaimed epochs, Claim all settles them in one transaction.

The vault has no withdraw function. Not for the owner, not for the tracker, not for Pons. The only code path that moves ETH out is claim(), and it only works with a valid proof for a published root. If we disappeared tomorrow, every published epoch would still be claimable and the tax would keep collecting.

How we built it

The system is three pieces, and we kept each one as small as it could be so it can be read in an afternoon.

Two Solidity contracts on Robinhood Chain (an Arbitrum Orbit L2, fully EVM, gas in ETH), built on OpenZeppelin 5 and compiled with Solidity 0.8.24. The token contract handles the tax; the vault handles payouts. They're tested with a Hardhat suite that covers every tax path, the launch sequence, the treasury split, Merkle claims, double-claim protection, and the 30-day rollover. All thirteen tests pass before anything ships.

A tracker service in Node.js that does the off-chain work: links wallets to X accounts, pulls posts from the X API, runs the scoring above, builds the Merkle tree with OpenZeppelin's merkle-tree library (the same one the vault verifies against), and publishes the root. It stores every epoch's tree, proofs and post list in SQLite and serves them over a small REST API.

The site is a single static page — no framework, no build step — that talks to the chain directly through your wallet and to the tracker for proofs and stats. It works on Robinhood Chain out of the box and adds the network to your wallet if it's missing.

Design principle throughout: anything that moves money is on-chain and dumb; anything that needs judgement (which posts count, how they score) is off-chain, deterministic, and published in full so anyone can rerun it.

The 2% tax

Every buy and sell on the DEX pays 2%. Wallet-to-wallet transfers pay nothing. Inside the token contract the 2% is split down the middle:

ShareGoes toHow
1%PonsSent in ETH to the Pons wallet during each swapback. The address is published at launch; every payment is a normal transfer you can watch on Blockscout.
1%BagworkersSent in ETH to the BagworkVault during each swapback. Paid out every epoch by the rules on this page.

Mechanically, the contract holds tax as $FOLLOW until a sell happens and the pile is over 0.05% of supply. Then it swaps that pile to ETH on Uniswap in the same transaction and forwards it: half to Pons, half to the vault. Swapping only on sells keeps buys cheap; capping the swap size per transaction keeps any single sell from getting slammed.

Guard rails baked into the code: the tax can never be set above 10% per side, the Pons share can never be set above 50% of the tax, and the owner can't pull tax tokens out of the contract. There is no blacklist and no pause.

The vault

BagworkVault is about 200 lines. It holds ETH and a list of epochs. Each epoch is a Merkle root, a total share count, an ETH allocation, how much has been claimed, and an expiry.

The publisher — a hot wallet the tracker controls — is the only address that can call publishEpoch(root, totalShares, amount). Passing 0 for amount allocates everything currently unallocated. This is the publisher's entire power: it decides how the pool is split. It cannot direct ETH to any address that isn't in the tree, cannot allocate ETH that's already promised to an earlier epoch, and cannot touch ETH after it's allocated.

Anyone can call claim(epochId, shares, proof). The vault rebuilds the leaf as keccak256(keccak256(abi.encode(wallet, shares))) — the OpenZeppelin standard — checks the proof against the root, checks you haven't claimed, checks the epoch hasn't expired, and pays allocated × shares ÷ totalShares. claimMany does the same for several epochs at once. After 30 days anyone can call sweepExpired, which returns the unclaimed remainder to the pool.

The vault's owner can rotate the publisher key (if the tracker's key is ever compromised) and adjust the claim window between 7 and 90 days. That's the full list of owner powers. There is no function that sends ETH to the owner.

The tracker

The tracker is the piece that needs judgement, so it's the piece we publish the most about. Each epoch it:

Pulls posts for the window from X's recent search endpoint, paging through results and backing off when rate-limited. Filters to linked authors and posts that mention the coin. Scores each post with the formula above. Reads every linked wallet's $FOLLOW balance at the epoch's closing block. Drops anyone under 100k. Computes shares. Sorts, builds the tree, and calls publishEpoch. Then it writes the tree, every wallet's proof, and every scored post to its database and exposes them at /epoch/:id, /epoch/:id/tree, and /epoch/:id/posts.

Because scoring is deterministic and the inputs are published, anyone can take the post list for an epoch, run the formula, build the tree with the same library, and check that the root matches what's on-chain. If it ever didn't, you'd have proof.

If the tracker goes offline, nothing is lost: the tax keeps flowing into the vault, and the next epoch simply covers a longer window with a bigger pool.

Linking X

To be paid, the tracker has to know that the person posting from an X account controls a specific wallet. Linking proves both sides without you giving up anything.

From the bagwork desk you connect your wallet and sign a short message. Signing is free — it's not a transaction, it grants no permissions, and it can't move funds. It only proves you hold the key. The tracker then sends you to X's own sign-in (OAuth 2.0), which proves you control the X account. The tracker asks X for read-only access and never posts on your behalf.

One X account can back one wallet. If you link the same X account from a new wallet, the old link is replaced. Your balance is always read from the wallet currently linked.

Verify an epoch yourself

You don't have to trust this page. For any epoch:

npm i @openzeppelin/merkle-tree
curl https://api.followrh.xyz/epoch/6/tree > tree.json

node -e '
  const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
  const t = StandardMerkleTree.load(require("./tree.json"));
  console.log(t.root);                  // compare with epochs(6).root on the vault
'

Then open the vault on Blockscout, read epochs(6), and compare the root. To go one level deeper, pull /epoch/6/posts, rerun the scoring formula, rebuild the tree from scratch, and compare again.

Bots and sybils

Could someone link fifty wallets to fifty bot accounts? They could try. Each wallet needs 100k+ $FOLLOW at the snapshot, so fifty wallets means fifty bags. And bot posts nobody interacts with earn the 10-point floor and nothing else, while a single real post from a real account can earn 500. Interaction-weighting means fake volume is expensive and pays badly. It isn't perfect — it's priced.

Buying engagement is the other angle. The per-post cap of 500 limits how much any one inflated post can take, and views are heavily discounted. We watch the post lists, and the scoring weights can be tuned between epochs if a pattern shows up. Any change is announced on @FollowonRH before it applies and is visible in the tracker's published config.

FAQ

When do I get paid?

Epochs close every 24 hours. Your claim appears on the bagwork desk a few minutes after the root is published. Claim any time in the next 30 days.

Do I have to claim every day?

No. Claims stack. Come back once a week and hit Claim all.

What is Robinhood ETH?

Native ETH on Robinhood Chain. Same asset as mainnet ETH, bridged over the canonical Arbitrum bridge. It's what pays for gas on the chain and it's what the vault pays you.

Does posting in another language count?

Yes, as long as the post contains $FOLLOW, #FOLLOW, "follow coin" or the contract address.

Do replies count?

Replies to your own thread count fully. Replies to other people's posts count at half weight — they're real bagwork, but they're cheaper to spam.

Can the team change the rules?

Scoring weights live in the tracker and can be tuned; the contracts can't be. Tax can never exceed 10%, the Pons share can never exceed 50% of tax, and nobody can withdraw from the vault except through claims. Any scoring change is announced before it takes effect.

Should I disclose that I'm paid?

Yes. X's rules require it, and it's the honest thing. "I get paid in ETH for posting about $FOLLOW" is a fine sentence to keep in your bio.