Skip to content
Guide · Web3

Sock puppets & airdrop farming

One person can spin up a thousand wallets to farm an airdrop meant for a thousand people — a sock-puppet attack. Anchor eligibility to the device, not the wallet: the security chip in a real machine costs real money to obtain while a fresh wallet costs nothing, so one human with one machine can't farm a thousand allocations. Wallet-graph analytics catch the lazy farms; this catches the ones that rotate fresh wallets behind cloud VMs and proxies.

1

Mint a challenge on your server

When a wallet begins a claim or a vote, your backend mints a single-use challenge with your rh_sk_ secret and relays the nonce to the dApp. The nonce is the anti-replay floor: the client has to quote a live TPM over it.

server — POST /claim/challengets
import { RootHerald } from "@rootherald/node";

const rh = new RootHerald({ secretKey: process.env.RH_SECRET_KEY! }); // rh_sk_… (backend only)

app.post("/claim/challenge", async (_req, res) => {
  const { challengeId, nonce } = await rh.issueChallenge();
  res.json({ challengeId, nonce }); // relay to the dApp
});
2

Collect evidence on the client

The dApp quotes the local TPM over the nonce and posts the opaque evidence blob back with the wallet address. The collector holds no key and never contacts Root Herald: it only talks to your server.

dApp — keyless collectorts
import { attest } from "@rootherald/browser";

const { challengeId, nonce } = await fetch("/claim/challenge", { method: "POST" }).then((r) => r.json());
const evidence = await attest(nonce); // quotes the TPM over the nonce; no keys on the client

await fetch("/claim", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ challengeId, evidence, wallet }),
});
3

Verify server→server and bind the device to the wallet

Your backend appraises the evidence with rh.verify() and gets the verdict back directly. On a pass you get a stable per-tenant device.ueid — the pseudonym you dedupe on. Bind (device.ueid → wallet) and enforce the rule that matches your distribution: one allocation per device, weight by device-distinct wallets, or flag wallets that share a device.

server — POST /claimts
app.post("/claim", async (req, res) => {
  const { challengeId, evidence, wallet } = req.body;

  const verdict = await rh.verify(evidence, {
    challengeId,
    policy: "rootherald:builtin:strict-hardware",
  });
  if (verdict.device.verdict !== "pass") {
    return res.status(403).json({ error: "device_check_failed" });
  }

  // One allocation per device. (Or: store every (ueid, wallet) pair and weight at snapshot.)
  const existing = await db.allocations.findByDevice(verdict.device.ueid);
  if (existing && existing.wallet !== wallet) {
    return res.status(409).json({ error: "device_already_claimed", with: existing.wallet });
  }
  await db.allocations.upsert({
    deviceId: verdict.device.ueid,
    wallet,
    attestationType: verdict.device.attestationType,
  });
  res.json({ ok: true });
});

What comes back

rh.verify() returns the verdict synchronously — no token, no JWKS, no offline step. You branch on device.verdict and key everything off device.ueid.

AttestationVerdict (trimmed)json
{
  "acr": "urn:rootherald:device:high",
  "amr": ["hwk"],
  "device": {
    "ueid": "2f9c4a…",            // stable per-tenant device id — dedupe on this
    "verdict": "pass",            // pass | warn | fail
    "earStatus": "affirming",     // affirming | warning | contraindicated
    "attestationType": "tpm20"
  }
}

Keep the strict-hardware policy — it rejects the cloud

A farmer's cheapest move is a fleet of cloud VMs, and a cloud vTPM (AWS NitroTPM, Azure, GCP) is an identity they mint for pennies. The default rootherald:builtin:strict-hardware rejects every cloud-vtpm and emulated class outright, so a NitroTPM-backed claim fails. This is the one use case where you almost never want to loosen it.

Heads up
Crypto-native users expect a thin attest client, but state the privacy posture plainly: Root Herald sees only a hardware-derived pseudonym, never the wallet, never PII, and the pseudonym is uncorrelatable across tenants. That framing keeps you out of the political territory that sank Web Environment Integrity.

Beat pre-snapshot farming: bind continuously

Bind devices on connect and on any reward-relevant action, not just at the snapshot; store the device set over time. A wallet that only ever appears alongside the same device as 200 other wallets is the signal you want, and it's visible even when each wallet's on-chain history looks clean.

The economics you are changing

A fake built on a rented cloud server costs about $0.10. A real-device fake costs $30–$200. When a single airdrop allocation can be worth six or seven figures, even the expensive fakes can pay off — so hardware attestation is not a win condition on its own. What it does is clear out the cheap flood and put a real price on every fake that remains.

Airdrop & giveaway farming

MYX $1.7M/wallet · LayerZero ZRO ~$15K peak · typical L2 $500–$5K

Partial
$30 floor

Per-identity yield

$500 – $1,700,000

Rational ceiling

$500 typical, $1M+ at top events

Partial mitigation only. Forces farms physical + detectable; doesn't solve $1M-per-wallet airdrops alone.

The default rootherald:builtin:strict-hardware policy turns away every fake built on a cloud server or software emulator. When LayerZero threw out 803,000 wallets, most were exactly these cloud-server clusters — the kind that can never pass a real-hardware check.

  • Rented cloud servers — a cloud VM dressed up as a PC can't prove there's a real chip behind it. AWS, Azure and GCP instances all fail the check.
  • Software emulators — a “chip” that is really software in a container can't prove it is a physical part. It even labels itself as emulated.
  • One chip, many claims — route a thousand fake sign-ins through one real machine and they collapse to a single ueid: one computer, counted once.

The honest residual

Two attacks still get through strict rules when a single claim is worth enough:

Refurbished-device farm. At $30–$200 a machine, a determined farmer can buy 100–1,000 before shipping and setup eat the profit. But they leave a trail: the same make, model and firmware appearing across dozens of “different users” on one network is a cluster your own analytics can catch, using the anonymous device id we return.

Paid real people. Pay 1,000 genuine people $5 each to claim. Every device is real and every claim is valid. Cryptography cannot tell these apart — behavioural signals, identity checks and wallet-graph analysis have to carry that one.

Where this sits in your stack

Root Herald adds a hard-to-fake hardware signal at the moment someone claims — before there is any on-chain activity to analyse. Tie each device to the wallet it claims from, keep every device-to-wallet pair over the claim window, and surface the clusters where one device claims through many wallets, or many devices share a network and hardware profile.