AntiVamp

Developers

Ship identity protection in one afternoon.

Sandbox key in seconds. One enforceLaunch call before mint. Ed25519-signed decisions. Webhooks when protection changes. Fail closed — always.

Minutes, not weeks

Self-serve sandbox key → signed allow/block before lunch.

Ed25519 decisions

Public-key verify. 5-minute TTL. No shared decision secrets.

Fail closed

Outages and allow_authorized_only never silently allow.

Quickstart

Five minutes to a signed decision

curl
# 1. Create a sandbox key (instant, free — shown once)
curl -X POST https://sandbox-api.antivamp.io/api/v1/sandbox/keys \
  -H "Content-Type: application/json" \
  -d '{"organization":"Your Launchpad","email":"dev@yourlaunchpad.xyz"}'

# 2. Validate a launch against a seeded scenario
curl -X POST https://sandbox-api.antivamp.io/api/v1/launches/validate \
  -H "Authorization: Bearer av_sbx_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chain": "solana",
    "launchpad": "your-launchpad",
    "name": "Sandbox Fox",
    "ticker": "SFOX",
    "launcher": "0x00000000000000000000000000000000000000a1"
  }'
# -> decision: "allow" (authorized wallet)
# Swap the launcher for any other address -> decision: "block"
TypeScript
import { AntiVampClient, AntiVampFailClosedError } from "@antivamp_io/sdk";

const antivamp = new AntiVampClient({
  apiKey: process.env.ANTIVAMP_API_KEY,
  baseUrl: "https://sandbox-api.antivamp.io", // prod: https://api.antivamp.io
});

try {
  // enforceLaunch verifies Ed25519 + maps decisions exhaustively
  const result = await antivamp.enforceLaunch({
    chain: "solana",
    launchpad: "your-launchpad",
    name, ticker, launcher,
  });
  if (!result.enforceable) rejectLaunch(result.userMessage);
  else proceed(result.raw);
} catch (e) {
  if (e instanceof AntiVampFailClosedError) rejectLaunch("validation unavailable");
  else throw e;
}
Python
import os, requests

r = requests.post(
    "https://sandbox-api.antivamp.io/api/v1/launches/validate",
    headers={
        "Authorization": f"Bearer {os.environ['ANTIVAMP_API_KEY']}",
        "Idempotency-Key": launch_attempt_id,
    },
    json={
        "chain": "solana",
        "launchpad": "your-launchpad",
        "name": name, "ticker": ticker, "launcher": launcher,
        "nonce": launch_attempt_id,
    },
    timeout=8,
)
r.raise_for_status()  # fail closed on any error
decision = r.json()
assert decision.get("signature"), "unsigned decisions must be rejected"
assert decision["decision"] == "allow", decision["reason"]
curl — complete identity lifecycle (reserve → validate → launch → bond)
# Full identity lifecycle in the sandbox
KEY="av_sbx_YOUR_KEY"; API="https://sandbox-api.antivamp.io/api/v1"
AUTHW="0xAAA0000000000000000000000000000000000001"

# 1. Reserve a pair for 7 days (sandbox reservations are API-created;
#    production reservations happen on-chain)
curl -X POST $API/reservations -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"chain":"base","name":"Robin Lifecycle","ticker":"RLC","launcher":"'$AUTHW'","durationSeconds":604800}'

# 2. Authorized wallet -> "authorized" / "allow"
curl -X POST $API/launches/validate -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"chain":"base","launchpad":"you","name":"Robin Lifecycle","ticker":"RLC","launcher":"'$AUTHW'"}'

# 3. Copycat wallet -> "blocked" / "block" — even leet look-alikes
#    ("R0bin Lifecycle" / "rlc") fold to the same identity key
curl -X POST $API/launches/validate -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"chain":"base","launchpad":"you","name":"R0bin Lifecycle","ticker":"rlc","launcher":"0xBBB0000000000000000000000000000000000002"}'

# 4. Report the launch
curl -X POST $API/launches/report -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"chain":"base","launchpad":"you","name":"Robin Lifecycle","ticker":"RLC","launcher":"'$AUTHW'","tokenOrMint":"0xYourToken"}'

# 5. Report bonding -> 60-day reserved lock (never shortens existing locks)
curl -X POST $API/events/bonded -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"chain":"base","launchpad":"you","name":"Robin Lifecycle","ticker":"RLC","wasReserved":true}'

# 6. Watch the identity update everywhere — public, no key needed
curl "$API/identity/status?key=ROBINLIFECYCLE::RLC"

In-app reserve

Let creators reserve without leaving your launchpad

Two integration paths — both settle on-chain and keep your av_live_ key server-side.

Hosted Reserve widget

Drop-in iframe with your logo + accent color. Wallet connect, quote, sign, and confirmation — postMessage events back to your app.

antivamp.io/embed/reserve.js

Prepare API

Your backend calls prepare → creator signs the returned transaction → poll status. Full UX control, scope reservation:prepare.

POST /api/v1/reservations/prepare

