mirror of
https://github.com/hanzoai/bootnode.git
synced 2026-10-25 02:31:30 +00:00
Hanzo CI/CD / cicd (push) Failing after 44m41s
CI/CD / cicd (push) Failing after 44m42s
Claude-Session: https://claude.ai/code/session_01WfkhhyQsijWm2ZAW144SVo
435 lines
17 KiB
TypeScript
435 lines
17 KiB
TypeScript
"use client"
|
|
|
|
import Link from "next/link"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Navbar } from "@/components/navbar"
|
|
import { Footer } from "@/components/footer"
|
|
import {
|
|
Activity,
|
|
ArrowRight,
|
|
Bell,
|
|
Blocks,
|
|
CheckCircle2,
|
|
Clock,
|
|
Filter,
|
|
Key,
|
|
MailCheck,
|
|
RefreshCw,
|
|
Shield,
|
|
TestTube,
|
|
Webhook,
|
|
Zap,
|
|
} from "@luxfi/ui/icons"
|
|
import { getBrand } from "@/lib/brand"
|
|
import { docsConfig } from "@/lib/docs-config"
|
|
|
|
export default function WebhooksProductPage() {
|
|
const brand = getBrand()
|
|
|
|
return (
|
|
<div className="flex min-h-screen flex-col">
|
|
<Navbar />
|
|
|
|
{/* Hero */}
|
|
<section className="relative overflow-hidden border-b bg-gradient-to-b from-background to-muted/20">
|
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#4f4f4f2e_1px,transparent_1px),linear-gradient(to_bottom,#4f4f4f2e_1px,transparent_1px)] bg-[size:14px_24px] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,#000_70%,transparent_100%)]" />
|
|
<div className="container relative py-24 md:py-32 lg:py-40">
|
|
<div className="mx-auto flex max-w-4xl flex-col items-center text-center">
|
|
<Badge variant="secondary" className="mb-4">
|
|
<Webhook className="mr-1 h-3 w-3" />
|
|
Real-Time Webhooks
|
|
</Badge>
|
|
<h1 className="text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl lg:text-7xl">
|
|
Never miss an{" "}
|
|
<span className="bg-gradient-to-r from-pink-500 to-rose-500 bg-clip-text text-transparent">
|
|
onchain event
|
|
</span>
|
|
</h1>
|
|
<p className="mt-6 max-w-2xl text-lg text-muted-foreground md:text-xl">
|
|
Get instant HTTP POST notifications for blockchain events. Address
|
|
activity, token transfers, NFT mints, new blocks, and more.
|
|
HMAC-signed, retried automatically, and delivered in milliseconds.
|
|
</p>
|
|
<div className="mt-10 flex flex-col gap-4 sm:flex-row">
|
|
<Button size="xl" asChild>
|
|
<Link href="/dashboard/webhooks">
|
|
Create a Webhook
|
|
<ArrowRight className="ml-2 h-4 w-4" />
|
|
</Link>
|
|
</Button>
|
|
<Button size="xl" variant="outline" asChild>
|
|
<Link href="/docs/webhooks/quickstart">Read the Docs</Link>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Event Types */}
|
|
<section className="border-b py-24">
|
|
<div className="container">
|
|
<div className="mx-auto max-w-2xl text-center">
|
|
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
|
|
Seven event types for every use case
|
|
</h2>
|
|
<p className="mt-4 text-lg text-muted-foreground">
|
|
Subscribe to exactly the events your application needs. Each
|
|
webhook can monitor one or more event types with custom filters.
|
|
</p>
|
|
</div>
|
|
<div className="mt-16 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
<EventTypeCard
|
|
name="ADDRESS_ACTIVITY"
|
|
description="Fires when a monitored address sends or receives any transaction, including native transfers, ERC-20 transfers, and contract interactions."
|
|
useCases="Wallet notifications, activity feeds, compliance monitoring"
|
|
/>
|
|
<EventTypeCard
|
|
name="MINED_TRANSACTION"
|
|
description="Fires when a specific transaction hash is included in a block. Use this to track transactions you have submitted and notify users of confirmation."
|
|
useCases="Transaction confirmation, payment processing"
|
|
/>
|
|
<EventTypeCard
|
|
name="NFT_ACTIVITY"
|
|
description="Fires on ERC-721 and ERC-1155 transfer events. Covers mints, transfers, and burns. Includes full token metadata in the payload."
|
|
useCases="NFT marketplace alerts, collection tracking, mint monitoring"
|
|
/>
|
|
<EventTypeCard
|
|
name="TOKEN_TRANSFER"
|
|
description="Fires on ERC-20 token transfer events. Filter by token address, sender, receiver, or minimum value to reduce noise."
|
|
useCases="DeFi monitoring, whale alerts, treasury tracking"
|
|
/>
|
|
<EventTypeCard
|
|
name="INTERNAL_TRANSFER"
|
|
description="Fires on internal (trace-level) ETH transfers that do not appear in standard transaction logs. Captures contract-to-contract value movement."
|
|
useCases="MEV detection, protocol revenue tracking, audit trails"
|
|
/>
|
|
<EventTypeCard
|
|
name="NEW_BLOCK"
|
|
description="Fires when a new block is mined on the monitored chain. Payload includes block number, hash, timestamp, gas used, and transaction count."
|
|
useCases="Block explorers, chain monitoring, sequencer health checks"
|
|
/>
|
|
<EventTypeCard
|
|
name="PENDING_TRANSACTION"
|
|
description="Fires when a transaction enters the mempool for monitored addresses. Get ahead of block confirmation for real-time UX updates."
|
|
useCases="Pending state UI, front-running detection, gas estimation"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Features */}
|
|
<section className="border-b bg-muted/30 py-24">
|
|
<div className="container">
|
|
<div className="mx-auto max-w-2xl text-center">
|
|
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
|
|
Built for production reliability
|
|
</h2>
|
|
<p className="mt-4 text-lg text-muted-foreground">
|
|
Webhooks are critical infrastructure. {brand.name} delivers them with
|
|
the reliability and security your application demands.
|
|
</p>
|
|
</div>
|
|
<div className="mt-16 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
<FeatureCard
|
|
icon={<Shield className="h-6 w-6" />}
|
|
title="HMAC-SHA256 Signing"
|
|
description={`Every webhook payload is signed with your secret key using HMAC-SHA256. Verify the X-${brand.name.replace(/\s+/g, "-")}-Signature header to confirm authenticity and prevent spoofing.`}
|
|
/>
|
|
<FeatureCard
|
|
icon={<RefreshCw className="h-6 w-6" />}
|
|
title="Automatic Retries"
|
|
description="Failed deliveries are retried with exponential backoff: 1s, 5s, 30s, 5m, 30m, 2h, 8h. Up to 7 retry attempts over 10 hours before the event is marked as failed."
|
|
/>
|
|
<FeatureCard
|
|
icon={<Filter className="h-6 w-6" />}
|
|
title="Custom Filters"
|
|
description="Narrow down events with filters on address, token contract, minimum value, function selector, or topic hash. Receive only the events that matter to your app."
|
|
/>
|
|
<FeatureCard
|
|
icon={<TestTube className="h-6 w-6" />}
|
|
title="Test Endpoint"
|
|
description="Send a test event to your webhook URL from the dashboard. Verify your handler processes the payload correctly before going live with real onchain events."
|
|
/>
|
|
<FeatureCard
|
|
icon={<Clock className="h-6 w-6" />}
|
|
title="Delivery Logs"
|
|
description="Full delivery history with request/response details, latency, and status codes. Debug failed deliveries and monitor endpoint health from the dashboard."
|
|
/>
|
|
<FeatureCard
|
|
icon={<Activity className="h-6 w-6" />}
|
|
title="Real-Time Dashboard"
|
|
description="Monitor delivery rates, success/failure metrics, and event volume in real time. Set up alerts for delivery failures or endpoint downtime."
|
|
/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Code Example */}
|
|
<section className="border-b py-24">
|
|
<div className="container">
|
|
<div className="grid grid-cols-1 gap-12 lg:grid-cols-2 lg:gap-16">
|
|
<div className="flex flex-col justify-center">
|
|
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
|
|
Simple to set up, simple to verify
|
|
</h2>
|
|
<p className="mt-4 text-lg text-muted-foreground">
|
|
Create a webhook from the dashboard or API. When an event fires,
|
|
{brand.name} sends a JSON POST to your URL. Verify the HMAC
|
|
signature and process the event.
|
|
</p>
|
|
<ul className="mt-8 space-y-3">
|
|
{[
|
|
"JSON payloads with consistent schema across all event types",
|
|
"Idempotency key in every payload for safe deduplication",
|
|
"Timestamps in ISO 8601 with block-level precision",
|
|
"Chain ID and network name included in every event",
|
|
"Webhook management via REST API or dashboard UI",
|
|
].map((item) => (
|
|
<li key={item} className="flex items-start gap-3 text-sm">
|
|
<CheckCircle2 className="mt-0.5 h-4 w-4 shrink-0 text-green-500" />
|
|
<span>{item}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
<div className="flex flex-col gap-4">
|
|
<CodeBlock
|
|
title="Webhook Payload"
|
|
brandName={brand.name}
|
|
/>
|
|
<CodeBlock2
|
|
title="Verify Signature (Node.js)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* How It Works */}
|
|
<section className="border-b bg-muted/30 py-24">
|
|
<div className="container">
|
|
<div className="mx-auto max-w-2xl text-center">
|
|
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">
|
|
How webhooks work
|
|
</h2>
|
|
</div>
|
|
<div className="mt-16 grid gap-6 md:grid-cols-4">
|
|
<StepCard
|
|
step="1"
|
|
title="Create"
|
|
description="Define your webhook in the dashboard or via API. Choose event types, add address or token filters, and provide your endpoint URL."
|
|
/>
|
|
<StepCard
|
|
step="2"
|
|
title="Listen"
|
|
description={`${brand.name} monitors the blockchain in real time. When an event matches your filters, a signed JSON payload is queued for delivery.`}
|
|
/>
|
|
<StepCard
|
|
step="3"
|
|
title="Deliver"
|
|
description="The payload is POST-ed to your endpoint with HMAC signature headers. Your server responds with 2xx to acknowledge receipt."
|
|
/>
|
|
<StepCard
|
|
step="4"
|
|
title="Retry"
|
|
description={`If delivery fails, ${brand.name} retries with exponential backoff up to 7 times. Failed events are visible in your delivery log dashboard.`}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* CTA */}
|
|
<section className="py-24">
|
|
<div className="container">
|
|
<div className="relative overflow-hidden rounded-3xl bg-gradient-to-r from-pink-600 to-rose-600 px-6 py-20 sm:px-12 sm:py-28">
|
|
<div className="absolute inset-0 bg-[linear-gradient(to_right,#ffffff1a_1px,transparent_1px),linear-gradient(to_bottom,#ffffff1a_1px,transparent_1px)] bg-[size:14px_24px]" />
|
|
<div className="relative mx-auto max-w-2xl text-center">
|
|
<h2 className="text-3xl font-bold tracking-tight text-white sm:text-4xl">
|
|
Stop polling. Start streaming.
|
|
</h2>
|
|
<p className="mt-4 text-lg text-white/80">
|
|
Set up your first webhook in under a minute. Free tier includes
|
|
1,000 webhook deliveries per day across all event types.
|
|
</p>
|
|
<div className="mt-10 flex flex-col justify-center gap-4 sm:flex-row">
|
|
<Button
|
|
size="xl"
|
|
className="bg-white text-pink-600 hover:bg-white/90"
|
|
asChild
|
|
>
|
|
<Link href="/dashboard/webhooks">
|
|
Create Your First Webhook
|
|
<ArrowRight className="ml-2 h-4 w-4" />
|
|
</Link>
|
|
</Button>
|
|
<Button
|
|
size="xl"
|
|
variant="outline"
|
|
className="border-white/30 bg-transparent text-white hover:bg-white/10"
|
|
asChild
|
|
>
|
|
<Link href="/docs/webhooks/quickstart">View Full Docs</Link>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<Footer />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function EventTypeCard({
|
|
name,
|
|
description,
|
|
useCases,
|
|
}: {
|
|
name: string
|
|
description: string
|
|
useCases: string
|
|
}) {
|
|
return (
|
|
<Card className="h-full">
|
|
<CardHeader>
|
|
<Badge variant="outline" className="w-fit font-mono text-xs">
|
|
{name}
|
|
</Badge>
|
|
<CardTitle className="mt-2 text-base">{name.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).replace(/\bNft\b/, "NFT")}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
<p className="text-sm text-muted-foreground">{description}</p>
|
|
<div>
|
|
<span className="text-xs font-medium text-muted-foreground">
|
|
Use cases:{" "}
|
|
</span>
|
|
<span className="text-xs text-muted-foreground">{useCases}</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
function FeatureCard({
|
|
icon,
|
|
title,
|
|
description,
|
|
}: {
|
|
icon: React.ReactNode
|
|
title: string
|
|
description: string
|
|
}) {
|
|
return (
|
|
<Card className="h-full">
|
|
<CardHeader>
|
|
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
|
{icon}
|
|
</div>
|
|
<CardTitle className="mt-4">{title}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-sm text-muted-foreground">{description}</p>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
function StepCard({
|
|
step,
|
|
title,
|
|
description,
|
|
}: {
|
|
step: string
|
|
title: string
|
|
description: string
|
|
}) {
|
|
return (
|
|
<Card className="h-full">
|
|
<CardHeader>
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary text-sm font-bold text-primary-foreground">
|
|
{step}
|
|
</div>
|
|
<CardTitle className="mt-4">{title}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-sm text-muted-foreground">{description}</p>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
function CodeBlock({ title, brandName }: { title: string; brandName: string }) {
|
|
const headerName = `X-${brandName.replace(/\s+/g, "-")}-Signature`
|
|
const code = `POST /your-endpoint HTTP/1.1
|
|
Content-Type: application/json
|
|
${headerName}: sha256=a1b2c3d4e5...
|
|
X-Webhook-Event: TOKEN_TRANSFER
|
|
X-Webhook-Idempotency-Key: evt_01HZ3K...
|
|
|
|
{
|
|
"id": "evt_01HZ3KQWERTY...",
|
|
"type": "TOKEN_TRANSFER",
|
|
"createdAt": "2025-01-15T14:23:01.456Z",
|
|
"chain": "ethereum",
|
|
"chainId": 1,
|
|
"data": {
|
|
"blockNumber": 19234567,
|
|
"transactionHash": "0x3a1b...",
|
|
"logIndex": 42,
|
|
"from": "0xd8dA6BF2...",
|
|
"to": "0x7a250d56...",
|
|
"token": {
|
|
"address": "0xA0b8...ec7",
|
|
"symbol": "USDC",
|
|
"decimals": 6
|
|
},
|
|
"value": "50000000000",
|
|
"valueFormatted": "50000.0"
|
|
}
|
|
}`
|
|
return (
|
|
<div className="overflow-hidden rounded-xl border bg-card">
|
|
<div className="flex items-center border-b px-4 py-2">
|
|
<span className="text-xs font-medium text-muted-foreground">
|
|
{title}
|
|
</span>
|
|
</div>
|
|
<pre className="overflow-x-auto p-4 text-sm leading-relaxed">
|
|
<code>{code}</code>
|
|
</pre>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function CodeBlock2({ title }: { title: string }) {
|
|
const code = `import crypto from "node:crypto";
|
|
|
|
function verifyWebhook(payload, signature, secret) {
|
|
const expected = crypto
|
|
.createHmac("sha256", secret)
|
|
.update(payload)
|
|
.digest("hex");
|
|
|
|
const sig = signature.replace("sha256=", "");
|
|
return crypto.timingSafeEqual(
|
|
Buffer.from(sig, "hex"),
|
|
Buffer.from(expected, "hex")
|
|
);
|
|
}`
|
|
return (
|
|
<div className="overflow-hidden rounded-xl border bg-card">
|
|
<div className="flex items-center border-b px-4 py-2">
|
|
<span className="text-xs font-medium text-muted-foreground">
|
|
{title}
|
|
</span>
|
|
</div>
|
|
<pre className="overflow-x-auto p-4 text-sm leading-relaxed">
|
|
<code>{code}</code>
|
|
</pre>
|
|
</div>
|
|
)
|
|
}
|