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.
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.
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
});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.
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 }),
});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.
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.
{
"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.
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
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.
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.