Full guide: Launchpad Embed docs

Sandbox scenarios

Seeded identities for every state

Sandbox keys validate exclusively against these seeded scenarios — production data is never touched. Test authorized launches, blocked copycats, expiry, and guardian corrections.

Name :: TickerSeeded state
SANDBOXFOX :: SFOXActive reservation — launcher 0x…00a1 is authorized
MOONBADGER :: BADGERProtected after bonding (reserved, 60-day lock)
IRONWHALE :: IRONProtected after verified ~$1M milestone (90-day lock)
DUSKRAVEN :: DUSKExpired protection — available again
VAMPCOIN :: VAMPGraduated identity block — always blocked
CLEARSKY :: CLEARGuardian-cleared — available after mistaken block
NIGHTSHADE :: SHADEUnreserved bond — short 48-hour lock
anything elseAvailable

Signed responses

Verify every decision and webhook

Validation decisions are Ed25519-signed (asymmetric — verify with GET /v1/keys) and expire after 5 minutes so stale allows cannot be replayed. Webhooks use a separate per-endpoint HMAC secret.

Verify a decision (Ed25519 · SDK)
import { AntiVampClient } from "@antivamp_io/sdk";

const antivamp = new AntiVampClient({
  apiKey: process.env.ANTIVAMP_API_KEY,
  baseUrl: "https://api.antivamp.io",
});

// Preferred: one call that verifies signature + enforces exhaustively
const result = await antivamp.enforceLaunch({
  chain, launchpad, name, ticker, launcher,
});
if (!result.enforceable) rejectLaunch(result.userMessage);

// Or verify manually (exact keyId — never fall back to keys[0])
const decision = await antivamp.validateLaunch({ chain, launchpad, name, ticker, launcher });
const v = await antivamp.verifyValidationDecision(decision);
if (!v.valid) rejectLaunch(v.reason);
Verify a webhook (HMAC · TypeScript)
// Header: X-AntiVamp-Signature: t=1789544400,v1=<hex>
// Signature: HMAC-SHA256(secret, `${t}.${rawBody}`)
import { createHmac, timingSafeEqual } from "node:crypto";

function verifyWebhook(secret: string, rawBody: string, header: string) {
  const { t, v1 } = Object.fromEntries(
    header.split(",").map((kv) => kv.split("=")),
  );
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // 5 min tolerance
  const mac = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return timingSafeEqual(Buffer.from(mac, "hex"), Buffer.from(v1, "hex"));
}

Public verification keys: GET /api/v1/keys · install the SDK: npm install @antivamp_io/sdk · run conformance: npx antivamp-conformance

Normalization standard v1

One fold. Names and tickers. Every chain.

Both names and tickers pass through the same canonical fold, mirrored exactly by the on-chain SymbolLib, this site, the partner API, and the registry index. Version 1 rules:

  • Uppercase ASCII letters; lowercase folds up.
  • Leet digits fold to look-alike letters: 0→O 1→I 3→E 4→A 5→S 7→T 8→B. Other digits (2, 6, 9) are kept.
  • $→S and @→A.
  • Whitespace, punctuation, and emoji are dropped.
  • All non-ASCII is dropped — full-width unicode, zero-width characters, and homoglyph scripts cannot smuggle a look-alike identity past the fold. Inputs that fold to empty are rejected on-chain (BadLength).
  • Identity key = NAME::TICKER after folding; on-chain keys are keccak256 of each folded string.
  • Length limits are enforced on-chain per registry config; a future v2 fold would ship as a new registry version with documented migration, never a silent change.

Official test vectors (version 2) are published in the repository and exercised by the unit test suite.

InputCanonicalCategory
Green RobinGREENROBINfrozen_robin
GREEN ROBINGREENROBINfrozen_robin
Green RobinGREENROBINfrozen_robin
$ROBINSROBINfrozen_robin
robinROBINfrozen_robin
ROBINROBINfrozen_robin
dogeDOGEcase
DoGeDOGEcase
Green Robin GREENROBINwhitespace
Green RobinGREENROBINwhitespace
$DOGESDOGEdollar_sign
@DOGEADOGEat_sign

Operational notes

Limits and versioning

Rate limits

Sandbox keys: 60 requests/minute. Production keys: 300/minute by default, raised per partner up to 2000/minute on request. Fixed windows aligned to the clock minute. Retry-After on every rate-limit 429, and RateLimit-Remaining on successful /launches/validate and /identity/check responses, so you can pace yourself rather than find the ceiling by hitting it.

Idempotency

Send Idempotency-Key (or a request nonce) on validation calls — retries replay the stored response with Idempotency-Replayed: true.

Versioning

Responses carry responseVersion. Breaking changes ship as a new version with a migration window — see the changelog.

Machine-readable spec: GET /api/v1/openapi · TypeScript SDK: npm install @antivamp_io/sdk · Hosts: api.antivamp.io / sandbox-api.antivamp.io

Integration help: support@antivamp.io · Partnership and production access: partners@antivamp.io