@dailydrop/shield
v1.4.0
Published
Proof of Presence verification SDK — verify real humans by their on-chain streak
Maintainers
Readme
@dailydrop/shield
Proof of Presence — verify real humans by their on-chain streak.
What's new in v1.4.0
- Fix: on-chain mode (
onChain: true) no longer treats an RPC failure as a zero streak. If both the Celo and Base reads fail (RPC down, bad network, timeout),verify()now throws instead of silently returningpassed: false— a sybil filter that swallowed infra errors as "not human" was a correctness bug, not just a nicety. If only one chain fails, you still get a real (partial) result, witherrorset to explain what's missing. - Fix: a custom
celoRpc/baseRpcpointed at the wrong network (wrong chain ID) used to fail silently and return incorrect data. It's now checked once per instance and throws a clear error if the RPC doesn't match the expected chain. - Added
LICENSEfile andengines.node >= 18topackage.json(the SDK already required nativefetch/AbortSignal.timeout, which need Node 18+; this is now declared instead of failing unexpectedly on older runtimes).
What's new in v1.3.0
- Security fix (server-side, no SDK code change needed):
/api/relayer— the endpoint that propagates check-ins across chains — previously accepted any successful transaction hash as proof of a check-in without verifying it was sent by the claimed user to the realDailyDropcontract. This let anyone fabricate cross-chain streaks. It's now fixed to validate the transaction sender, recipient, and emitted event before accepting it. This directly affects the accuracy of the streak data this SDK reads, so an upgrade is recommended even though the client API is unchanged. getUserDataABI updated to match the latestDailyDrop.sol(totalCheckInsremoved from contract storage — it's now derived off-chain from confirmed transactions)- Behavior change: in
onChain: truemode,checkins.totalis always0(no contract storage to read it from). Use the default API mode for an accurate count, or read it yourself from/api/verify.
What's new in v1.2.0
- Fix: removed broken
"import": "dist/index.mjs"from exports (file never existed — ESM consumers failed silently) - New exports:
STREAK_MASTER_ADDRESS(Base) andSTREAK_NFT_ADDRESS(Base) — deployed contract addresses - All 5 contract addresses now exported as named constants
What was in v1.1.0
verifyBatch()— verify multiple addresses in parallelgetProfile()— alias forverify(address, 1)with full data- Automatic retry on network errors
- Configurable timeout (
timeoutoption) - Address validation before any network call
Why?
Airdrops get sybil-attacked. DAOs get fake votes. NFT mints get botted.
A streak on-chain can't be faked retroactively. If a wallet showed up every day for 7+ days, it's a real human.
DailyDrop Shield gives any project a one-line sybil check powered by behavioral proof-of-humanity.
Install
npm install @dailydrop/shieldQuick Start
import DailyDropShield from "@dailydrop/shield"
const shield = new DailyDropShield()
// Verify a wallet has 7+ day streak
const result = await shield.verify("0xABC...", 7)
if (result.passed) {
console.log("✅ Real human — allow airdrop")
} else {
console.log("❌ Streak too low — reject")
}
// Quick boolean check
const isHuman = await shield.isHuman("0xABC...", 7)
// Get streak number
const streak = await shield.getStreak("0xABC...")
// Get badge level (0=none, 1=weekly, 2=monthly, 3=century)
const badge = await shield.getBadge("0xABC...")
// Verify multiple wallets at once (airdrop filtering)
const results = await shield.verifyBatch([
"0xABC...",
"0xDEF...",
"0x123...",
], 7)
const eligible = Object.entries(results)
.filter(([, r]) => r.passed)
.map(([addr]) => addr)
console.log(`${eligible.length} eligible wallets`)Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiUrl | string | https://dailydrop-five.vercel.app | Custom API endpoint |
| onChain | boolean | false | Read directly from blockchain |
| celoRpc | string | https://forno.celo.org | Custom Celo RPC |
| baseRpc | string | https://mainnet.base.org | Custom Base RPC |
| timeout | number | 10000 | Fetch timeout in ms |
Response
{
address: "0xABC...",
passed: true, // meets minStreak requirement
minStreak: 7,
streak: {
current: 12, // highest streak across all chains
bestChain: "celo",
celo: 12,
base: 0,
},
badges: {
level: 1, // 0=none 1=weekly 2=monthly 3=century
label: "🥉 Weekly",
weekly: true, // 7+ days
monthly: false, // 30+ days
century: false, // 100+ days
},
checkins: {
total: 36,
canCheckIn: false,
canClaim: false,
nextCheckIn: 1780613801,
},
shield: "0x24eFf9bdE979D6dccC869178F353D663bC8A6983",
meta: {
verifiedAt: "2026-06-04T08:11:01.983Z",
celoscan: "https://celoscan.io/address/0xABC...",
basescan: "https://basescan.org/address/0xABC...",
}
}Error handling
try {
const result = await shield.verify("0xABC...", 7)
if (result.error) {
// onChain mode only: one of the two chains failed to read, but the other
// succeeded — result is a real (partial) answer, just less complete than usual
}
} catch (err) {
if (err.message.includes("invalid Ethereum address")) {
// Bad address format
} else if (err.message.includes("chainId")) {
// onChain mode: your celoRpc/baseRpc option points at the wrong network
} else if (err.message.includes("both Celo and Base")) {
// onChain mode: total RPC failure — don't treat this as "not human",
// you genuinely don't know yet
} else if (err.message.includes("timeout")) {
// Network timeout — retry or use onChain: true
} else {
// API error
}
}Use Cases
| Use Case | minStreak | |----------|-----------| | Airdrop sybil filter | 7 | | DAO voting weight | 30 | | NFT allowlist | 7 | | DeFi APY boost | 14 | | Exclusive access | 100 |
On-Chain Mode
For maximum trust, verify directly on-chain without the API:
const shield = new DailyDropShield({ onChain: true })
const result = await shield.verify("0xABC...", 7)Note:
checkins.totalis always0in on-chain mode — total check-in count isn't stored on-chain (gas optimization) and is only derived in the default API mode. Streak, badges,canCheckIn,canClaim, andnextCheckInare unaffected.
REST API
Free public API — no auth required:
GET https://dailydrop-five.vercel.app/api/verify?address=0xABC&minStreak=7Contracts
| Contract | Network | Address |
|----------|---------|---------|
| DailyDrop | Celo Mainnet | 0xd8Cc2a639a8D4e7A75a5B41C28606712e4fDf70b |
| DailyDrop | Base Mainnet | 0x974fB504172f2aABbecc698Ebf137202a5E4e495 |
| DailyDropShield | Celo Mainnet | 0x24eFf9bdE979D6dccC869178F353D663bC8A6983 |
| StreakMaster | Base Mainnet | 0x038F496eCf99ecA5959A40493C96670Ea8a14345 |
| StreakNFT | Base Mainnet | 0xbBa5865b3E3A5033730f851d555cc922B74B25Fa |
All 5 addresses are exported as named constants:
import {
CELO_DAILYDROP,
BASE_DAILYDROP,
SHIELD_ADDRESS,
STREAK_MASTER_ADDRESS,
STREAK_NFT_ADDRESS,
} from "@dailydrop/shield"License
MIT © 2026 @wkalidev
Built on Celo & Base · Behavioral Proof of Humanity
