diff --git a/app/[locale]/(home)/_pages/page.en.tsx b/app/[locale]/(home)/_pages/page.en.tsx index 8255e291a..4e9569ec5 100644 --- a/app/[locale]/(home)/_pages/page.en.tsx +++ b/app/[locale]/(home)/_pages/page.en.tsx @@ -57,6 +57,7 @@ export default function HomePage() { } + badge="beta" href="/tools/chainhook" title="Chainhook" description="Create custom event streams and triggers for real-time blockchain data processing." diff --git a/app/[locale]/(home)/tools/_pages/page.en.tsx b/app/[locale]/(home)/tools/_pages/page.en.tsx index 98bdf9542..01db002dd 100644 --- a/app/[locale]/(home)/tools/_pages/page.en.tsx +++ b/app/[locale]/(home)/tools/_pages/page.en.tsx @@ -16,12 +16,15 @@ export default function ToolsPage() { href="/tools/chainhook" title="Chainhook" icon={} + badge="beta" + tag="Stacks" description="Create custom event streams and triggers for real-time blockchain data processing." /> } + tag="Stacks" description="Monitor and track smart contract activity and performance metrics." /> {page.data.interactive ? ( @@ -138,14 +140,20 @@ export default async function Page(props: { components={getMDXComponents({ // Custom overrides that need special handling API: (props) => , - APIPage: (props) => ( - - ), + APIPage: (props) => { + const config = props.document + ? getAPIConfig(String(props.document)) + : undefined; + + const mergedProps = { + ...(config ?? {}), + ...props, + playgroundOptions: + props.playgroundOptions ?? config?.playgroundOptions, + }; + + return ; + }, h1: ({ children, ...props }: HeadingProps) => { const H1 = defaultMdxComponents.h1 as React.ComponentType; const id = typeof children === 'string' ? children : undefined; @@ -238,14 +246,20 @@ export default async function Page(props: { components={getMDXComponents({ // Custom overrides that need special handling API: (props) => , - APIPage: (props) => ( - - ), + APIPage: (props) => { + const config = props.document + ? getAPIConfig(String(props.document)) + : undefined; + + const mergedProps = { + ...(config ?? {}), + ...props, + playgroundOptions: + props.playgroundOptions ?? config?.playgroundOptions, + }; + + return ; + }, h1: ({ children, ...props }: HeadingProps) => { const H1 = defaultMdxComponents.h1 as React.ComponentType; const id = typeof children === 'string' ? children : undefined; diff --git a/app/api/proxy/route.ts b/app/api/proxy/route.ts new file mode 100644 index 000000000..bb5b58857 --- /dev/null +++ b/app/api/proxy/route.ts @@ -0,0 +1,86 @@ +import { NextResponse } from 'next/server'; + +const ALLOWED_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']); +const ALLOWED_HOSTNAMES = new Set(['api.hiro.so', 'api.mainnet.hiro.so', 'api.testnet.hiro.so']); +const BLOCKED_REQUEST_HEADERS = new Set(['host', 'cookie', 'connection', 'content-length']); +const STRIPPED_RESPONSE_HEADERS = new Set(['set-cookie', 'server', 'via', 'www-authenticate']); + +export const dynamic = 'force-dynamic'; + +export async function POST(request: Request) { + try { + const { url, method = 'GET', headers = {}, body } = await request.json(); + + if (!url || typeof url !== 'string') { + return NextResponse.json({ error: 'A target URL is required.' }, { status: 400 }); + } + + let parsedUrl: URL; + try { + parsedUrl = new URL(url); + } catch { + return NextResponse.json({ error: 'URL must be absolute.' }, { status: 400 }); + } + + if (parsedUrl.protocol !== 'https:' || !ALLOWED_HOSTNAMES.has(parsedUrl.hostname)) { + return NextResponse.json( + { error: 'This proxy only allows Hiro API hosts over HTTPS.' }, + { status: 403 }, + ); + } + + const upperMethod = String(method).toUpperCase(); + if (!ALLOWED_METHODS.has(upperMethod)) { + return NextResponse.json( + { error: `Method ${upperMethod} is not supported by the proxy.` }, + { status: 405 }, + ); + } + + const upstreamHeaders = new Headers(); + if (headers && typeof headers === 'object') { + for (const [key, value] of Object.entries(headers)) { + if (typeof value === 'string' && !BLOCKED_REQUEST_HEADERS.has(key.toLowerCase())) { + upstreamHeaders.set(key, value); + } + } + } + + const requestInit: RequestInit = { + method: upperMethod, + headers: upstreamHeaders, + }; + + if (body !== undefined && body !== null && upperMethod !== 'GET' && upperMethod !== 'HEAD') { + requestInit.body = typeof body === 'string' ? body : JSON.stringify(body); + } + + const upstreamResponse = await fetch(parsedUrl, requestInit); + const contentType = upstreamResponse.headers.get('content-type') ?? ''; + let data: unknown; + + if (contentType.includes('application/json')) { + data = await upstreamResponse.json(); + } else { + data = await upstreamResponse.text(); + } + + const sanitizedHeaders = Object.fromEntries( + Array.from(upstreamResponse.headers.entries()).filter( + ([key]) => !STRIPPED_RESPONSE_HEADERS.has(key.toLowerCase()), + ), + ); + + return NextResponse.json({ + status: upstreamResponse.status, + statusText: upstreamResponse.statusText, + headers: sanitizedHeaders, + data, + }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : 'Proxy request failed.' }, + { status: 500 }, + ); + } +} diff --git a/app/layout.config.tsx b/app/layout.config.tsx index 5b277cc09..9339be0f7 100644 --- a/app/layout.config.tsx +++ b/app/layout.config.tsx @@ -25,6 +25,7 @@ export const baseOptions: BaseLayoutProps = { text: 'Chainhook', description: 'Monitor and analyze Clarity smart contract activity.', url: '/tools/chainhook', + isBeta: true, }, { text: 'Contract Monitoring', @@ -35,7 +36,6 @@ export const baseOptions: BaseLayoutProps = { text: 'Bitcoin Indexer', description: 'Indexer for Bitcoin blockchain data.', url: '/tools/bitcoin-indexer', - isNew: true, }, ], }, @@ -69,6 +69,12 @@ export const baseOptions: BaseLayoutProps = { description: 'API for retrieving NFT and fungible token metadata.', url: '/apis/token-metadata-api', }, + { + text: 'Chainhook API', + description: 'RESTful API for accessing Chainhook', + url: '/apis/chainhook-api', + isNew: true, + }, { text: 'Platform API', description: 'API for accessing Hiro Platform data and functionality.', diff --git a/app/layout.tsx b/app/layout.tsx index 598c17358..1f9353d94 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -4,6 +4,7 @@ import type { ReactNode } from 'react'; import { aeonik, aeonikFono, aeonikMono, inter } from '@/fonts'; import { KeyboardShortcutsProvider } from '@/hooks/use-keyboard-shortcuts'; import { QueryProvider } from '@/providers/query-provider'; +import { ApiCredentialsProvider } from '@/providers/api-credentials-provider'; export default function RootLayout({ children }: { children: ReactNode }) { return ( @@ -14,15 +15,17 @@ export default function RootLayout({ children }: { children: ReactNode }) { > - - - {children} - - + + + + {children} + + + diff --git a/components/card.tsx b/components/card.tsx index ef7543fe6..e966f8081 100644 --- a/components/card.tsx +++ b/components/card.tsx @@ -123,6 +123,7 @@ export type SecondaryCardProps = { title: string; description: string; tag?: string; + beta?: string; } & Omit; export function SecondaryCard({ @@ -174,6 +175,7 @@ export type IndexCardProps = { title: string; description: string; tag?: string; + badge?: 'new' | 'beta'; } & Omit; export function IndexCard({ @@ -181,10 +183,15 @@ export function IndexCard({ title, description, tag, + badge, ...props }: IndexCardProps): React.ReactElement { const isBitcoinTag = tag && (tag.toLowerCase() === 'bitcoin' || tag.toLowerCase() === 'bitcoin l1'); + const badgeClassName = + badge === 'beta' + ? 'bg-brand-orange dark:bg-brand-orange text-neutral-950' + : 'bg-[var(--color-brand-mint)] dark:bg-[var(--color-brand-mint)] text-neutral-950'; return ( )}
-

{title}

+
+

{title}

+ {badge && ( + + {badge.toUpperCase()} + + )} +

{description}

{tag && ( {tag} @@ -223,12 +239,24 @@ export function IndexCard({ } export type SmallCardProps = { - icon: ReactNode; + icon?: ReactNode; title: string; description: string; + badge?: 'new' | 'beta'; } & Omit; -export function SmallCard({ icon, title, description, ...props }: CardProps): React.ReactElement { +export function SmallCard({ + icon, + title, + description, + badge, + ...props +}: SmallCardProps): React.ReactElement { + const badgeClassName = + badge === 'beta' + ? 'bg-brand-orange dark:bg-brand-orange text-neutral-950' + : 'bg-[var(--color-brand-mint)] dark:bg-[var(--color-brand-mint)] text-neutral-950'; + return ( )}
-

- {title} -

+
+

+ {title} +

+ {badge && ( + + {badge.toUpperCase()} + + )} +

{description}

diff --git a/components/docskit/code-group.tsx b/components/docskit/code-group.tsx index 42c747b09..a9621beea 100644 --- a/components/docskit/code-group.tsx +++ b/components/docskit/code-group.tsx @@ -1,6 +1,6 @@ export const TITLEBAR = 'px-2 py-1 w-full h-10 font-inter'; export const CODEBLOCK = - 'border rounded selection:bg-ch-selection border-ch-border overflow-x-auto my-4 relative grid'; + 'border rounded selection:bg-ch-selection border-ch-border overflow-x-auto my-4 relative grid group'; type CodeOptions = { copyButton?: boolean; diff --git a/components/docskit/code.client.tsx b/components/docskit/code.client.tsx index 7c3acff74..9dde4ebb0 100644 --- a/components/docskit/code.client.tsx +++ b/components/docskit/code.client.tsx @@ -23,21 +23,37 @@ export function MultiCode({ group, className }: { group: CodeGroup; className?: className={cn(CODEBLOCK, className)} style={style} > - - {group.tabs.map(({ icon, title }) => ( - - {icon} - {title} -
- - ))} + + {group.tabs.map(({ icon, title }) => { + const isActive = currentTitle === title; + + return ( + + {icon} + {title} +
+ + ); + })} {group.options.copyButton && (
diff --git a/components/docskit/copy-button.tsx b/components/docskit/copy-button.tsx index 1151ddd33..3712a80a7 100644 --- a/components/docskit/copy-button.tsx +++ b/components/docskit/copy-button.tsx @@ -12,7 +12,10 @@ export function CopyButton({ text, className }: { text: string; className?: stri type="button" className={cn( copied && '!bg-[#A6E3A1] hover:bg-[#A6E3A1] !text-[hsl(var(--dark))]/70', - 'hover:bg-accent -m-1 p-1 rounded hidden sm:block', + 'bg-card/80 -m-1 p-1 rounded hidden sm:block backdrop-blur', + 'opacity-0 group-hover:opacity-100 focus-visible:opacity-100', + 'pointer-events-none group-hover:pointer-events-auto focus-visible:pointer-events-auto', + 'transition-opacity duration-200', className, )} onClick={() => { diff --git a/components/layout/mobile-navigation.tsx b/components/layout/mobile-navigation.tsx index c9faac650..1e693b10c 100644 --- a/components/layout/mobile-navigation.tsx +++ b/components/layout/mobile-navigation.tsx @@ -243,14 +243,23 @@ export function MobileNavigation({ isOpen = false, onClose, tree }: MobileNaviga onClick={handleClose} className={cn( 'flex items-center justify-between px-2 py-3 text-lg hover:bg-accent transition-colors', - subItem.isNew && 'gap-3 justify-start', + (subItem.isNew || subItem.isBeta) && 'gap-3 justify-start', )} > {subItem.text} - {subItem.isNew && ( - - New - + {(subItem.isNew || subItem.isBeta) && ( +
+ {subItem.isNew && ( + + New + + )} + {subItem.isBeta && ( + + Beta + + )} +
)} ); diff --git a/components/layouts/docs.tsx b/components/layouts/docs.tsx index 90628a8b2..aa29c4418 100644 --- a/components/layouts/docs.tsx +++ b/components/layouts/docs.tsx @@ -24,6 +24,27 @@ import { DocsLogo } from '../ui/icon'; import { NavigationMenu, NavigationMenuList } from '../ui/navigation-menu'; import { renderNavItem } from './links'; +type SeparatorBadge = 'new' | 'beta'; + +const separatorBadgeStyles: Record = { + new: 'font-regular text-[10px] px-1 py-0.5 rounded uppercase text-neutral-950 border-none bg-[var(--color-brand-mint)] dark:bg-[var(--color-brand-mint)]', + beta: 'font-regular text-[10px] px-1 py-0.5 rounded uppercase bg-brand-orange dark:bg-brand-orange text-neutral-950 border-none', +}; + +function parseSeparatorMeta(name?: string): { label: string; badge?: SeparatorBadge } { + if (!name) { + return { label: '' }; + } + const [rawLabel, rawBadge] = name.split('|'); + const label = rawLabel?.trim() || name; + const normalizedBadge = rawBadge?.trim().toLowerCase(); + const badge = + normalizedBadge === 'new' || normalizedBadge === 'beta' + ? (normalizedBadge as SeparatorBadge) + : undefined; + return { label, badge }; +} + export interface DocsLayoutProps { tree: PageTree.Root; children: ReactNode; @@ -310,8 +331,15 @@ export function SidebarItem({ item, children }: { item: PageTree.Node; children: } if (item.type === 'separator') { + const separatorName = typeof item.name === 'string' ? item.name : undefined; + const { label, badge } = parseSeparatorMeta(separatorName); return ( -

{item.name}

+
+

{label || item.name}

+ {badge ? ( + {badge.toUpperCase()} + ) : null} +
); } @@ -395,7 +423,7 @@ export function PageBadges({ item }: { item: PageTree.Node }) { badges.push( New , diff --git a/components/layouts/links.tsx b/components/layouts/links.tsx index ea3091d55..f3aab10c9 100644 --- a/components/layouts/links.tsx +++ b/components/layouts/links.tsx @@ -52,6 +52,7 @@ export interface MainItemType extends BaseLinkType { text: ReactNode; description?: ReactNode; isNew?: boolean; + isBeta?: boolean; } export interface IconItemType extends BaseLinkType { @@ -311,13 +312,28 @@ export function renderNavItem(item: LinkItemType): ReactNode { > {menuItem.text} - {menuItem.isNew && ( - - New - + {(menuItem.isNew || menuItem.isBeta) && ( +
+ {menuItem.isNew && ( + + New + + )} + {menuItem.isBeta && ( + + Beta + + )} +
)}
@@ -378,7 +394,23 @@ function DropdownNavItem({ item }: { item: DropdownItemType }) { return ( -
{dropdownItem.text}
+
+ {dropdownItem.text} + {(dropdownItem.isNew || dropdownItem.isBeta) && ( +
+ {dropdownItem.isNew && ( + + New + + )} + {dropdownItem.isBeta && ( + + Beta + + )} +
+ )} +
{dropdownItem.description && (
{dropdownItem.description}
)} diff --git a/components/openapi/api-page.tsx b/components/openapi/api-page.tsx index 96d10f8a5..740edefdb 100644 --- a/components/openapi/api-page.tsx +++ b/components/openapi/api-page.tsx @@ -20,6 +20,8 @@ interface APIPageProps { }; }; baseUrl?: string; + credentialId?: string; + credentialPublicOperations?: Array<{ method: string; path: string }>; } export async function APIPage({ @@ -30,6 +32,8 @@ export async function APIPage({ clarityConversion = false, playgroundOptions, baseUrl, + credentialId, + credentialPublicOperations, }: APIPageProps) { let apiDoc: OpenAPIDocument; @@ -71,6 +75,8 @@ export async function APIPage({ hasHead={hasHead} playgroundOptions={playgroundOptions} baseUrl={baseUrl} + credentialId={credentialId} + credentialPublicOperations={credentialPublicOperations} /> ))} diff --git a/components/openapi/api-playground/index.tsx b/components/openapi/api-playground/index.tsx index 3a30108af..0e6ba0827 100644 --- a/components/openapi/api-playground/index.tsx +++ b/components/openapi/api-playground/index.tsx @@ -1,13 +1,16 @@ 'use client'; import { cvToJSON, cvToString, hexToCV } from '@stacks/transactions'; -import { ChevronDown, Play } from 'lucide-react'; -import { useRef, useState } from 'react'; +import { ChevronDown, Eye, EyeOff, Play } from 'lucide-react'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import type { CSSProperties } from 'react'; import { CodeSync } from '@/components/docskit/code'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; +import { Input } from '@/components/ui/input'; import { cn } from '@/lib/utils'; +import { useApiCredentials } from '@/providers/api-credentials-provider'; import type { OpenAPIOperation } from '../types'; import { RequestBuilder } from './request-builder'; import { executeRequest } from './request-executor'; @@ -24,6 +27,8 @@ interface APIPlaygroundProps { headerName?: string; }; }; + credentialId?: string; + credentialPublicOperations?: Array<{ method: string; path: string }>; } interface APIResponse { @@ -35,14 +40,57 @@ interface APIResponse { error?: string; } +type ObfuscatedInputStyle = CSSProperties & { + WebkitTextSecurity?: 'disc' | 'none'; +}; + export function APIPlayground({ operation, baseUrl = '', clarityConversion = false, playgroundOptions, + credentialId, + credentialPublicOperations, }: APIPlaygroundProps) { const [loading, setLoading] = useState(false); const [response, setResponse] = useState(null); + const { getCredential, setCredential, clearCredential } = useApiCredentials(); + const credentialValue = credentialId ? getCredential(credentialId) : ''; + const [credentialInput, setCredentialInput] = useState(credentialValue); + const [credentialVisible, setCredentialVisible] = useState(false); + const credentialMaskStyle = useMemo( + () => ({ + WebkitTextSecurity: credentialInput && !credentialVisible ? 'disc' : 'none', + }), + [credentialInput, credentialVisible], + ); + + useEffect(() => { + setCredentialInput(credentialValue); + }, [credentialValue]); + + const isPublicOperation = + credentialPublicOperations?.some( + (entry) => + entry.path === operation.path && + entry.method.toUpperCase() === operation.method.toUpperCase(), + ) ?? false; + + const needsCredential = Boolean(credentialId) && !isPublicOperation; + + const mergedPlaygroundOptions = useMemo(() => { + if (needsCredential && credentialValue) { + return { + ...(playgroundOptions ?? {}), + defaultAuth: { + type: 'api-key' as const, + headerName: 'x-api-key', + value: credentialValue, + }, + }; + } + return playgroundOptions; + }, [credentialValue, needsCredential, playgroundOptions]); const getInitialFormData = () => { const initialData: Record = {}; @@ -145,6 +193,18 @@ export function APIPlayground({ return true; }; + const handleCredentialChange = (value: string) => { + setCredentialInput(value); + if (!value) setCredentialVisible(false); + if (!credentialId) return; + + if (value.trim()) { + setCredential(credentialId, value); + } else { + clearCredential(credentialId); + } + }; + const handleSend = async () => { const requiredSections = []; if (pathParams.some((p) => p.required && !formData[p.name])) requiredSections.push('path'); @@ -272,8 +332,8 @@ export function APIPlayground({ baseUrl || 'https://api.hiro.so', clarityConversion, { - proxyUrl: playgroundOptions?.proxyUrl, - auth: playgroundOptions?.defaultAuth, + proxyUrl: mergedPlaygroundOptions?.proxyUrl, + auth: mergedPlaygroundOptions?.defaultAuth, }, ); setResponse(result); @@ -302,7 +362,7 @@ export function APIPlayground({ />
{/* Parameters sections - integrated into the same component */} - {hasParameters && ( + {(hasParameters || needsCredential) && (
+ {needsCredential && ( +
+
+ Hiro API key + {credentialValue ? ( + + ) : null} +
+
+ handleCredentialChange(event.target.value)} + placeholder="Enter your Hiro API key to enable requests" + type="text" + autoComplete="off" + spellCheck={false} + inputMode="text" + name={`${credentialId}-api-key`} + autoCorrect="off" + autoCapitalize="off" + className="text-sm font-fono bg-white dark:bg-neutral-950 border-border/50" + data-lpignore="true" + data-form-type="other" + style={credentialMaskStyle} + /> + {credentialInput && ( + + )} +
+
+ )} + {/* Path Parameters */} {pathParams.length > 0 && ( )} +
+ )} - {/* Response section */} - {response && ( -
- {response.status && ( - = 200 && response.status < 300 - ? 'bg-[#e7f7e7] text-[#4B714D] border-[#c2ebc4] dark:bg-background dark:text-[#c2ebc4] dark:border-[#c2ebc4]' - : 'bg-[#ffe7e7] text-[#8A4B4B] border-[#ffc2c2] dark:bg-background dark:text-[#ffc2c2] dark:border-[#ffc2c2]', - )} - > - {response.status} {response.statusText || ''} - + {/* Response section */} + {response && ( +
+ {response.status && ( + = 200 && response.status < 300 + ? 'bg-[#e7f7e7] text-[#4B714D] border-[#c2ebc4] dark:bg-background dark:text-[#c2ebc4] dark:border-[#c2ebc4]' + : 'bg-[#ffe7e7] text-[#8A4B4B] border-[#ffc2c2] dark:bg-background dark:text-[#ffc2c2] dark:border-[#ffc2c2]', )} - {response.error ? ( -
{response.error}
- ) : response.data ? ( - - ) : null} -
+ > + {response.status} {response.statusText || ''} + )} + {response.error ? ( +
{response.error}
+ ) : response.data ? ( + + ) : null}
)}
diff --git a/components/openapi/api-playground/request-executor.tsx b/components/openapi/api-playground/request-executor.tsx index c4a01d069..d8734319c 100644 --- a/components/openapi/api-playground/request-executor.tsx +++ b/components/openapi/api-playground/request-executor.tsx @@ -85,11 +85,12 @@ export async function executeRequest( } } + const methodSupportsBody = ['POST', 'PUT', 'PATCH', 'DELETE'].includes( + operation.method.toUpperCase(), + ); + let requestBody: any; - if ( - formData.body && - (operation.requestBody || ['POST', 'PUT', 'PATCH'].includes(operation.method.toUpperCase())) - ) { + if (formData.body && (operation.requestBody || methodSupportsBody)) { try { // Parse and re-stringify to validate JSON const parsedBody = JSON.parse(formData.body); @@ -99,15 +100,8 @@ export async function executeRequest( console.error('Failed to parse body as JSON:', error); requestBody = formData.body; } - } else { - console.log( - 'No body to send. formData.body:', - formData.body, - 'operation.requestBody:', - operation.requestBody, - 'method:', - operation.method, - ); + } else if (operation.requestBody?.required && !formData.body) { + console.warn(`Request body is required for ${operation.method} ${operation.path}`); } const startTime = performance.now(); diff --git a/components/openapi/operation-section.tsx b/components/openapi/operation-section.tsx index 8e7a6843d..143bfcc4f 100644 --- a/components/openapi/operation-section.tsx +++ b/components/openapi/operation-section.tsx @@ -22,6 +22,8 @@ interface OperationSectionProps { }; }; baseUrl?: string; + credentialId?: string; + credentialPublicOperations?: Array<{ method: string; path: string }>; } export async function OperationSection({ @@ -32,6 +34,8 @@ export async function OperationSection({ hasHead, playgroundOptions, baseUrl: baseUrlOverride, + credentialId, + credentialPublicOperations, }: OperationSectionProps) { const baseUrl = baseUrlOverride || servers?.[0]?.url || ''; @@ -45,6 +49,8 @@ export async function OperationSection({ baseUrl={baseUrl} clarityConversion={clarityConversion} playgroundOptions={playgroundOptions} + credentialId={credentialId} + credentialPublicOperations={credentialPublicOperations} /> )} diff --git a/content/docs/en/apis/chainhook-api/index.mdx b/content/docs/en/apis/chainhook-api/index.mdx new file mode 100644 index 000000000..e33a56769 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/index.mdx @@ -0,0 +1,100 @@ +--- +title: Chainhook API +sidebarTitle: Overview +description: REST API for managing Chainhooks programmatically +--- + +## Overview + +The Chainhook API is a REST API that allows you to programmatically manage chainhooks, configure event filters, and control webhook delivery. It provides the same functionality as the [Chainhook SDK](/tools/chainhook/chainhook-sdk) through direct HTTP requests. + +Use the Chainhook API when: +- You're working in a language without SDK support +- You need direct HTTP control over chainhook operations +- You're building custom integrations or automation workflows +- You prefer REST APIs over client libraries + +## Key Features + +- **Full chainhook management** - Create, read, update, and delete chainhooks +- **Event filtering** - Configure filters for FT, NFT, STX, contract, and system events +- **Webhook configuration** - Set up HTTP POST endpoints for event delivery +- **Evaluation endpoints** - Test chainhooks against historical blocks +- **Bulk operations** - Enable or disable multiple chainhooks at once +- **Secret management** - Rotate webhook consumer secrets for security + +## Base URLs + +The Chainhook API is available on both mainnet and testnet: + +``` +Testnet: https://api.testnet.hiro.so/chainhooks/v1 +Mainnet: https://api.mainnet.hiro.so/chainhooks/v1 +``` + +## Authentication + +All API requests require authentication using a Hiro API key in the `x-api-key` header: + +```bash +curl https://api.testnet.hiro.so/chainhooks/v1/me/ \ + -H "x-api-key: YOUR_API_KEY" +``` + +Get your API key from [platform.hiro.so](https://platform.hiro.so). + +## Quick Example + +Register a new chainhook to monitor FT transfers: + +```bash +curl -X POST https://api.testnet.hiro.so/chainhooks/v1/me/ \ + -H "x-api-key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "ft-transfer-monitor", + "version": "1", + "chain": "stacks", + "network": "testnet", + "filters": { + "events": [{ + "type": "ft_transfer", + "asset_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.usda-token::usda" + }] + }, + "action": { + "type": "http_post", + "url": "https://example.com/webhooks" + }, + "options": { + "enable_on_registration": true + } + }' +``` + +## SDK vs REST API + +Both the SDK and REST API provide identical functionality: + +| Feature | SDK | REST API | +|---------|-----|----------| +| Language | TypeScript/JavaScript | Any language | +| Type Safety | Yes | No | +| Authentication | Automatic | Manual headers | +| Error Handling | Built-in | Custom | +| Best For | Node.js/Bun apps | Other languages, scripts | + +For TypeScript/JavaScript projects, we recommend using the [Chainhook SDK](/tools/chainhook/chainhook-sdk) for better developer experience. + +## Next Steps + +:::next-steps +* [Usage Guide](/apis/chainhook-api/usage): Authentication and best practices +* [API Reference](/apis/chainhook-api/reference): Complete endpoint documentation +::: + +:::callout +type: help +### Need help building with the Chainhook API? +Reach out to us on the **#chainhook** channel on [Discord](https://stacks.chat/) under Hiro Developer Tools. There's also a [weekly office hours](https://www.addevent.com/event/oL21905919) call every Thursday at 11am ET. +::: diff --git a/content/docs/en/apis/chainhook-api/meta.json b/content/docs/en/apis/chainhook-api/meta.json new file mode 100644 index 000000000..ed8d35b52 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/meta.json @@ -0,0 +1,5 @@ +{ + "title": "Chainhook API", + "root": true, + "pages": ["index", "usage", "---Reference---", "...reference"] +} diff --git a/content/docs/en/apis/chainhook-api/reference/chainhooks/bulk-enable-chainhooks.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/bulk-enable-chainhooks.mdx new file mode 100644 index 000000000..e8c0b267e --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/bulk-enable-chainhooks.mdx @@ -0,0 +1,12 @@ +--- +title: Bulk enable/disable status +sidebarTitle: Bulk enable/disable status +description: Updates the enabled status of chainhooks that match the provided filters +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/chainhooks/delete-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/delete-chainhook.mdx new file mode 100644 index 000000000..eb3974c08 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/delete-chainhook.mdx @@ -0,0 +1,12 @@ +--- +title: Delete +sidebarTitle: Delete +description: Deletes a chainhook by its UUID +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/chainhooks/evaluate-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/evaluate-chainhook.mdx new file mode 100644 index 000000000..73c77e858 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/evaluate-chainhook.mdx @@ -0,0 +1,12 @@ +--- +title: Evaluate a specific block +sidebarTitle: Evaluate a specific block +description: Queues an on-demand evaluation job for a specific block +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhook.mdx new file mode 100644 index 000000000..441b21d07 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhook.mdx @@ -0,0 +1,12 @@ +--- +title: Get a specific chainhook +sidebarTitle: Get a specific chainhook +description: Returns a chainhook by its UUID +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhooks.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhooks.mdx new file mode 100644 index 000000000..a312452de --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhooks.mdx @@ -0,0 +1,12 @@ +--- +title: Get all chainhooks +sidebarTitle: Get all chainhooks +description: Returns all chainhooks registered by the current user +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/chainhooks/index.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/index.mdx new file mode 100644 index 000000000..ad838a5df --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/index.mdx @@ -0,0 +1,6 @@ +--- +title: Chainhooks +index: true +full: true +description: Manage Chainhook API endpoints for creating, updating, and evaluating chainhooks. +--- diff --git a/content/docs/en/apis/chainhook-api/reference/chainhooks/meta.json b/content/docs/en/apis/chainhook-api/reference/chainhooks/meta.json new file mode 100644 index 000000000..dd2beb039 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/meta.json @@ -0,0 +1,14 @@ +{ + "title": "Chainhooks", + "pages": [ + "register-chainhook", + "get-chainhooks", + "get-chainhook", + "update-chainhook", + "delete-chainhook", + "evaluate-chainhook", + "update-chainhook-enabled", + "bulk-enable-chainhooks" + ], + "defaultOpen": false +} diff --git a/content/docs/en/apis/chainhook-api/reference/chainhooks/register-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/register-chainhook.mdx new file mode 100644 index 000000000..e0c27af52 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/register-chainhook.mdx @@ -0,0 +1,12 @@ +--- +title: Create +sidebarTitle: Create +description: Allows users to create/register a new Chainhook +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook-enabled.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook-enabled.mdx new file mode 100644 index 000000000..188f6cf99 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook-enabled.mdx @@ -0,0 +1,12 @@ +--- +title: Enable/disable status +sidebarTitle: Enable/disable status +description: Changes the enabled status of a chainhook by its UUID +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook.mdx new file mode 100644 index 000000000..2322db17f --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook.mdx @@ -0,0 +1,12 @@ +--- +title: Update +sidebarTitle: Update +description: Updates a chainhook by its UUID +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/info/index.mdx b/content/docs/en/apis/chainhook-api/reference/info/index.mdx new file mode 100644 index 000000000..03549d682 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/info/index.mdx @@ -0,0 +1,6 @@ +--- +title: Info +index: true +full: true +description: Check the current status of the Chainhook API. +--- diff --git a/content/docs/en/apis/chainhook-api/reference/info/meta.json b/content/docs/en/apis/chainhook-api/reference/info/meta.json new file mode 100644 index 000000000..0021087d5 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/info/meta.json @@ -0,0 +1,5 @@ +{ + "title": "Info", + "pages": ["status"], + "defaultOpen": false +} diff --git a/content/docs/en/apis/chainhook-api/reference/info/status.mdx b/content/docs/en/apis/chainhook-api/reference/info/status.mdx new file mode 100644 index 000000000..092888e4f --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/info/status.mdx @@ -0,0 +1,12 @@ +--- +title: API Status +sidebarTitle: API status +description: Displays the status of the API and its current workload +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/secrets/delete-consumer-secret.mdx b/content/docs/en/apis/chainhook-api/reference/secrets/delete-consumer-secret.mdx new file mode 100644 index 000000000..95df97d3c --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/secrets/delete-consumer-secret.mdx @@ -0,0 +1,12 @@ +--- +title: Delete consumer secret +sidebarTitle: Delete consumer secret +description: Deletes the Chainhooks event payload consumer secret +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/secrets/index.mdx b/content/docs/en/apis/chainhook-api/reference/secrets/index.mdx new file mode 100644 index 000000000..e4d1fbc9b --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/secrets/index.mdx @@ -0,0 +1,6 @@ +--- +title: Secrets +index: true +full: true +description: Rotate or remove Chainhook consumer secrets that secure your webhooks. +--- diff --git a/content/docs/en/apis/chainhook-api/reference/secrets/meta.json b/content/docs/en/apis/chainhook-api/reference/secrets/meta.json new file mode 100644 index 000000000..00a71343a --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/secrets/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Secrets", + "pages": [ + "rotate-consumer-secret", + "delete-consumer-secret" + ], + "defaultOpen": false +} diff --git a/content/docs/en/apis/chainhook-api/reference/secrets/rotate-consumer-secret.mdx b/content/docs/en/apis/chainhook-api/reference/secrets/rotate-consumer-secret.mdx new file mode 100644 index 000000000..b531aa573 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/secrets/rotate-consumer-secret.mdx @@ -0,0 +1,12 @@ +--- +title: Rotate consumer secret +sidebarTitle: Rotate consumer secret +description: Generates and returns a new Chainhooks event payload consumer secret +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/usage.mdx b/content/docs/en/apis/chainhook-api/usage.mdx new file mode 100644 index 000000000..2c7408dbe --- /dev/null +++ b/content/docs/en/apis/chainhook-api/usage.mdx @@ -0,0 +1,287 @@ +--- +title: Usage +description: Learn how to authenticate, make requests, and handle responses with the Chainhook API +--- + +## Authentication + +All Chainhook API requests require authentication using a Hiro API key. + +### Get Your API Key + +1. Visit [platform.hiro.so](https://platform.hiro.so) +2. Sign in or create an account +3. Navigate to API Keys +4. Generate or copy your API key + +### Using Your API Key + +Include your API key in the `x-api-key` header for all requests: + +```bash +curl https://api.testnet.hiro.so/chainhooks/v1/me/ \ + -H "x-api-key: YOUR_API_KEY" +``` + +**Security best practices:** +- Store API keys in environment variables, never in code +- Use different keys for development and production +- Rotate keys periodically +- Never commit keys to version control + +## Base URLs + +Use the appropriate base URL for your environment: + +``` +Testnet: https://api.testnet.hiro.so/chainhooks/v1 +Mainnet: https://api.mainnet.hiro.so/chainhooks/v1 +``` + +**Always test on testnet first** before deploying to mainnet. + +## Request Format + +### Headers + +All requests should include: + +```http +Content-Type: application/json +x-api-key: YOUR_API_KEY +``` + +### Request Body + +Most endpoints accept JSON request bodies. Example for registering a chainhook: + +```bash +curl -X POST https://api.testnet.hiro.so/chainhooks/v1/me/ \ + -H "x-api-key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "my-chainhook", + "version": "1", + "chain": "stacks", + "network": "testnet", + "filters": { + "events": [{ + "type": "stx_transfer", + "sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }] + }, + "action": { + "type": "http_post", + "url": "https://example.com/webhooks" + } + }' +``` + +## Response Format + +### Success Responses + +Successful responses return JSON with relevant data: + +```json +{ + "uuid": "123e4567-e89b-12d3-a456-426614174000", + "definition": { + "name": "my-chainhook", + "version": "1", + "chain": "stacks", + "network": "testnet", + ... + }, + "status": { + "status": "new", + "enabled": false, + "created_at": 1234567890, + ... + } +} +``` + +### HTTP Status Codes + +| Code | Meaning | Description | +|------|---------|-------------| +| 200 | OK | Request succeeded, response body contains data | +| 204 | No Content | Request succeeded, no response body | +| 400 | Bad Request | Invalid request format or parameters | +| 401 | Unauthorized | Missing or invalid API key | +| 404 | Not Found | Chainhook UUID not found | +| 429 | Too Many Requests | Rate limit exceeded | +| 500 | Server Error | Internal server error | + +### Error Responses + +Error responses include details about what went wrong: + +```json +{ + "error": "Invalid request body", + "details": "filters.events is required" +} +``` + +## Common Patterns + +### List All Chainhooks + +```bash +curl https://api.testnet.hiro.so/chainhooks/v1/me/ \ + -H "x-api-key: YOUR_API_KEY" +``` + +### Get a Specific Chainhook + +```bash +curl https://api.testnet.hiro.so/chainhooks/v1/me/{uuid} \ + -H "x-api-key: YOUR_API_KEY" +``` + +### Enable a Chainhook + +```bash +curl -X PATCH https://api.testnet.hiro.so/chainhooks/v1/me/{uuid}/enabled \ + -H "x-api-key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"enabled": true}' +``` + +### Delete a Chainhook + +```bash +curl -X DELETE https://api.testnet.hiro.so/chainhooks/v1/me/{uuid} \ + -H "x-api-key: YOUR_API_KEY" +``` + +### Evaluate Against a Block + +Test your chainhook against a specific block: + +```bash +curl -X POST https://api.testnet.hiro.so/chainhooks/v1/me/{uuid}/evaluate \ + -H "x-api-key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"block_height": 150000}' +``` + +### Bulk Enable/Disable + +Enable or disable multiple chainhooks matching filters: + +```bash +curl -X PATCH https://api.testnet.hiro.so/chainhooks/v1/me/enabled \ + -H "x-api-key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "enabled": false, + "filters": { + "webhook_url": "https://old-endpoint.com/webhooks" + } + }' +``` + +## Rate Limits + +The Chainhook API enforces rate limits based on your subscription tier: + +- Free tier: 100 requests per minute +- Pro tier: 1000 requests per minute +- Enterprise: Custom limits + +When rate limited, you'll receive a `429 Too Many Requests` response. Implement exponential backoff in your application: + +```javascript +async function makeRequestWithRetry(url, options, maxRetries = 3) { + for (let i = 0; i < maxRetries; i++) { + const response = await fetch(url, options); + + if (response.status === 429) { + const delay = Math.pow(2, i) * 1000; + await new Promise(resolve => setTimeout(resolve, delay)); + continue; + } + + return response; + } + + throw new Error('Max retries exceeded'); +} +``` + +## Webhook Security + +### Consumer Secret + +When you register a chainhook, webhook payloads include an `x-chainhook-consumer-secret` header. Validate this secret in your webhook endpoint: + +```javascript +app.post('/webhooks', (req, res) => { + const receivedSecret = req.headers['x-chainhook-consumer-secret']; + const expectedSecret = process.env.CHAINHOOK_CONSUMER_SECRET; + + if (receivedSecret !== expectedSecret) { + return res.status(401).send('Unauthorized'); + } + + // Process webhook payload + res.sendStatus(200); +}); +``` + +### Rotate Secret + +Periodically rotate your consumer secret: + +```bash +curl -X POST https://api.testnet.hiro.so/chainhooks/v1/me/secret \ + -H "x-api-key: YOUR_API_KEY" +``` + +Store the new secret securely and update your webhook endpoint. + +## Best Practices + +1. **Start on testnet** - Always test chainhooks on testnet before mainnet +2. **Enable gradually** - Create chainhooks disabled, test with `evaluate`, then enable +3. **Handle errors** - Implement proper error handling and retries +4. **Validate webhooks** - Always verify the consumer secret header +5. **Use HTTPS** - Webhook URLs must use HTTPS for security +6. **Monitor usage** - Track API usage to stay within rate limits +7. **Version control** - Document your chainhook configurations +8. **Clean up** - Delete unused chainhooks to reduce costs + +## Pagination + +List endpoints support pagination via query parameters: + +```bash +# Get first page (default: 20 results) +curl "https://api.testnet.hiro.so/chainhooks/v1/me/?limit=20&offset=0" \ + -H "x-api-key: YOUR_API_KEY" + +# Get next page +curl "https://api.testnet.hiro.so/chainhooks/v1/me/?limit=20&offset=20" \ + -H "x-api-key: YOUR_API_KEY" +``` + +Response includes pagination metadata: + +```json +{ + "limit": 20, + "offset": 0, + "total": 45, + "results": [...] +} +``` + +## Next Steps + +:::next-steps +* [API Reference](/apis/chainhook-api/reference): Complete endpoint documentation +* [Filter Reference](/tools/chainhook/reference/filters): Event filter syntax +::: diff --git a/content/docs/en/resources/archive/download-guide.mdx b/content/docs/en/resources/archive/download-guide.mdx index a78699448..a0a5610c2 100644 --- a/content/docs/en/resources/archive/download-guide.mdx +++ b/content/docs/en/resources/archive/download-guide.mdx @@ -137,7 +137,7 @@ The `marf.sqlite.blobs` file can be very large and may take significant time to ## FAQ - + Why do downloads keep failing? diff --git a/content/docs/en/resources/guides/index.mdx b/content/docs/en/resources/guides/index.mdx index 0b9de22b9..701f815c0 100644 --- a/content/docs/en/resources/guides/index.mdx +++ b/content/docs/en/resources/guides/index.mdx @@ -7,8 +7,6 @@ llm: false ## Overview -To explore Guides with AI, copy and paste [llms.txt](/resources/guides/llms.txt) into your LLM of choice. - ## Building Projects - [Build an NFT Marketplace](/resources/guides/build-an-nft-marketplace) - Guide to building a decentralized NFT marketplace on Stacks. @@ -26,4 +24,4 @@ To explore Guides with AI, copy and paste [llms.txt](/resources/guides/llms.txt) - [Installing Docker](/resources/guides/installing-docker) - Instructions for setting up Docker for development. - [Sync a Bitcoin Node](/resources/guides/sync-a-bitcoin-node) - Guide to synchronizing a Bitcoin node. -- [Sync a Stacks Node](/resources/guides/sync-a-stacks-node) - Guide to synchronizing a Stacks node. \ No newline at end of file +- [Sync a Stacks Node](/resources/guides/sync-a-stacks-node) - Guide to synchronizing a Stacks node. diff --git a/content/docs/en/resources/guides/meta.json b/content/docs/en/resources/guides/meta.json index dad1610fa..d0e495ae7 100644 --- a/content/docs/en/resources/guides/meta.json +++ b/content/docs/en/resources/guides/meta.json @@ -15,8 +15,6 @@ "using-clarity-values", "---Applications---", "no-loss-lottery", - "---Integrations---", - "using-pyth-price-feeds", "---Infrastructure---", "installing-docker", "sync-a-bitcoin-node", diff --git a/content/docs/en/resources/guides/using-pyth-price-feeds.mdx b/content/docs/en/resources/guides/using-pyth-price-feeds.mdx deleted file mode 100644 index 8e7d40811..000000000 --- a/content/docs/en/resources/guides/using-pyth-price-feeds.mdx +++ /dev/null @@ -1,418 +0,0 @@ ---- -title: Using Pyth price feeds -description: Complete guide to integrating real-time market price data from Pyth Network into your Stacks applications. ---- - -import { File, Folder, Files } from 'fumadocs-ui/components/files'; -import { Steps, Step } from '@/components/steps'; -import { ArrowRight, Check } from 'lucide-react'; - -## Overview - -This comprehensive guide walks you through integrating [Pyth Network](https://pyth.network)'s decentralized oracle for real-time price data in your Stacks applications. We'll build a complete example: an NFT that can only be minted by paying exactly $100 worth of sBTC. - -:::callout -The Pyth protocol integration is available as a Beta on both testnet and mainnet networks. It's maintained by Trust Machines and provides access to real-time price feeds for BTC, STX, ETH, and USDC. -::: - -## Architecture overview - -Pyth Network uses a unique **pull-based** oracle design: - - - -Unlike push-based oracles that continuously update on-chain prices, Pyth allows users to fetch and submit price updates only when needed, making it more gas-efficient. - -## What we're building - -We'll create a "Benjamin Club" - an exclusive NFT that costs exactly $100 worth of sBTC to mint. This demonstrates: - -- Reading real-time BTC/USD prices from Pyth -- Converting between USD and crypto amounts -- Handling fixed-point arithmetic -- Building a complete frontend integration -- Testing oracle-dependent contracts - - - - - - - - - - - - - - - - - - - -## Implementation steps - - - - ### Write the smart contract - - First, implement the Clarity contract that reads Pyth price data: - - ```clarity contracts/benjamin-club.clar - ;; Benjamin Club - $100 NFT minting contract - (define-constant CONTRACT-OWNER tx-sender) - (define-constant BENJAMIN-COST u100) ;; $100 USD - (define-constant ERR-INSUFFICIENT-FUNDS (err u100)) - (define-constant ERR-PRICE-UPDATE-FAILED (err u101)) - (define-constant ERR-STALE-PRICE (err u102)) - - ;; Pyth oracle contracts - (define-constant PYTH-ORACLE 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3) - (define-constant PYTH-STORAGE 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3) - (define-constant PYTH-DECODER 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-pnau-decoder-v2) - (define-constant WORMHOLE-CORE 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.wormhole-core-v3) - - ;; BTC price feed ID - (define-constant BTC-USD-FEED-ID 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) - - ;; NFT definition - (define-non-fungible-token benjamin-nft uint) - (define-data-var last-token-id uint u0) - - (define-public (mint-for-hundred-dollars (price-feed-bytes (buff 8192))) - (let ( - ;; Update price feed with fresh VAA data - (update-result (try! (contract-call? PYTH-ORACLE - verify-and-update-price-feeds price-feed-bytes { - pyth-storage-contract: PYTH-STORAGE, - pyth-decoder-contract: PYTH-DECODER, - wormhole-core-contract: WORMHOLE-CORE - }))) - - ;; Get the updated BTC price - (price-data (try! (contract-call? PYTH-ORACLE - get-price BTC-USD-FEED-ID PYTH-STORAGE))) - - ;; Process the price data - (btc-price (process-price-data price-data)) - - ;; Calculate required sBTC amount for $100 - (required-sbtc (calculate-sbtc-amount btc-price)) - - ;; Get user's sBTC balance - (user-balance (unwrap! - (contract-call? .sbtc-token get-balance tx-sender) - ERR-INSUFFICIENT-FUNDS)) - ) - ;; Verify price is fresh (less than 5 minutes old) - (try! (verify-price-freshness price-data)) - - ;; Verify user has enough sBTC - (asserts! (>= user-balance required-sbtc) ERR-INSUFFICIENT-FUNDS) - - ;; Transfer sBTC from user - (try! (contract-call? .sbtc-token transfer - required-sbtc tx-sender (as-contract tx-sender) none)) - - ;; Mint the NFT - (let ((token-id (+ (var-get last-token-id) u1))) - (try! (nft-mint? benjamin-nft token-id tx-sender)) - (var-set last-token-id token-id) - (ok { token-id: token-id, price-paid: required-sbtc })) - ) - ) - - (define-private (process-price-data (price-data { - price-identifier: (buff 32), - price: int, - conf: uint, - expo: int, - ema-price: int, - ema-conf: uint, - publish-time: uint, - prev-publish-time: uint - })) - (let ( - ;; Convert fixed-point to regular number - ;; For expo = -8, divide by 10^8 - (denominator (pow u10 (to-uint (* (get expo price-data) -1)))) - (price-uint (to-uint (get price price-data))) - ) - (/ price-uint denominator) - ) - ) - - (define-private (calculate-sbtc-amount (btc-price-usd uint)) - ;; $100 in sats = (100 * 10^8) / btc-price-usd - (/ (* BENJAMIN-COST u100000000) btc-price-usd) - ) - - (define-private (verify-price-freshness (price-data (tuple))) - (let ( - (current-time (unwrap-panic (get-block-info? time block-height))) - (publish-time (get publish-time price-data)) - (max-age u300) ;; 5 minutes - ) - (if (<= (- current-time publish-time) max-age) - (ok true) - ERR-STALE-PRICE) - ) - ) - ``` - - For a detailed explanation of the contract components, see the [Stacks smart contract reference](https://docs.stacks.co/reference). - - - - ### Build the frontend integration - - Create a service to fetch price data from Pyth: - - ```typescript frontend/pyth-service.ts - import { PriceServiceConnection } from '@pythnetwork/price-service-client'; - import { Buffer } from 'buffer'; - - const PRICE_FEEDS = { - BTC_USD: '0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43' - }; - - export async function fetchBTCPriceVAA(): Promise { - const pythClient = new PriceServiceConnection( - 'https://hermes.pyth.network', - { priceFeedRequestConfig: { binary: true } } - ); - - const vaas = await pythClient.getLatestVaas([PRICE_FEEDS.BTC_USD]); - const messageBuffer = Buffer.from(vaas[0], 'base64'); - - return `0x${messageBuffer.toString('hex')}`; - } - ``` - - Then create a React component for minting: - - ```typescript frontend/MintButton.tsx - import { request } from '@stacks/connect'; - import { Cl, Pc } from '@stacks/transactions'; - import { fetchBTCPriceVAA } from './pyth-service'; - import { useState } from 'react'; - - export function MintBenjaminNFT() { - const [loading, setLoading] = useState(false); - - const handleMint = async () => { - setLoading(true); - try { - // Fetch fresh price data - const priceVAA = await fetchBTCPriceVAA(); - - // Create post-conditions for safety - const postConditions = [ - // Oracle fee (1 uSTX max) - Pc.principal('SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R').willSendLte(1).ustx() - ]; - - // Call contract using request - const response = await request('stx_callContract', { - contract: 'SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R.benjamin-club', - functionName: 'mint-for-hundred-dollars', - functionArgs: [Cl.bufferFromHex(priceVAA.slice(2))], - postConditions, - postConditionMode: 'deny', - network: 'mainnet' - }); - - alert(`NFT minted! Transaction ID: ${response.txid}`); - } catch (error) { - console.error('Minting failed:', error); - alert('Failed to mint NFT'); - } finally { - setLoading(false); - } - }; - - return ( - - ); - } - ``` - - For complete frontend integration details, see the [Stacks developer reference](https://docs.stacks.co/reference). - - - - ### Test your implementation - - Write comprehensive tests using Clarinet: - - ```typescript tests/benjamin-club.test.ts - import { describe, expect, it } from "vitest"; - import { Cl } from '@stacks/transactions'; - - describe("Benjamin Club Tests", () => { - it("should calculate correct sBTC amount", () => { - // Set mock BTC price to $100,000 - simnet.callPublicFn( - "mock-pyth-oracle", - "set-mock-price", - [ - Cl.bufferFromHex(BTC_FEED_ID), - Cl.int(10000000000000), // $100,000 with 8 decimals - Cl.int(-8) - ], - deployer - ); - - // Test price calculation - const response = simnet.callReadOnlyFn( - "benjamin-club", - "get-required-sbtc-amount", - [], - wallet1 - ); - - // $100 at $100k/BTC = 0.001 BTC = 100,000 sats - expect(response.result).toBeOk(Cl.uint(100000)); - }); - }); - ``` - - For advanced testing strategies including mainnet simulation, review the [official Stacks reference](https://docs.stacks.co/reference). - - - -## Best practices - -### Price freshness - -Always verify price data is recent enough for your use case: - -```clarity -(define-constant MAX-PRICE-AGE u300) ;; 5 minutes - -(define-private (verify-price-freshness (price-data (tuple))) - (let ((age (- block-height (get publish-time price-data)))) - (asserts! (<= age MAX-PRICE-AGE) ERR-STALE-PRICE) - (ok true))) -``` - -### Error handling - -Implement comprehensive error handling for oracle failures: - -```typescript -try { - const vaa = await fetchBTCPriceVAA(); - // Process VAA... -} catch (error) { - if (error.message.includes('Network')) { - // Retry with exponential backoff - await retryWithBackoff(() => fetchBTCPriceVAA()); - } else { - // Handle other errors - throw error; - } -} -``` - -### Gas optimization - -Batch multiple price updates when possible: - -```clarity -(define-public (update-multiple-prices - (btc-vaa (buff 8192)) - (eth-vaa (buff 8192)) - (stx-vaa (buff 8192))) - (let ((all-vaas (concat btc-vaa (concat eth-vaa stx-vaa)))) - (contract-call? PYTH-ORACLE verify-and-update-price-feeds all-vaas params))) -``` - -## Troubleshooting - -### Common issues - - - - VAA verification fails - - Ensure you're fetching VAA data with `binary: true` option and converting from base64 to hex correctly. The VAA must be recent (typically within 5 minutes). - - - - - Price calculations are incorrect - - Check that you're handling the exponent correctly. Pyth uses fixed-point representation where the actual price = raw_price * 10^exponent. For negative exponents, divide by 10^(-exponent). - - - - - Transaction runs out of gas - - Oracle updates can be gas-intensive. Ensure your gas limits account for both the oracle update and your contract logic. Consider caching prices when multiple operations need the same price. - - - - -## Security considerations - -1. **Price manipulation**: Always use confidence intervals and implement sanity checks -2. **Front-running**: Consider using commit-reveal schemes for price-sensitive operations -3. **Oracle fees**: Set appropriate post-conditions to limit fee exposure -4. **Staleness**: Reject prices older than your security threshold - -## Quick reference - -### Contract addresses - -| Network | Contract | Address | -|---------|----------|---------| -| Mainnet | pyth-oracle-v3 | `SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3` | -| Mainnet | pyth-storage-v3 | `SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3` | -| Testnet | pyth-oracle-v3 | `ST3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3` | - -### Price feed IDs - -| Asset | Feed ID | -|-------|---------| -| BTC/USD | `0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43` | -| STX/USD | `0xec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c17` | -| ETH/USD | `0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace` | -| USDC/USD | `0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a` | - -## Additional resources - -- [Pyth Network documentation](https://docs.pyth.network) -- [Trust Machines Pyth integration](https://github.com/Trust-Machines/stacks-pyth-bridge) -- [Wormhole VAA specification](https://wormhole.com/docs/protocol/infrastructure/vaas/) - -## Next steps - -:::next-steps -- [Deep dive on Clarity](https://docs.stacks.co/reference): Advanced oracle patterns and optimizations -- [Deep dive on frontend](https://docs.stacks.co/reference): Building production-ready oracle UIs -::: diff --git a/content/docs/en/resources/snippets/build-a-stx-pc.mdx b/content/docs/en/resources/snippets/build-a-stx-pc.mdx deleted file mode 100644 index 901d62c43..000000000 --- a/content/docs/en/resources/snippets/build-a-stx-pc.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Build a STX post-condition -description: A helper function that creates a post-condition for STX token transfers using the Pc builder class, ensuring exact amounts are transferred as expected. -full: true ---- - -```typescript -import { - AnchorMode, - broadcastTransaction, - makeSTXTokenTransfer, - Pc, - PostConditionMode, -} from "@stacks/transactions"; - -// Create a post-condition that ensures exactly 10 STX is sent -const pc = Pc.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM") - .willSendEq(10000000) // 10 STX in micro-STX - .ustx(); - -const txOptions = { - recipient: "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5", - amount: 10000000, - senderKey: "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601", - network: "testnet", - postConditions: [pc], - postConditionMode: PostConditionMode.Deny, - anchorMode: AnchorMode.Any, -}; - -const transaction = await makeSTXTokenTransfer(txOptions); -const broadcastResponse = await broadcastTransaction(transaction); -console.log("Transaction ID:", broadcastResponse.txid); -``` - -## Use cases - -- Securing STX token transfers with transfer amount validation -- Protecting users from unexpected token transfers -- Ensuring contract interactions behave as expected - -## Key concepts - -The `Pc` builder provides a fluent interface for creating post-conditions: - -- `Pc.principal()` - Specify the principal that will send tokens -- `.willSendEq()` - Ensure exactly this amount is sent (also supports `willSendGte`, `willSendLte`, `willSendGt`, `willSendLt`) -- `.ustx()` - Specify the token type (micro-STX) \ No newline at end of file diff --git a/content/docs/en/resources/snippets/build-an-ft-pc.mdx b/content/docs/en/resources/snippets/build-an-ft-pc.mdx deleted file mode 100644 index d864ec769..000000000 --- a/content/docs/en/resources/snippets/build-an-ft-pc.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Build an FT post-condition -description: Create post-conditions for fungible token transfers to ensure exact amounts are transferred as expected ---- - -```typescript -import { Pc } from '@stacks/transactions'; - -// Create a post-condition for fungible token transfers -const postCondition = Pc.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM") - .willSendGte(500) // Amount in token's smallest unit - .ft("ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE.token-ft", "my-token"); - -// Use in transaction options -const txOptions = { - // ... other transaction options - postConditions: [postCondition], - postConditionMode: PostConditionMode.Deny, -}; -``` - -## Use cases - -- Securing fungible token transfers with amount validation -- Protecting users from unexpected token transfers in DeFi protocols -- Ensuring token swaps happen with expected amounts - -## Key concepts - -The `Pc` builder for fungible tokens accepts: - -- `.ft()` - Takes two parameters: - - Contract address with asset name (e.g., `"contract.asset-name"`) - - Token name as defined in the contract \ No newline at end of file diff --git a/content/docs/en/resources/snippets/build-an-nft-pc.mdx b/content/docs/en/resources/snippets/build-an-nft-pc.mdx deleted file mode 100644 index cb8a42fe0..000000000 --- a/content/docs/en/resources/snippets/build-an-nft-pc.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Build an NFT post-condition -description: Create post-conditions for NFT transfers to ensure specific tokens are or aren't transferred ---- - -```typescript -import { Pc, Cl } from '@stacks/transactions'; - -// Ensure a specific NFT will be sent -const sendPC = Pc.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM") - .willSendAsset() - .nft('ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE.cool-nfts::nft-token', Cl.uint(42)); - -// Ensure a specific NFT will NOT be sent -const keepPC = Pc.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM") - .willNotSendAsset() - .nft('ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE.cool-nfts::nft-token', Cl.uint(1)); -``` - -## Use cases - -- Protecting valuable NFTs from accidental transfers -- Ensuring specific NFTs are transferred in marketplace transactions -- Safeguarding NFT collections during contract interactions - -## Key concepts - -NFT post-conditions use the `.nft()` method which requires: - -- **Asset identifier**: Contract address + asset name with `::` separator -- **Token ID**: The specific NFT ID as a Clarity value (using `Cl.uint()`) \ No newline at end of file diff --git a/content/docs/en/resources/snippets/build-an-unsigned-tx.mdx b/content/docs/en/resources/snippets/build-an-unsigned-tx.mdx deleted file mode 100644 index 726d1571c..000000000 --- a/content/docs/en/resources/snippets/build-an-unsigned-tx.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: Build an unsigned transaction -description: Create unsigned transactions for hardware wallets or multi-signature scenarios ---- - -```typescript -import { getPublicKeyFromPrivate } from "@stacks/encryption"; -import { - makeUnsignedSTXTokenTransfer, - makeUnsignedContractCall, - Cl, - AnchorMode, - PostConditionMode -} from "@stacks/transactions"; -import { STACKS_TESTNET } from "@stacks/network"; - -// Get public key from private key -const privateKey = "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601"; -const publicKey = getPublicKeyFromPrivate(privateKey); - -// Build unsigned STX transfer -const unsignedTx = await makeUnsignedSTXTokenTransfer({ - recipient: "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5", - amount: 1000000n, // 1 STX in micro-STX - fee: 200n, - nonce: 0n, - network: STACKS_TESTNET, - memo: "Test transfer", - publicKey, - anchorMode: AnchorMode.Any, - postConditionMode: PostConditionMode.Deny, -}); - -// Transaction is ready for external signing -console.log("Unsigned transaction created:", unsignedTx.txid()); -``` - -## Use cases - -- Hardware wallet integration (Ledger, Trezor) -- Multi-signature wallet transactions -- Offline transaction signing -- Secure key management systems - -## Key concepts - -Unsigned transactions separate transaction creation from signing: - -- **Public key only**: No private key needed for creation -- **External signing**: Sign with hardware wallet or secure enclave -- **Serialization**: Can be transported and signed elsewhere \ No newline at end of file diff --git a/content/docs/en/resources/snippets/check-for-duplicates.mdx b/content/docs/en/resources/snippets/check-for-duplicates.mdx deleted file mode 100644 index 9f6676760..000000000 --- a/content/docs/en/resources/snippets/check-for-duplicates.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: Check for duplicates -description: Detect duplicate characters in strings and duplicate items in lists using Clarity ---- - -```clarity -;; Check for duplicate characters in a string -(define-read-only (has-duplicate-chars? (input (string-ascii 200))) - (is-none (fold dup input (slice? (concat input "|END") u1 (+ (len input) u4)))) -) - -(define-private (dup (ch (string-ascii 1)) (out (optional (string-ascii 204)))) - (match out out_some - (match (index-of? (unwrap-panic (slice? out_some u0 (- (len out_some) u4))) ch) - found none - (slice? out_some u1 (len out_some)) - ) - out - ) -) - -;; Example usage -(has-duplicate-chars? "hello") ;; Returns true (duplicate 'l') -(has-duplicate-chars? "world") ;; Returns false (no duplicates) -``` - -## Use cases - -- Validating usernames for uniqueness of characters -- Checking NFT trait uniqueness in collections -- Preventing duplicate entries in voting systems -- Ensuring unique identifiers in lists - -## Key concepts - -The duplicate detection uses different strategies: - -- **Strings**: Uses `fold` with `index-of?` to find repeated characters -- **Lists**: Checks if elements appear again in the remaining list -- **Optimization**: Early exit on first duplicate found \ No newline at end of file diff --git a/content/docs/en/resources/snippets/convert-btc-to-stx-address.mdx b/content/docs/en/resources/snippets/convert-btc-to-stx-address.mdx deleted file mode 100644 index 0c9082014..000000000 --- a/content/docs/en/resources/snippets/convert-btc-to-stx-address.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Convert BTC to STX address -description: Convert Bitcoin addresses to their corresponding Stacks addresses using base58 decoding in Clarity ---- - -```clarity -(define-read-only (btc-to-stx (input (string-ascii 60))) - (let ( - ;; Decode base58 string to numbers - (b58-numbers (map unwrap-uint (filter is-some-uint (map b58-to-uint input)))) - ;; Validate all characters are valid base58 - (t1 (asserts! (>= (len b58-numbers) (len input)) ERR_INVALID_CHAR)) - ;; Count leading '1's (zeros in base58) - (leading-ones-count (default-to (len input) (index-of? (map is-zero b58-numbers) false))) - ;; Convert to bytes - (decoded (concat (fold decode-outer to-decode LST) leading-zeros)) - (decoded-hex (fold to-hex-rev decoded 0x)) - ;; Verify checksum - (actual-checksum (unwrap-panic (slice? (sha256 (sha256 (unwrap-panic (slice? decoded-hex u0 (- decoded-hex-len u4))))) u0 u4))) - (expected-checksum (unwrap-panic (slice? decoded-hex (- decoded-hex-len u4) decoded-hex-len))) - (t3 (asserts! (is-eq actual-checksum expected-checksum) ERR_BAD_CHECKSUM)) - ;; Extract version and construct principal - (version (unwrap-panic (element-at? STX_VER (unwrap! (index-of? BTC_VER (unwrap-panic (element-at? decoded-hex u0))) ERR_INVALID_VERSION)))) - ) - (principal-construct? version (unwrap-panic (as-max-len? (unwrap-panic (slice? decoded-hex u1 (- decoded-hex-len u4))) u20))) - ) -) - -;; Example usage -(btc-to-stx "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa") ;; Returns Stacks address -``` - -## Use cases - -- Cross-chain address mapping for Bitcoin-Stacks bridges -- Verifying ownership across both chains -- Converting legacy Bitcoin addresses to Stacks format -- Building cross-chain authentication systems - -## Key concepts - -The conversion process involves: - -1. **Base58 decoding**: Bitcoin addresses use base58 encoding -2. **Checksum verification**: Last 4 bytes are a double SHA-256 checksum -3. **Version mapping**: Bitcoin version bytes map to Stacks version bytes -4. **Principal construction**: Build Stacks principal from decoded data \ No newline at end of file diff --git a/content/docs/en/resources/snippets/convert-string-to-principal.mdx b/content/docs/en/resources/snippets/convert-string-to-principal.mdx deleted file mode 100644 index f10fa90a5..000000000 --- a/content/docs/en/resources/snippets/convert-string-to-principal.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Convert string to principal -description: Parse string addresses into principal types using c32 decoding in Clarity ---- - -```clarity -(define-read-only (string-to-principal? (input (string-ascii 82))) - (let ( - ;; Find the dot separator for contract addresses - (dot (default-to (len input) (index-of? input "."))) - ;; Extract address part (skip first char which is version) - (addr (unwrap! (slice? input u1 dot) ERR_INVALID_LENGTH)) - ;; Decode c32 characters to numbers - (addressc32 (map unwrap-panic-uint (filter is-some-uint (map c32-index addr)))) - ;; Validate all characters are valid c32 - (isValidChars (asserts! (is-eq (len addr) (len addressc32)) ERR_INVALID_CHAR)) - ;; Extract version and decode address data - (version (unwrap-panic (element-at? addressc32 u0))) - (decoded (decode-address addressc32)) - ;; Verify checksum - (checksum (verify-checksum decoded version)) - ) - ;; Construct principal with or without contract name - (match (slice? input (+ u1 dot) (len input)) contract - (principal-construct? (to-byte version) (get-address-bytes decoded) contract) - (principal-construct? (to-byte version) (get-address-bytes decoded)) - ) - ) -) - -;; Example usage -(string-to-principal? "SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7") -;; Returns (some SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7) - -(string-to-principal? "SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7.my-contract") -;; Returns (some SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7.my-contract) -``` - -## Use cases - -- Parsing user input addresses in contracts -- Converting stored string addresses to principals -- Validating address formats before use -- Building dynamic contract calls with string inputs - -## Key concepts - -Stacks addresses use c32 encoding: - -- **c32 alphabet**: `0123456789ABCDEFGHJKMNPQRSTVWXYZ` (no I, L, O, U) -- **Checksum**: Last 4 bytes verify address integrity -- **Version byte**: First character indicates address type -- **Contract addresses**: Include `.contract-name` suffix \ No newline at end of file diff --git a/content/docs/en/resources/snippets/create-a-random-burn-address.mdx b/content/docs/en/resources/snippets/create-a-random-burn-address.mdx deleted file mode 100644 index 41a330a82..000000000 --- a/content/docs/en/resources/snippets/create-a-random-burn-address.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Create a random burn address -description: Generate burn addresses for permanently removing tokens from circulation ---- - -```clarity -(define-read-only (generate-burn-address (entropy (string-ascii 40))) - (let ( - ;; Hash the entropy to create address bytes - (hash-bytes (hash160 (unwrap-panic (to-consensus-buff? entropy)))) - ;; Use version byte for current network - (version-byte (if is-in-mainnet 0x16 0x1a)) - ) - ;; Construct a valid principal that no one controls - (principal-construct? version-byte hash-bytes) - ) -) - -;; Example: Generate unique burn address -(generate-burn-address "BURN-2024-01-15-PROJECT-XYZ") -;; Returns: (ok SP1FJPSG7V4QMA7D4XVPZ3B2HQ8GY3EK8GC0NGNT3) -``` - -## Use cases - -- Token burning mechanisms -- Proof-of-burn implementations -- Creating unspendable addresses for protocol fees -- Deflationary token economics - -## Key concepts - -Burn addresses are valid principals that: - -- **No private key**: Generated from arbitrary data, not a key pair -- **Verifiable**: Anyone can verify tokens sent to these addresses -- **Unique**: Different entropy creates different addresses -- **Permanent**: Funds sent are irretrievably lost \ No newline at end of file diff --git a/content/docs/en/resources/snippets/create-a-sponsored-tx.mdx b/content/docs/en/resources/snippets/create-a-sponsored-tx.mdx deleted file mode 100644 index 707c097c1..000000000 --- a/content/docs/en/resources/snippets/create-a-sponsored-tx.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Create a sponsored transaction -description: Build transactions where a sponsor pays the fees on behalf of users ---- - -```typescript -import { STACKS_TESTNET } from "@stacks/network"; -import { bytesToHex } from "@stacks/common"; -import { - broadcastTransaction, - deserializeTransaction, - makeContractCall, - sponsorTransaction, - BufferReader, - AnchorMode, - Cl, -} from "@stacks/transactions"; - -// Step 1: User creates the transaction with sponsored flag -const userTxOptions = { - contractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", - contractName: "my-contract", - functionName: "my-function", - functionArgs: [Cl.uint(123)], - fee: 0, // User doesn't pay fees - senderKey: "b244296d5907de9864c0b0d51f98a13c52890be0404e83f273144cd5b9960eed01", - network: STACKS_TESTNET, - sponsored: true, // Mark as sponsored - anchorMode: AnchorMode.Any, -}; - -const transaction = await makeContractCall(userTxOptions); -const serializedTx = bytesToHex(transaction.serialize()); - -// Step 2: Send serialized transaction to sponsor -// (In practice, this would be sent to a sponsorship service) - -// Step 3: Sponsor signs and pays fees -const sponsorKey = "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601"; -const deserializedTx = deserializeTransaction(serializedTx); -const sponsoredTx = await sponsorTransaction({ - transaction: deserializedTx, - sponsorPrivateKey: sponsorKey, - fee: 1000, // Sponsor pays the fee - sponsorNonce: 0, -}); - -// Step 4: Broadcast the sponsored transaction -const broadcastResponse = await broadcastTransaction({ - transaction: sponsoredTx, - network: STACKS_TESTNET, -}); - -console.log("Sponsored transaction ID:", broadcastResponse.txid); -``` - -## Use cases - -- Onboarding new users without STX for fees -- Subsidizing transaction costs for dApp users -- Enterprise applications paying for user transactions -- Gaming applications with seamless user experience - -## Key concepts - -Sponsored transactions have two parties: - -- **User**: Creates and signs the transaction with `sponsored: true` -- **Sponsor**: Pays the fees and broadcasts the transaction \ No newline at end of file diff --git a/content/docs/en/resources/snippets/create-sha256-hash-clarity.mdx b/content/docs/en/resources/snippets/create-sha256-hash-clarity.mdx deleted file mode 100644 index 486b85c53..000000000 --- a/content/docs/en/resources/snippets/create-sha256-hash-clarity.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Create SHA256 hash in Clarity -description: Generate SHA-256 hashes from buffer data in Clarity smart contracts ---- - -```clarity -(define-read-only (create-sha256-hash (data (buff 4096))) - (sha256 (unwrap-panic (to-consensus-buff? data))) -) - -;; Example usage -(define-read-only (hash-message (message (string-utf8 200))) - (create-sha256-hash (unwrap-panic (to-consensus-buff? message))) -) - -;; Hash a simple string -(print (hash-message u"Hello World")) -``` - -## Use cases - -- Creating unique identifiers from data -- Verifying data integrity in contracts -- Implementing commit-reveal schemes -- Building merkle trees for proofs - -## Key concepts - -The SHA-256 implementation in Clarity: - -- Takes a buffer as input (max 1MB) -- Returns a 32-byte buffer hash -- Uses `to-consensus-buff?` to ensure consistent encoding -- Produces the same hash as off-chain implementations \ No newline at end of file diff --git a/content/docs/en/resources/snippets/create-sha256-hash-stacks-js.mdx b/content/docs/en/resources/snippets/create-sha256-hash-stacks-js.mdx deleted file mode 100644 index 8a8fccd0a..000000000 --- a/content/docs/en/resources/snippets/create-sha256-hash-stacks-js.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Create SHA256 hash in Stacks.js -description: Generate SHA-256 hashes that match Clarity's hashing output ---- - -```typescript -import { sha256 } from "@noble/hashes/sha256"; -import { bytesToHex, hexToBytes, utf8ToBytes } from "@stacks/common"; -import { bufferCV, stringUtf8CV, serializeCV } from "@stacks/transactions"; - -// Hash a string (matching Clarity's sha256 output) -function hashString(text: string) { - const clarityValue = stringUtf8CV(text); - const serialized = serializeCV(clarityValue); - return bytesToHex(sha256(serialized)); -} - -// Hash hex data (matching Clarity's sha256 output) -function hashHexData(hexData: string) { - const clarityValue = bufferCV(hexToBytes(hexData)); - const serialized = serializeCV(clarityValue); - return bytesToHex(sha256(serialized)); -} - -// Example usage -const hash1 = hashString("Hello World"); -console.log("String hash:", hash1); - -const hash2 = hashHexData("0x1234567890abcdef"); -console.log("Hex hash:", hash2); -``` - -## Use cases - -- Creating deterministic identifiers -- Verifying data integrity between on-chain and off-chain -- Implementing commit-reveal schemes off-chain -- Building merkle trees compatible with Clarity - -## Key concepts - -To match Clarity's SHA-256 output: - -1. **Convert to Clarity value**: Use appropriate CV type (`stringUtf8CV`, `bufferCV`, etc.) -2. **Serialize**: Use `serializeCV` to match Clarity's encoding -3. **Hash**: Apply SHA-256 to the serialized bytes \ No newline at end of file diff --git a/content/docs/en/resources/snippets/deploy-a-contract.mdx b/content/docs/en/resources/snippets/deploy-a-contract.mdx deleted file mode 100644 index 46d29137a..000000000 --- a/content/docs/en/resources/snippets/deploy-a-contract.mdx +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Deploy a contract -description: Deploy a Clarity smart contract to the Stacks blockchain using Stacks.js ---- - -```typescript -import { STACKS_TESTNET } from "@stacks/network"; -import { - makeContractDeploy, - broadcastTransaction, - AnchorMode, - PostConditionMode -} from "@stacks/transactions"; - -const contractCode = ` -(define-data-var counter uint u0) - -(define-public (increment) - (ok (var-set counter (+ (var-get counter) u1)))) - -(define-read-only (get-counter) - (ok (var-get counter))) -`; - -const txOptions = { - contractName: "my-counter", - codeBody: contractCode, - senderKey: "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601", - network: STACKS_TESTNET, - anchorMode: AnchorMode.Any, - postConditionMode: PostConditionMode.Allow, - fee: 100000n, // Set an appropriate fee -}; - -const transaction = await makeContractDeploy(txOptions); -const broadcastResponse = await broadcastTransaction({ transaction }); -console.log("Contract deployed!"); -console.log("Transaction ID:", broadcastResponse.txid); -``` - -## Use cases - -- Deploying new smart contracts to mainnet or testnet -- Automating contract deployments in CI/CD pipelines -- Programmatic contract deployment for dApp initialization -- Deploying contract upgrades or new versions - -## Key concepts - -Contract deployment requires: - -- **Contract name**: Unique identifier for your contract (letters, numbers, hyphens) -- **Code body**: The Clarity contract code as a string -- **Sender key**: Private key of the account deploying the contract -- **Network**: Target network (mainnet, testnet, or devnet) \ No newline at end of file diff --git a/content/docs/en/resources/snippets/derive-principal-addresses-between-networks.mdx b/content/docs/en/resources/snippets/derive-principal-addresses-between-networks.mdx deleted file mode 100644 index 40f082e91..000000000 --- a/content/docs/en/resources/snippets/derive-principal-addresses-between-networks.mdx +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Derive principal addresses between networks -description: Convert addresses between mainnet and testnet by extracting and reconstructing with different version bytes ---- - -```clarity -;; Extract hash bytes from an address -(define-read-only (get-address-hash-bytes (address principal)) - (get hash-bytes (unwrap-panic (principal-destruct? address))) -) - -;; Convert testnet address to mainnet -(define-read-only (testnet-to-mainnet (testnet-address principal)) - (let ( - ;; Extract the hash bytes from testnet address - (hash-bytes (get-address-hash-bytes testnet-address)) - ;; Mainnet version byte - (mainnet-version 0x16) - ) - ;; Reconstruct with mainnet version - (principal-construct? mainnet-version hash-bytes) - ) -) - -;; Convert mainnet address to testnet -(define-read-only (mainnet-to-testnet (mainnet-address principal)) - (let ( - ;; Extract the hash bytes from mainnet address - (hash-bytes (get-address-hash-bytes mainnet-address)) - ;; Testnet version byte - (testnet-version 0x1a) - ) - ;; Reconstruct with testnet version - (principal-construct? testnet-version hash-bytes) - ) -) - -;; Example usage -(testnet-to-mainnet 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM) -;; Returns: (ok SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R) -``` - -## Use cases - -- Cross-network address verification -- Building multi-network dApps -- Address validation tools -- Network migration utilities - -## Key concepts - -Stacks addresses consist of: - -- **Version byte**: Indicates network and address type -- **Hash bytes**: 20-byte hash of the public key -- **Checksum**: Built into the c32 encoding - -### Version bytes reference - -| Version | Network | Type | Prefix | -|---------|---------|------|--------| -| 0x16 | Mainnet | Standard | SP | -| 0x17 | Mainnet | Contract | SM | -| 0x1a | Testnet | Standard | ST | -| 0x1b | Testnet | Contract | SN | \ No newline at end of file diff --git a/content/docs/en/resources/snippets/derive-stacks-address-from-keys.mdx b/content/docs/en/resources/snippets/derive-stacks-address-from-keys.mdx deleted file mode 100644 index 1e0d76b91..000000000 --- a/content/docs/en/resources/snippets/derive-stacks-address-from-keys.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: Derive Stacks address from keys -description: Generate Stacks addresses from private or public keys using multiple methods ---- - -```typescript -import { getPublicKeyFromPrivate } from "@stacks/encryption"; -import { - getAddressFromPrivateKey, - getAddressFromPublicKey -} from "@stacks/transactions"; - -// Derive address from private key -const privateKey = process.env.PRIVATE_KEY; // Keep this secret! -const addressFromPrivate = getAddressFromPrivateKey(privateKey, "testnet"); - -// Derive public key and address -const publicKey = getPublicKeyFromPrivate(privateKey); -const addressFromPublic = getAddressFromPublicKey(publicKey, "testnet"); - -console.log("Address:", addressFromPrivate); -console.log("Public key:", publicKey); -console.log("Same address:", addressFromPrivate === addressFromPublic); // true -``` - -## Use cases - -- Wallet address generation -- Key pair validation -- Address recovery from backup keys -- Multi-signature wallet setup - -## Key concepts - -Stacks addresses are derived through: - -- **Private key**: 32-byte random number (keep secret!) -- **Public key**: Derived from private key using ECDSA -- **Address**: Base58check-encoded hash of public key -- **Network**: Different prefixes for mainnet (SP/SM) vs testnet (ST/SN) \ No newline at end of file diff --git a/content/docs/en/resources/snippets/fetch-testnet-bitcoin-on-regtest.mdx b/content/docs/en/resources/snippets/fetch-testnet-bitcoin-on-regtest.mdx deleted file mode 100644 index d1f587924..000000000 --- a/content/docs/en/resources/snippets/fetch-testnet-bitcoin-on-regtest.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Fetch testnet Bitcoin on regtest -description: Request testnet BTC from the Hiro faucet for local development and testing ---- - -```typescript -const TESTNET_ADDRESS = 'bcrt1q728h29ejjttmkupwdkyu2x4zcmkuc3q29gvwaa'; - -try { - const response = await fetch( - `https://api.testnet.hiro.so/extended/v1/faucets/btc?address=${TESTNET_ADDRESS}`, - { - method: 'POST', - headers: { - "Content-Type": "application/json", - }, - } - ); - - const result = await response.json(); - console.log("Faucet response:", result); - - if (result.success) { - console.log(`Transaction ID: ${result.txid}`); - console.log(`Amount sent: ${result.amount} sats`); - } -} catch (error) { - console.error("Faucet request failed:", error); -} -``` - -## Use cases - -- Local development with Bitcoin transactions -- Testing sBTC operations -- Integration testing for cross-chain applications -- Developing Bitcoin-aware smart contracts - -## Key concepts - -The Hiro testnet faucet: - -- **Rate limited**: One request per address per hour -- **Amount**: Sends 0.5 testnet BTC per request -- **Network**: Works with Bitcoin testnet/regtest addresses -- **Format**: Supports legacy, segwit, and taproot addresses \ No newline at end of file diff --git a/content/docs/en/resources/snippets/filter-items-from-a-list.mdx b/content/docs/en/resources/snippets/filter-items-from-a-list.mdx deleted file mode 100644 index 9821760a7..000000000 --- a/content/docs/en/resources/snippets/filter-items-from-a-list.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: Filter items from a list -description: Remove specific items from lists using fold in Clarity ---- - -```clarity -(define-read-only (filter-item (l (list 100 uint)) (remove uint)) - (get newList (fold remove-value l { compareTo: remove, newList: (list) })) -) - -(define-private (remove-value (listValue uint) (trackerTuple { compareTo: uint, newList: (list 100 uint) })) - (merge trackerTuple {newList: - (if (is-eq listValue (get compareTo trackerTuple)) - (get newList trackerTuple) - (unwrap-panic (as-max-len? (append (get newList trackerTuple) listValue) u100)) - ) - }) -) - -;; Example usage -(filter-item (list u1 u2 u3 u2 u4) u2) ;; Returns (u1 u3 u4) -``` - -## Use cases - -- Removing blacklisted addresses from access lists -- Filtering out completed tasks from todo lists -- Excluding specific tokens from portfolios -- Data cleanup in smart contracts - -## Key concepts - -This pattern uses `fold` to iterate through a list and build a new list: - -- **Accumulator**: Tracks the value to remove and builds the new list -- **Conditional append**: Only adds items that don't match the filter -- **Type safety**: Maintains list type and maximum length \ No newline at end of file diff --git a/content/docs/en/resources/snippets/generate-a-secret-key.mdx b/content/docs/en/resources/snippets/generate-a-secret-key.mdx deleted file mode 100644 index 46c5697ad..000000000 --- a/content/docs/en/resources/snippets/generate-a-secret-key.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Generate a secret key -description: Create mnemonic seed phrases for wallet generation ---- - -```typescript -import { generateSecretKey } from '@stacks/wallet-sdk'; - -// Generate a 24-word mnemonic (256 bits of entropy) -const mnemonic24 = generateSecretKey(); -// Example: "aunt birth lounge misery utility blind holiday walnut fuel make gift parent gap picnic exact various express sphere family nerve oil drill engage youth" - -// Generate a 12-word mnemonic (128 bits of entropy) -const mnemonic12 = generateSecretKey(128); -// Example: "winter crash infant long upset beauty cram tank short remain obtain sauce" -``` - -## Use cases - -- Creating new wallet seed phrases -- Generating secure entropy for applications -- Building wallet creation flows -- Testing wallet functionality - -## Key concepts - -Mnemonic seed phrases follow the BIP39 standard: - -- **Entropy**: Random data used to generate the phrase -- **Word count**: 12 words (128 bits) or 24 words (256 bits) -- **Word list**: Standardized list of 2048 words -- **Checksum**: Built-in error detection \ No newline at end of file diff --git a/content/docs/en/resources/snippets/generate-a-wallet.mdx b/content/docs/en/resources/snippets/generate-a-wallet.mdx deleted file mode 100644 index f203caeca..000000000 --- a/content/docs/en/resources/snippets/generate-a-wallet.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Generate a wallet -description: Create a new wallet with mnemonic phrase or restore from existing seed ---- - -```typescript -import { generateWallet, generateSecretKey } from '@stacks/wallet-sdk'; - -// Generate a new wallet with a new 24-word seed phrase -const secretKey = generateSecretKey(256); // 256 bits = 24 words -const wallet = await generateWallet({ - secretKey, - password: 'your-secure-password', -}); - -// Access the first account -const account = wallet.accounts[0]; -console.log('Address:', account.stxAddress); -console.log('Mnemonic:', secretKey); -``` - -## Use cases - -- Creating new wallets for users -- Restoring wallets from seed phrases -- Generating deterministic wallet addresses -- Building wallet applications - -## Key concepts - -The wallet SDK generates hierarchical deterministic (HD) wallets following BIP32/BIP39 standards: - -- **Secret key**: Can be a mnemonic phrase or private key -- **Password**: Encrypts the wallet (different from mnemonic passphrase) -- **Accounts**: Multiple accounts can be derived from one seed \ No newline at end of file diff --git a/content/docs/en/resources/snippets/generate-random-number.mdx b/content/docs/en/resources/snippets/generate-random-number.mdx deleted file mode 100644 index 46333a203..000000000 --- a/content/docs/en/resources/snippets/generate-random-number.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Generate random number -description: Create pseudo-random numbers using block hashes for randomness in smart contracts ---- - -```clarity -(define-read-only (generate-random (block-height uint)) - (let ( - ;; Get block header hash - (block-hash (unwrap! (get-stacks-block-info? id-header-hash block-height) (err u1001))) - ;; Take a slice of the hash for randomness - (hash-slice (unwrap-panic (slice? block-hash u16 u32))) - ;; Convert to uint - (random-value (buff-to-uint-be (unwrap-panic (as-max-len? hash-slice u16)))) - ) - (ok random-value) - ) -) - -;; Generate random number in range -(define-read-only (random-in-range (block-height uint) (min uint) (max uint)) - (let ( - (random (try! (generate-random block-height))) - (range (- max min)) - ) - (ok (+ min (mod random (+ u1 range)))) - ) -) - -;; Example: Random between 1-100 -(random-in-range block-height u1 u100) -``` - -## Use cases - -- Lottery and gaming contracts -- Random NFT trait generation -- Fair distribution mechanisms -- Random selection from lists - -## Key concepts - -Blockchain randomness is deterministic but unpredictable: - -- **Block hashes**: Use historical block data as entropy source -- **Future blocks**: Cannot predict future block hashes -- **Commitment schemes**: Combine with commit-reveal for fairness \ No newline at end of file diff --git a/content/docs/en/resources/snippets/get-account-details-from-wallet.mdx b/content/docs/en/resources/snippets/get-account-details-from-wallet.mdx deleted file mode 100644 index cea0214e7..000000000 --- a/content/docs/en/resources/snippets/get-account-details-from-wallet.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Get account details from wallet -description: Extract addresses, keys, and other account information from a wallet instance ---- - -```typescript -import { STACKS_TESTNET, STACKS_MAINNET } from "@stacks/network"; -import { generateWallet, getStxAddress } from "@stacks/wallet-sdk"; - -// Generate or restore wallet -const wallet = await generateWallet({ - secretKey: "your twenty four word mnemonic phrase goes here...", - password: "wallet-password", -}); - -// Get first account -const account = wallet.accounts[0]; - -// Get addresses for different networks -const testnetAddress = getStxAddress({ - account, - transactionVersion: 0 // testnet -}); - -const mainnetAddress = getStxAddress({ - account, - transactionVersion: 1 // mainnet -}); - -// Get keys -const privateKey = account.stxPrivateKey; -const publicKey = account.stxPublicKey; - -console.log("Testnet address:", testnetAddress); -console.log("Mainnet address:", mainnetAddress); -console.log("Public key:", publicKey); -``` - -## Use cases - -- Displaying user addresses in wallet UIs -- Getting private keys for transaction signing -- Deriving addresses for different networks -- Building wallet management tools - -## Key concepts - -Wallet accounts contain: - -- **Private key**: Used for signing transactions -- **Public key**: Derived from private key -- **Addresses**: Network-specific (mainnet vs testnet) -- **Derivation path**: BIP44 path used to generate the account \ No newline at end of file diff --git a/content/docs/en/resources/snippets/helper-function-to-restrict-contract-calls.mdx b/content/docs/en/resources/snippets/helper-function-to-restrict-contract-calls.mdx deleted file mode 100644 index c70bd1422..000000000 --- a/content/docs/en/resources/snippets/helper-function-to-restrict-contract-calls.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: Helper function to restrict contract calls -description: Implement access control to ensure functions can only be called by users, not other contracts ---- - -```clarity -;; Check if caller is a standard principal (user wallet) -(define-private (is-standard-principal-call) - (is-none (get name (unwrap! (principal-destruct? contract-caller) false))) -) - -;; Public function restricted to direct user calls -(define-public (user-only-function (amount uint)) - (begin - (asserts! (is-standard-principal-call) (err u401)) - ;; Function logic here - (ok true) - ) -) -``` - -## Use cases - -- Preventing contract-to-contract reentrancy attacks -- Ensuring human-initiated transactions for governance -- Restricting token minting to direct user actions -- Protecting admin functions from automated calls - -## Key concepts - -Principal types in Clarity: - -- **Standard principals**: User wallets (SP/ST addresses) -- **Contract principals**: Deployed contracts (address.contract-name) -- **contract-caller**: The immediate caller of the current function -- **tx-sender**: The original transaction initiator \ No newline at end of file diff --git a/content/docs/en/resources/snippets/index.mdx b/content/docs/en/resources/snippets/index.mdx deleted file mode 100644 index a0e8900bf..000000000 --- a/content/docs/en/resources/snippets/index.mdx +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: Snippets -sidebarTitle: Overview -description: Ready-to-use code examples for building on Stacks and Bitcoin -llm: false ---- - -## Overview - -To explore Snippets with AI, copy and paste [llms.txt](/resources/snippets/llms.txt) into your LLM of choice. - -## Transactions - -### Token Transfers - -- [Transfer STX](/resources/snippets/transfer-stx) - Send STX tokens between addresses -- [Transfer a SIP-10 token](/resources/snippets/transfer-a-sip10-token) - Transfer fungible tokens - -### Transaction Building - -- [Build an unsigned transaction](/resources/snippets/build-an-unsigned-tx) - Create transactions for hardware wallets -- [Create a sponsored transaction](/resources/snippets/create-a-sponsored-tx) - Pay fees on behalf of users - -### Post-Conditions - -- [Build a STX post-condition](/resources/snippets/build-a-stx-pc) - Secure STX transfers -- [Build an FT post-condition](/resources/snippets/build-an-ft-pc) - Secure fungible token transfers -- [Build an NFT post-condition](/resources/snippets/build-an-nft-pc) - Secure NFT transfers - -## Smart Contracts - -### Contract Deployment - -- [Deploy a contract](/resources/snippets/deploy-a-contract) - Deploy contracts to the blockchain - -### Clarity Functions - -- [Create SHA256 hash](/resources/snippets/create-sha256-hash-clarity) - Generate hashes in Clarity -- [Filter items from a list](/resources/snippets/filter-items-from-a-list) - List manipulation in Clarity -- [Generate random number](/resources/snippets/generate-random-number) - Pseudo-random numbers in contracts -- [Check for duplicates](/resources/snippets/check-for-duplicates) - Find duplicate items in lists -- [Return an entry from a map](/resources/snippets/return-an-entry-from-a-map) - Access map data structures -- [Helper function to restrict contract calls](/resources/snippets/helper-function-to-restrict-contract-calls) - Access control patterns - -## Accounts & Addresses - -### Wallet Management - -- [Generate a wallet](/resources/snippets/generate-a-wallet) - Create new wallet with mnemonic -- [Generate a secret key](/resources/snippets/generate-a-secret-key) - Create private keys -- [Get account details from wallet](/resources/snippets/get-account-details-from-wallet) - Extract account information - -### Address Utilities - -- [Convert BTC to STX address](/resources/snippets/convert-btc-to-stx-address) - Cross-chain address conversion -- [Convert string to principal](/resources/snippets/convert-string-to-principal) - Parse principal addresses -- [Derive Stacks address from keys](/resources/snippets/derive-stacks-address-from-keys) - Generate addresses from key pairs -- [Derive principal addresses between networks](/resources/snippets/derive-principal-addresses-between-networks) - Network address mapping -- [Create a random burn address](/resources/snippets/create-a-random-burn-address) - Generate burn addresses - -## Cryptography & Security - -### Hashing - -- [Create SHA256 hash (Stacks.js)](/resources/snippets/create-sha256-hash-stacks-js) - Generate hashes in JavaScript -- [Create SHA256 hash (Clarity)](/resources/snippets/create-sha256-hash-clarity) - Generate hashes in smart contracts - -### API Integration - -- [Integrate API keys using Stacks.js](/resources/snippets/integrate-api-keys-using-stacksjs) - Secure API key usage - -## Development Tools - -### Testing - -- [Fetch testnet Bitcoin on regtest](/resources/snippets/fetch-testnet-bitcoin-on-regtest) - Get test BTC for development \ No newline at end of file diff --git a/content/docs/en/resources/snippets/integrate-api-keys-using-stacksjs.mdx b/content/docs/en/resources/snippets/integrate-api-keys-using-stacksjs.mdx deleted file mode 100644 index 2307698cd..000000000 --- a/content/docs/en/resources/snippets/integrate-api-keys-using-stacksjs.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Integrate API keys using Stacks.js -description: Configure Stacks.js to use API keys for enhanced rate limits and monitoring ---- - -```typescript -import { createApiKeyMiddleware, createFetchFn } from "@stacks/common"; -import { StacksMainnet, StacksTestnet } from "@stacks/network"; - -// Create middleware with your API key -const apiMiddleware = createApiKeyMiddleware({ - apiKey: process.env.HIRO_API_KEY -}); - -// Create custom fetch function -const customFetch = createFetchFn(apiMiddleware); - -// Configure network with API key -const network = new StacksMainnet({ - fetchFn: customFetch -}); -``` - -## Use cases - -- Increased API rate limits for production applications -- API usage monitoring and analytics -- Priority access during high traffic periods -- Custom enterprise support features - -## Key concepts - -API key benefits: - -- **Higher rate limits**: 500 requests/minute vs 50 for anonymous -- **Usage tracking**: Monitor your API consumption -- **Priority queue**: Better performance during peak times -- **Support**: Access to dedicated support channels \ No newline at end of file diff --git a/content/docs/en/resources/snippets/meta.json b/content/docs/en/resources/snippets/meta.json deleted file mode 100644 index 6c1e693f1..000000000 --- a/content/docs/en/resources/snippets/meta.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "title": "Snippets", - "root": true, - "pages": [ - "---Snippets---", - "index", - "---Transactions---", - "transfer-stx", - "transfer-a-sip10-token", - "build-an-unsigned-tx", - "create-a-sponsored-tx", - "build-a-stx-pc", - "build-an-ft-pc", - "build-an-nft-pc", - "---Smart Contracts---", - "deploy-a-contract", - "create-sha256-hash-clarity", - "filter-items-from-a-list", - "generate-random-number", - "check-for-duplicates", - "return-an-entry-from-a-map", - "helper-function-to-restrict-contract-calls", - "---Accounts & Addresses---", - "generate-a-wallet", - "generate-a-secret-key", - "get-account-details-from-wallet", - "convert-btc-to-stx-address", - "convert-string-to-principal", - "derive-stacks-address-from-keys", - "derive-principal-addresses-between-networks", - "create-a-random-burn-address", - "---Cryptography & Security---", - "create-sha256-hash-stacks-js", - "integrate-api-keys-using-stacksjs", - "---Development Tools---", - "fetch-testnet-bitcoin-on-regtest" - ] -} diff --git a/content/docs/en/resources/snippets/return-an-entry-from-a-map.mdx b/content/docs/en/resources/snippets/return-an-entry-from-a-map.mdx deleted file mode 100644 index b0acc93ea..000000000 --- a/content/docs/en/resources/snippets/return-an-entry-from-a-map.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Return an entry from a map -description: Query contract map data using the Stacks API map_entry endpoint ---- - -```typescript -import { Cl, cvToHex } from "@stacks/transactions"; - -// Query a map entry from a contract -const contractAddress = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM"; -const contractName = "my-contract"; -const mapName = "user-balances"; - -// Create the map key (e.g., a principal) -const mapKey = Cl.standardPrincipal("ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5"); - -const response = await fetch( - `https://api.hiro.so/v2/map_entry/${contractAddress}/${contractName}/${mapName}`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(cvToHex(mapKey)), - } -); - -const result = await response.json(); -const data = result.data ? Cl.deserialize(result.data) : null; - -console.log("Map value:", data); -``` - -## Use cases - -- Reading user balances from token contracts -- Checking NFT ownership records -- Retrieving configuration values from contracts -- Monitoring contract state without transactions - -## Key concepts - -The map_entry API: - -- **POST request**: Send the serialized map key -- **Hex encoding**: Keys must be hex-encoded Clarity values -- **Response format**: Returns hex-encoded Clarity value or null \ No newline at end of file diff --git a/content/docs/en/resources/snippets/transfer-a-sip10-token.mdx b/content/docs/en/resources/snippets/transfer-a-sip10-token.mdx deleted file mode 100644 index 4ea8d885b..000000000 --- a/content/docs/en/resources/snippets/transfer-a-sip10-token.mdx +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: Transfer a SIP-10 token -description: Transfer fungible tokens using the SIP-10 standard with post-conditions ---- - -```typescript -import { STACKS_MAINNET } from "@stacks/network"; -import { - AnchorMode, - broadcastTransaction, - Cl, - makeContractCall, - Pc, - PostConditionMode, -} from "@stacks/transactions"; - -// Token contract details -const tokenAddress = "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9"; -const tokenName = "wrapped-bitcoin"; -const contractIdentifier = `${tokenAddress}.${tokenName}`; - -// Create post-condition to ensure exact amount is transferred -const postConditions = [ - Pc.principal("SP2C20XGZBAYFZ1NYNHT1J6MGBGVX9X7X3P7LAX7K") - .willSendEq(100000000) // 1 wBTC (8 decimals) - .ft(contractIdentifier, tokenName) -]; - -const txOptions = { - contractAddress: tokenAddress, - contractName: tokenName, - functionName: "transfer", - functionArgs: [ - Cl.uint(100000000), // amount (with decimals) - Cl.principal("SP2C20XGZBAYFZ1NYNHT1J6MGBGVX9X7X3P7LAX7K"), // sender - Cl.principal("SP31DA84DWTF6510EW6DCTC3GB3XH1EEBGP7MYT2"), // recipient - Cl.none(), // optional memo - ], - senderKey: "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601", - validateWithAbi: true, - network: STACKS_MAINNET, - postConditions, - postConditionMode: PostConditionMode.Deny, - anchorMode: AnchorMode.Any, -}; - -const transaction = await makeContractCall(txOptions); -const broadcastResponse = await broadcastTransaction({ - transaction, - network: STACKS_MAINNET, -}); - -console.log("Transaction ID:", broadcastResponse.txid); -``` - -## Use cases - -- Transferring fungible tokens between wallets -- Integrating token transfers in dApps -- Building DEX or swap functionality -- Implementing payment systems with custom tokens - -## Key concepts - -SIP-10 is the fungible token standard on Stacks, similar to ERC-20: - -- **Standard interface**: All SIP-10 tokens implement `transfer`, `get-balance`, etc. -- **Post-conditions**: Protect users by ensuring exact amounts are transferred -- **Memos**: Optional field for including transfer notes \ No newline at end of file diff --git a/content/docs/en/resources/snippets/transfer-stx.mdx b/content/docs/en/resources/snippets/transfer-stx.mdx deleted file mode 100644 index d44e9718a..000000000 --- a/content/docs/en/resources/snippets/transfer-stx.mdx +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Transfer STX -description: Send STX tokens between addresses with post-conditions for secure transfers ---- - -```typescript -import { STACKS_TESTNET } from "@stacks/network"; -import { - AnchorMode, - broadcastTransaction, - makeSTXTokenTransfer, - Pc, - PostConditionMode, -} from "@stacks/transactions"; - -// Define sender and recipient -const senderAddress = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM"; -const recipientAddress = "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5"; - -// Create post-condition to ensure exact amount is sent -const postConditions = Pc.principal(senderAddress) - .willSendEq(1000000) // 1 STX in micro-STX - .ustx(); - -// Configure transaction options -const txOptions = { - recipient: recipientAddress, - amount: 1000000, // 1 STX in micro-STX - senderKey: "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601", - network: STACKS_TESTNET, - memo: "Transfer memo", // Optional memo field - postConditions: [postConditions], - postConditionMode: PostConditionMode.Deny, - anchorMode: AnchorMode.Any, -}; - -// Create and broadcast the transaction -const transaction = await makeSTXTokenTransfer(txOptions); -const broadcastResponse = await broadcastTransaction({ transaction }); -console.log("Transaction ID:", broadcastResponse.txid); -``` - -## Use cases - -- Transferring STX tokens securely between addresses -- Validating transaction amounts for secure transfers - -## Key concepts - -- **Post-conditions**: Ensure the exact amount of STX is transferred \ No newline at end of file diff --git a/content/docs/en/resources/templates/index.mdx b/content/docs/en/resources/templates/index.mdx deleted file mode 100644 index c40a0d010..000000000 --- a/content/docs/en/resources/templates/index.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: Project templates -description: Project templates for building on Stacks. -llm: false ---- - -## Project templates - diff --git a/content/docs/en/resources/templates/meta.json b/content/docs/en/resources/templates/meta.json deleted file mode 100644 index 1fe5c96f2..000000000 --- a/content/docs/en/resources/templates/meta.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title": "Project templates", - "root": true, - "pages": ["index", "..."] -} diff --git a/content/docs/en/tools/bitcoin-indexer/(indexer)/full-sync.mdx b/content/docs/en/tools/bitcoin-indexer/(indexer)/full-sync.mdx index 3d2afdeda..f9fe6a3bc 100644 --- a/content/docs/en/tools/bitcoin-indexer/(indexer)/full-sync.mdx +++ b/content/docs/en/tools/bitcoin-indexer/(indexer)/full-sync.mdx @@ -15,6 +15,8 @@ import { ArrowRight, Check } from 'lucide-react'; - Monitor indexing progress effectively ::: +## Prerequisites + :::prerequisites - Bitcoin Core node running with `txindex=1` - PostgreSQL running with databases created @@ -180,9 +182,7 @@ Test that APIs are returning data: $ curl http://localhost:3000/ordinals/v1/inscriptions/0 ``` -## Next steps - :::next-steps - [Hiro Archives](/tools/bitcoin-indexer/archive-bootstrap): Skip weeks of indexing by bootstrapping from Hiro's pre-indexed archives. - [Advanced configuration](/tools/bitcoin-indexer/configuration): Configure the Bitcoin Indexer for optimal performance and customize metaprotocol settings. -::: \ No newline at end of file +::: diff --git a/content/docs/en/tools/chainhook/(chainhook-cli)/cli-reference.mdx b/content/docs/en/tools/chainhook/(chainhook-cli)/cli-reference.mdx deleted file mode 100644 index dc2695cf8..000000000 --- a/content/docs/en/tools/chainhook/(chainhook-cli)/cli-reference.mdx +++ /dev/null @@ -1,209 +0,0 @@ ---- -title: CLI reference -sidebarTitle: CLI reference -description: Complete reference for all Chainhook CLI commands and options. ---- - -The Chainhook CLI provides tools for creating, testing, and deploying blockchain event observers. From predicate creation to service management, Chainhook streamlines your blockchain monitoring workflow. - -- Generate configuration: [`chainhook config new`](#chainhook-config) -- Create predicates: [`chainhook predicates new`](#chainhook-predicates) -- Test predicates: [`chainhook predicates scan`](#chainhook-predicates) -- Run as a service: [`chainhook service start`](#chainhook-service) -- Manage Stacks database: [`chainhook stacks db`](#chainhook-stacks) - -## Configuration management [#configuration-management] - -### chainhook config - -`chainhook config` generates configuration files. - -| Command | Description | -|---------|-------------| -| `new` | Generate new config | - -**Usage with `new`** - -```console -chainhook config new [OPTIONS] -``` - -```terminal -$ chainhook config new --mainnet -``` - -| Option | Description | -|--------|-------------| -| `--mainnet` | Target Mainnet network | -| `--testnet` | Target Testnet network | -| `--devnet` | Target Devnet network | - -## Predicate management [#predicate-management] - -### chainhook predicates - -`chainhook predicates` generates and tests predicates. - -| Command | Description | -|---------|-------------| -| `new` | Generate new predicate | -| `check` | Check given predicate | -| `scan` | Scan blocks (one-off) from specified network and apply provided predicate | - -**Usage with `new`** - -```console -chainhook predicates new [OPTIONS] -``` - -```terminal -$ chainhook predicates new my-predicate --stacks -$ chainhook predicates new bitcoin-transfers --bitcoin -``` - -| Option | Description | -|--------|-------------| -| `--stacks` | Generate a Stacks predicate | -| `--bitcoin` | Generate a Bitcoin predicate | - -**Usage with `scan`** - -```console -chainhook predicates scan [OPTIONS] -``` - -```terminal -$ chainhook predicates scan my-predicate.json --mainnet -$ chainhook predicates scan transfers.json --testnet --config-path ./Chainhook.toml -``` - -| Option | Description | -|--------|-------------| -| `--mainnet` | Target Mainnet network | -| `--testnet` | Target Testnet network | -| `--config-path ` | Load config file path | - -**Usage with `check`** - -```console -chainhook predicates check [OPTIONS] -``` - -```terminal -$ chainhook predicates check my-predicate.json --mainnet -``` - -| Option | Description | -|--------|-------------| -| `--config-path ` | Load config file path | -| `--mainnet` | Target Mainnet network | -| `--testnet` | Target Testnet network | - -## Service management [#service-management] - -### chainhook service - -`chainhook service` runs a service streaming blocks and evaluating registered predicates. - -| Command | Description | -|---------|-------------| -| `start` | Start chainhook-cli | - -**Usage with `start`** - -```console -chainhook service start [OPTIONS] -``` - -```terminal -$ chainhook service start --config-path=./Chainhook.toml -$ chainhook service start --predicate-path=./my-predicate.json --mainnet -``` - -| Option | Description | -|--------|-------------| -| `--config-path ` | Load config file path | -| `--predicate-path ` | Specify relative path of the chainhooks (yaml format) to evaluate | -| `--start-http-api` | Start REST API for managing predicates | -| `--prometheus-port ` | If provided, serves Prometheus metrics at `localhost:{port}/metrics` | -| `--mainnet` | Target Mainnet network | -| `--testnet` | Target Testnet network | -| `--devnet` | Target Devnet network | - -## Stacks integration [#stacks-integration] - -### chainhook stacks - -`chainhook stacks` provides Stacks-specific commands. - -| Command | Description | -|---------|-------------| -| `db` | Db maintenance related commands | - -**Usage with `db`** - -```console -chainhook stacks db -``` - -```terminal -$ chainhook stacks db check -$ chainhook stacks db update --config-path ./Chainhook.toml -``` - -| Subcommand | Description | -|------------|-------------| -| `check` | Check integrity | -| `drop` | Update blocks from database | -| `get` | Retrieve a block from the Stacks db | -| `get-latest` | Get latest blocks from the unconfirmed and confirmed block db | -| `unconfirm` | Deletes a block from the confirmed block db and moves it to the unconfirmed block db | -| `update` | Update database using latest Stacks archive file | - -| Option | Description | -|--------|-------------| -| `--config-path ` | Load config file path | - -## Utilities [#utilities] - -### chainhook docs - -`chainhook docs` generates documentation. - -| Command | Description | -|---------|-------------| -| `api` | Generate new documentation for the predicate registration API | - -**Usage with `api`** - -```console -chainhook docs api -``` - -```terminal -$ chainhook docs api -``` - -### chainhook help - -`chainhook help` prints help information. - -**Usage** - -```console -chainhook help [SUBCOMMAND] -``` - -```terminal -$ chainhook help -$ chainhook help predicates -``` - -## Global options [#global-options] - -These options can be used with the main command: - -| Option | Short | Description | -|--------|-------|-------------| -| `--help` | `-h` | Print help information | -| `--version` | `-V` | Print version information | \ No newline at end of file diff --git a/content/docs/en/tools/chainhook/(chainhook-cli)/event-scanning.mdx b/content/docs/en/tools/chainhook/(chainhook-cli)/event-scanning.mdx deleted file mode 100644 index 0c5687dd1..000000000 --- a/content/docs/en/tools/chainhook/(chainhook-cli)/event-scanning.mdx +++ /dev/null @@ -1,202 +0,0 @@ ---- -title: Scanning blockchain events -sidebarTitle: Scan events -description: Test predicates by scanning historical blockchain data before deploying them in production. ---- - -This guide shows you how to use Chainhook's scanning mode to test predicates against historical blockchain data. Scanning helps validate your predicate logic and understand what events will be captured before going live. - -## Basic scanning - -Test your predicate against historical data by scanning the blockchain: - -```terminal -$ chainhook predicates scan my-predicate.json --mainnet -``` - -The scan command downloads blockchain data from Hiro Archive (cached after first run) and evaluates your predicate against each block in the specified range. - -### Scan with specific networks - -```terminal -$ chainhook predicates scan predicate.json --mainnet -$ chainhook predicates scan predicate.json --testnet -$ chainhook predicates scan predicate.json --devnet -``` - -## Block range scanning - -Limit scanning to specific blocks for faster testing and targeted analysis: - -```terminal -$ chainhook predicates scan my-predicate.json \ - --start-block 150000 \ - --end-block 150100 \ - --mainnet -``` - -### Configure ranges in predicates - -```json block-range-predicate.json -{ - "chain": "stacks", - "networks": { - "mainnet": { - "start_block": 150000, - "end_block": 151000, - "if_this": { - "scope": "contract_call", - "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-dao" - } - } - } -} -``` - -This predicate scans only blocks 150,000 to 151,000, significantly reducing scan time. - -## Output configuration - -Control where scan results are written using the `then_that` section. - -### File output - -```json file-output-predicate.json -{ - "then_that": { - "file_append": { - "path": "./scan-results.json" - } - } -} -``` - -### Console output - -```json console-output-predicate.json -{ - "then_that": { - "file_append": { - "path": "-" // Writes to stdout - } - } -} -``` - -## Performance optimization - -Speed up your scans by being specific about what you're looking for. - -### Use specific scopes - -```json optimized-scope.json -{ - "if_this": { - "scope": "contract_call", - "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-dao", - "method": "collateralize-and-mint" - } -} -``` - -Targeting specific contracts and methods scans much faster than broad scopes. - -### Limit occurrences - -```json limited-occurrences.json -{ - "networks": { - "mainnet": { - "expire_after_occurrence": 100, - "if_this": { - "scope": "nft_event", - "actions": ["mint"] - } - } - } -} -``` - -Stop scanning after finding 100 matching events. - -## Common scanning patterns - -Learn from these practical examples of scanning for specific blockchain events. - -### Find first contract deployment - -```json find-deployment.json -{ - "chain": "stacks", - "networks": { - "mainnet": { - "expire_after_occurrence": 1, - "if_this": { - "scope": "contract_deployment", - "deployer": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR" - } - } - } -} -``` - -```terminal -$ chainhook predicates scan find-deployment.json --mainnet -``` - -### Collect NFT activity - -```json nft-activity.json -{ - "if_this": { - "scope": "nft_event", - "asset_identifier": "SP32AEEF6WW5Y0NMJ1S8SBSZDAY8R5J32NBZFPKKZ.free-punks-v0::free-punks", - "actions": ["mint", "transfer", "burn"] - }, - "then_that": { - "file_append": { - "path": "./nft-activity.json" - } - } -} -``` - -### Monitor address transactions - -```json address-monitor.json -{ - "if_this": { - "scope": "stx_event", - "actions": ["transfer"], - "predicate": { - "or": [ - { - "equals": { - "sender": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR" - } - }, - { - "equals": { - "recipient": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR" - } - } - ] - } - } -} -``` - -Track all STX transfers involving a specific address as sender or recipient. - -## Debug scanning - -Enable verbose output to understand why events match or don't match: - -```terminal -$ chainhook predicates scan my-predicate.json --mainnet --verbose -``` - -## Further reading - -- [Service mode deployment](/tools/chainhook/service-mode) -- [Usage examples](/tools/chainhook/usage) \ No newline at end of file diff --git a/content/docs/en/tools/chainhook/(chainhook-cli)/meta.json b/content/docs/en/tools/chainhook/(chainhook-cli)/meta.json deleted file mode 100644 index f2b9770fa..000000000 --- a/content/docs/en/tools/chainhook/(chainhook-cli)/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Chainhook CLI", - "pages": ["event-scanning", "service-mode", "cli-reference"] -} diff --git a/content/docs/en/tools/chainhook/(chainhook-cli)/service-mode.mdx b/content/docs/en/tools/chainhook/(chainhook-cli)/service-mode.mdx deleted file mode 100644 index 7f84fd853..000000000 --- a/content/docs/en/tools/chainhook/(chainhook-cli)/service-mode.mdx +++ /dev/null @@ -1,152 +0,0 @@ ---- -title: Run Chainhook as a service -sidebarTitle: Service mode -description: Deploy Chainhook as a service for real-time blockchain event streaming with Bitcoin and Stacks nodes. ---- - -This guide shows you how to run Chainhook as a service for continuous monitoring of blockchain events. You'll learn how to configure Chainhook with both Bitcoin and Stacks nodes, manage predicates dynamically, and optimize for production deployments. - -## Basic service setup - -Start Chainhook as a service using a configuration file: - -```terminal -$ chainhook service start --config-path=./Chainhook.toml -``` - -The service connects to your blockchain nodes and begins monitoring for events that match your registered predicates. - -### Minimal configuration - -```toml Chainhook.toml -[storage] -working_dir = "/var/chainhook" - -[network] -mode = "mainnet" - -[limits] -max_number_of_bitcoin_predicates = 100 -max_number_of_stacks_predicates = 10 -``` - -## Bitcoin node configuration - -Configure Chainhook to work with a Bitcoin node for monitoring Bitcoin events: - -```toml Chainhook.toml -[network] -mode = "mainnet" -bitcoind_rpc_url = "http://localhost:8332" -bitcoind_rpc_username = "devnet" -bitcoind_rpc_password = "devnet" - -# Option 1: Receive events via ZeroMQ (recommended for Bitcoin-only) -bitcoind_zmq_url = "tcp://0.0.0.0:18543" - -# Option 2: Receive events via Stacks node (if running both chains) -# stacks_node_rpc_url = "http://localhost:20443" -``` - -### Bitcoin configuration mapping - -| bitcoin.conf | Chainhook.toml | -|--------------|----------------| -| rpcuser | bitcoind_rpc_username | -| rpcpassword | bitcoind_rpc_password | -| rpcport | bitcoind_rpc_url | -| zmqpubhashblock | bitcoind_zmq_url | - -## Stacks node configuration - -For monitoring Stacks events, configure both the Stacks node and Chainhook: - -### Stacks node setup - -```toml Stacks.toml -[node] -working_dir = "/stacks-blockchain" -rpc_bind = "0.0.0.0:20443" -p2p_bind = "0.0.0.0:20444" - -[burnchain] -chain = "bitcoin" -mode = "mainnet" -peer_host = "localhost" -username = "devnet" # Must match bitcoin.conf rpcuser -password = "devnet" # Must match bitcoin.conf rpcpassword -rpc_port = 8332 # Must match bitcoin.conf rpcport - -[[events_observer]] -endpoint = "localhost:20455" -retry_count = 255 -events_keys = ["*"] -``` - -### Chainhook configuration for Stacks - -```toml Chainhook.toml -[network] -mode = "mainnet" -bitcoind_rpc_url = "http://localhost:8332" -bitcoind_rpc_username = "devnet" -bitcoind_rpc_password = "devnet" -stacks_node_rpc_url = "http://localhost:20443" -stacks_events_ingestion_port = 20455 -``` - -## Predicate management - -Register predicates with the service to start monitoring for specific events. - -### Start with predicates - -```terminal -$ chainhook service start --predicate-path=my-predicate.json --config-path=Chainhook.toml -``` - -### Dynamic registration - -Enable the HTTP API to register predicates while the service is running: - -```toml Chainhook.toml -[http_api] -http_port = 20456 -database_uri = "redis://localhost:6379/" -``` - -Register a new predicate via API: - -```terminal -$ curl -X POST http://localhost:20456/v1/chainhooks \ - -H "Content-Type: application/json" \ - -d @predicate.json - -{"result":"f8d43129-dba1-4a6c-b368-21426de0f3cd","status":200} -``` - -## Service monitoring - -Monitor the health and status of your Chainhook service. - -### Health check - -```terminal -$ curl http://localhost:20456/health -``` - -```json -{ - "status": "healthy", - "stacks_node": "connected", - "bitcoin_node": "connected", - "database": "connected", - "predicates_active": 3 -} -``` - -## Further reading - -- [Predicate design](/tools/chainhook/usage) -- [Bitcoin event scopes](/tools/chainhook/reference/bitcoin-scopes) -- [Stacks event scopes](/tools/chainhook/reference/stacks-scopes) \ No newline at end of file diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx new file mode 100644 index 000000000..18708cdd4 --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx @@ -0,0 +1,80 @@ +--- +title: Edit chainhooks +description: Modify existing chainhooks using the SDK +--- + +Modify existing chainhooks including filters, actions, and options. The Platform UI doesn't support editing - use the SDK instead. + +:::callout +The Platform UI does not currently support editing chainhooks. You must use the SDK or API to make updates. +::: + +## updateChainhook + +### Mutable vs Immutable fields + +| Mutable | Immutable | +|---------------------|---------------------------| +| `name` | `chain` | +| `filters` | `network` | +| `action` | | +| `options` | | + +### Basic Update Example + +```typescript +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +await client.updateChainhook('chainhook-uuid', { + name: 'Updated chainhook name', + filters: { + events: [{ type: 'ft_transfer', asset_identifier: 'SP...ABC.token::usdc' }], + }, +}); +``` + +### Add event filter (while preserving existing events) + +In order to add a new event filter to an existing chainhook, you can fetch the current definition, modify it, and then update it. + +```typescript +// ✅ Good: Fetch first +const current = await client.getChainhook(uuid); +await client.updateChainhook('chainhook-uuid', { + filters: { + events: [ + ...(current.definition.filters.events ?? []), + { type: 'contract_call', contract_identifier: 'SP...XYZ.counter' }, + ], + }, +}); + +// ❌ Bad: Will overwrite any existing events +await client.updateChainhook(uuid, { + filters: { events: { type: 'contract_call', contract_identifier: 'SP...XYZ.counter' } }, +}); +``` + +### Update Multiple Fields + +```typescript +await client.updateChainhook('chainhook-uuid', { + name: 'Updated name', + filters: { events: [{ type: 'stx_transfer', sender: 'SP...SENDER' }] }, + action: { type: 'http_post', url: 'https://new-url.com/webhooks' }, + options: { decode_clarity_values: true }, +}); + +const updated = await client.getChainhook('chainhook-uuid'); +console.log('Updated:', updated.definition.name); +``` + +:::next-steps +- [List & Fetch](/tools/chainhook/list-fetch): Retrieve chainhook information before updating +- [Filter Reference](/tools/chainhook/reference/filters): Explore all filter options +::: diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/evaluate.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/evaluate.mdx new file mode 100644 index 000000000..d92f8b1f5 --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/evaluate.mdx @@ -0,0 +1,67 @@ +--- +title: Evaluate a specific block +description: Run your chainhooks against specific blocks for testing, debugging, and historical indexing. +--- + +The evaluate endpoint replays a single block against one of your registered chainhooks so you can validate filters without waiting for live traffic. + +Use it to reproduce missed deliveries, inspect payload schemas after filter changes, or test webhook infrastructure with known blocks before enabling a hook in production. + +## evaluateChainhook + +### Evaluation Methods + +| Method | Parameter | Example | +|--------|-----------|---------| +| **By height** | `block_height` | `{ block_height: 100000 }` | +| **By hash** | `index_block_hash` | `{ index_block_hash: '0xa204...' }` | + +### Example + + + +```ts !! with-block-height.ts +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +await client.evaluateChainhook('chainhook-uuid', { + // !mark + block_height: 100000, +}); +``` + +```ts !! with-block-hash.ts +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +await client.evaluateChainhook('chainhook-uuid', { + // !mark + index_block_hash: '0xa204...', +}); +``` + + + +Returns HTTP `204 No Content`. If filters match, webhook payload is sent to your `action.url`. + +## Use Cases + +| Use Case | Description | Example | +|----------|-------------|---------| +| **Debug** | Investigate missed events | Evaluate specific block that should have triggered | +| **Backfill** | Index historical data | Process past blocks after creating chainhook | +| **Re-process** | Fix webhook handler issues | Re-evaluate after fixing bugs | + + +:::next-steps +- [Register & Enable](/tools/chainhook/register-enable): Create chainhooks to evaluate +- [Filter Reference](/tools/chainhook/reference/filters): Configure which events to match +::: diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/introduction.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/introduction.mdx new file mode 100644 index 000000000..8f4c5b750 --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/introduction.mdx @@ -0,0 +1,165 @@ +--- +title: Introduction +description: TypeScript/JavaScript SDK for managing chainhooks programmatically +--- + +## Overview + +The Chainhook SDK (`@hirosystems/chainhooks-client`) provides a TypeScript/JavaScript client for programmatically managing chainhooks. + +## Installation + + + ```terminal !! npm + $ npm install @hirosystems/chainhooks-client + ``` + + ```terminal !! yarn + $ yarn add @hirosystems/chainhooks-client + ``` + + ```terminal !! pnpm + $ pnpm add @hirosystems/chainhooks-client + ``` + + ```terminal !! bun + $ bun add @hirosystems/chainhooks-client + ``` + + +## Quick Example + +```typescript +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, // or CHAINHOOKS_BASE_URL.mainnet + apiKey: process.env.HIRO_API_KEY!, +}); + +// Register and enable a chainhook +const chainhook = await client.registerChainhook({ + version: '1', + name: 'my-first-chainhook', + chain: 'stacks', + network: 'testnet', + filters: { + events: [ + { + type: 'contract_call', + contract_identifier: 'SP...XYZ.counter', + function_name: 'increment', + }, + ], + }, + action: { + type: 'http_post', + url: 'https://example.com/webhooks', + }, + options: { + decode_clarity_values: true, + enable_on_registration: true, + }, +}); + +console.log('Chainhook created:', chainhook.uuid); +``` + +## Base URLs + +The SDK provides network-specific constants: + +| Network | Constant | URL | +|---------|----------|-----| +| Testnet | `CHAINHOOKS_BASE_URL.testnet` | https://api.testnet.hiro.so | +| Mainnet | `CHAINHOOKS_BASE_URL.mainnet` | https://api.mainnet.hiro.so | + +```typescript +import { CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +// Testnet (for development) +const testnetClient = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +// Mainnet (for production) +const mainnetClient = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, + apiKey: process.env.HIRO_API_KEY!, +}); +``` + +## Authentication + +### Get Your API Key + +1. Visit [platform.hiro.so](https://platform.hiro.so) +2. Sign in or create an account +3. Navigate to API Keys section +4. Generate or copy your API key + +### Configure Client + +```typescript +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, // Store securely in environment variables +}); +``` + +:::callout +type: warn +### API keys +Never commit API keys to version control. Always use environment variables or secure secret management. +::: + +## SDK Methods + +The SDK provides the following methods: + +### Core Methods + +| Method | Description | +|--------|-------------| +| `registerChainhook()` | Create a new chainhook | +| `getChainhooks()` | List all your chainhooks (with pagination) | +| `getChainhook()` | Get a specific chainhook by UUID | +| `updateChainhook()` | Update an existing chainhook | +| `deleteChainhook()` | Delete a chainhook | + +### Activation Methods + +| Method | Description | +|--------|-------------| +| `enableChainhook()` | Enable or disable a single chainhook | +| `bulkEnableChainhooks()` | Enable or disable multiple chainhooks with filters | + +### Utility Methods + +| Method | Description | +|--------|-------------| +| `evaluateChainhook()` | Evaluate a chainhook against specific past blocks | +| `rotateConsumerSecret()` | Rotate the webhook secret for payload verification | + +## TypeScript Support + +### Available Types + +The SDK provides full TypeScript type definitions: + +```typescript +import type { + ChainhookDefinitionSchema, // Chainhook configuration + ChainhookStatusSchema, // Status and activity info + EvaluateChainhookRequest, // Evaluation parameters + BulkEnableChainhooksRequest, // Bulk operation filters +} from '@hirosystems/chainhooks-client'; +``` + +## Next Steps + +:::next-steps +- [Quickstart](/tools/chainhook/quickstart): Get started in 5 minutes +- [Register & Enable](/tools/chainhook/register-enable): Create and activate chainhooks +::: diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx new file mode 100644 index 000000000..cd9c609e6 --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx @@ -0,0 +1,54 @@ +--- +title: Fetch chainhooks +description: Retrieve chainhook information using the SDK +--- + +## getChainhooks + +Retrieve a paginated list of all your chainhooks. + +### Example + +```typescript +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +// Get first page (default: 20 results) +const chainhooks = await client.getChainhooks(); + +console.log('Total chainhooks:', chainhooks.total); +console.log('Results:', chainhooks.results.length); +console.log('Limit:', chainhooks.limit); +console.log('Offset:', chainhooks.offset); +``` + +### With options + +```typescript +// Get specific page with 50 chainhooks starting from position 100 +const chainhooks = await client.getChainhooks({ + limit: 50, + offset: 100, +}); +``` + +--- + +## getChainhook + +Retrieve a specific chainhook by UUID. + +### Example + +```typescript +const chainhook = await client.getChainhook('be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185'); +``` + +:::next-steps +- [Edit & Update](/tools/chainhook/edit-update): Modify existing chainhooks +- [Register & Enable](/tools/chainhook/register-enable): Create new chainhooks +::: diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx new file mode 100644 index 000000000..1eb9777c6 --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx @@ -0,0 +1,66 @@ +--- +title: Manage Keys +description: Rotate consumer secrets and validate every Chainhook delivery +--- + +## What you'll learn + +:::objectives +- Create/rotate a Chainhook consumer secret. +- Validate webhook requests by checking the `Authorization` header. +::: + +## Prerequisites + +:::prerequisites +- Hiro API key +- Node.js (server example uses Fastify). +::: + +## Validating webhook requests with a consumer secret + +When you create a secret, our Chainhook service attaches an `Authorization: Bearer ` header to every webhook attempt, giving you a simple shared-secret handshake. Here's how to get started: + +1. Rotate the secret with `rotateConsumerSecret` (or the `/chainhooks/{uuid}/secret` API) whenever you need to initialize or create a new token. +2. Reject webhook deliveries whose `Authorization` header does not equal `Bearer `. + +### Create/rotate consumer secret + +```ts server.ts +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, // or .testnet / custom URL + apiKey: process.env.HIRO_API_KEY!, +}); + +// Store this value securely and use it to validate webhook requests +const secret = await client.rotateConsumerSecret(chainhookUuid).secret; +``` + +### Example Fastify server + +```ts -n +import Fastify from 'fastify'; + +const server = Fastify(); + +server.post('/webhook', async (request, reply) => { + if (!secret) { + reply.code(503).send({ error: 'consumer secret unavailable' }); + return; + } + + const authHeader = request.headers.authorization; + if (authHeader !== `Bearer ${secret}`) { + reply.code(401).send({ error: 'invalid consumer secret' }); + return; + } + + const event = request.body; + console.log(`received chainhook ${event.chainhook.uuid}`); + reply.code(204).send(); +}); + +await server.listen({ port: Number(process.env.PORT) || 3000 }); +``` diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json b/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json new file mode 100644 index 000000000..592f45017 --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Chainhook SDK", + "pages": [ + "---Chainhook SDK|new---", + "introduction", + "register-enable", + "list-fetch", + "edit-update", + "evaluate", + "manage-keys" + ] +} diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx new file mode 100644 index 000000000..583222ce7 --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx @@ -0,0 +1,145 @@ +--- +title: Create chainhooks +description: Create and activate chainhooks using the Chainhook SDK +--- + +# Register & Enable + +Learn how to create and activate chainhooks using the Chainhook SDK. + +## registerChainhook + +Creates a new chainhook configuration. By default, new chainhooks are created in a disabled state unless `enable_on_registration` is set to `true`. + +### Example + +```ts -nc +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +const chainhook = await client.registerChainhook({ + version: '1', + name: 'my-chainhook', + chain: 'stacks', + network: 'testnet', + filters: { + events: [ + { + type: 'contract_call', + contract_identifier: 'SP...XYZ.counter', + function_name: 'increment', + }, + ], + }, + action: { + type: 'http_post', + url: 'https://example.com/webhooks', + }, + options: { + decode_clarity_values: true, + enable_on_registration: true, + }, +}); + +console.log('Chainhook UUID:', chainhook.uuid); +console.log('Enabled:', chainhook.status.enabled); // true +``` + +If you don't set `enable_on_registration`, the chainhook will be created but disabled by default. + +--- + +## enableChainhook + +Enable or disable a single chainhook by UUID. This allows you to enable chainhooks after registration or pause without deleting it. + +### Examples + +```typescript +// Enable a chainhook +await client.enableChainhook('chainhook-uuid', true); + +// Disable a chainhook +await client.enableChainhook('chainhook-uuid', false); +``` + +Returns HTTP `204 No Content` on success. + +--- + +## bulkEnableChainhooks + +Enable or disable multiple chainhooks at once using filters. This is useful for managing many chainhooks programmatically. + +### By UUIDs + +Enable specific chainhooks by their UUIDs (maximum 200): + +```typescript +await client.bulkEnableChainhooks({ + enabled: true, + filters: { + // !mark(1:5) + uuids: [ + 'uuid-1', + 'uuid-2', + 'uuid-3', + ], + }, +}); +``` + +### By Webhook URL + +Enable all chainhooks that POST to a specific URL: + +```typescript +await client.bulkEnableChainhooks({ + enabled: true, + filters: { + // !mark + webhook_url: 'https://example.com/webhooks', + }, +}); +``` + +### By Status + +Enable all chainhooks with a specific status: + +```typescript + +await client.bulkEnableChainhooks({ + enabled: true, + filters: { + // !mark + statuses: ['inactive'], + }, +}); +``` + +### Combined Filters + +Use multiple filters together: + +```typescript +await client.bulkEnableChainhooks({ + enabled: false, // Disable matching chainhooks + filters: { + // !mark(1:2) + webhook_url: 'https://old-server.com/webhooks', + statuses: ['active'], + }, +}); +``` + +This will disable all active chainhooks that POST to the old webhook URL. + +:::next-steps +- [Evaluate](/tools/chainhook/evaluate): Test chainhooks against past blocks +- [Filter Reference](/tools/chainhook/reference/filters): Explore all filter options +::: diff --git a/content/docs/en/tools/chainhook/(event-handling)/custom-indexer.mdx b/content/docs/en/tools/chainhook/(event-handling)/custom-indexer.mdx deleted file mode 100644 index 8a45cd667..000000000 --- a/content/docs/en/tools/chainhook/(event-handling)/custom-indexer.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Build a custom indexer -description: Create your own blockchain indexer with Chainhook. ---- - -Placeholder content - new guide to be created \ No newline at end of file diff --git a/content/docs/en/tools/chainhook/(event-handling)/example-indexers.mdx b/content/docs/en/tools/chainhook/(event-handling)/example-indexers.mdx deleted file mode 100644 index 48f7d24e4..000000000 --- a/content/docs/en/tools/chainhook/(event-handling)/example-indexers.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Example indexers -description: Reference implementations of Chainhook indexers. ---- - -Placeholder content - new guide to be created \ No newline at end of file diff --git a/content/docs/en/tools/chainhook/(event-handling)/meta.json b/content/docs/en/tools/chainhook/(event-handling)/meta.json deleted file mode 100644 index 8959d64e9..000000000 --- a/content/docs/en/tools/chainhook/(event-handling)/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Event handling", - "pages": ["webhook-setup", "payload-handling", "custom-indexer", "example-indexers"] -} diff --git a/content/docs/en/tools/chainhook/(event-handling)/payload-handling.mdx b/content/docs/en/tools/chainhook/(event-handling)/payload-handling.mdx deleted file mode 100644 index 0b0ff8650..000000000 --- a/content/docs/en/tools/chainhook/(event-handling)/payload-handling.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Payload handling -description: Process and handle Chainhook webhook payloads. ---- - -Placeholder content - new guide to be created \ No newline at end of file diff --git a/content/docs/en/tools/chainhook/(event-handling)/webhook-setup.mdx b/content/docs/en/tools/chainhook/(event-handling)/webhook-setup.mdx deleted file mode 100644 index 493e3c47f..000000000 --- a/content/docs/en/tools/chainhook/(event-handling)/webhook-setup.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Webhook setup -description: Configure webhook endpoints to receive blockchain events. ---- - -Placeholder content - new guide to be created \ No newline at end of file diff --git a/content/docs/en/tools/chainhook/(integrations)/meta.json b/content/docs/en/tools/chainhook/(integrations)/meta.json deleted file mode 100644 index dacb8d143..000000000 --- a/content/docs/en/tools/chainhook/(integrations)/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Integrations", - "pages": ["register-chainhooks-on-devnet"] -} diff --git a/content/docs/en/tools/chainhook/(integrations)/register-chainhooks-on-devnet.mdx b/content/docs/en/tools/chainhook/(integrations)/register-chainhooks-on-devnet.mdx deleted file mode 100644 index aa20fdb7b..000000000 --- a/content/docs/en/tools/chainhook/(integrations)/register-chainhooks-on-devnet.mdx +++ /dev/null @@ -1,123 +0,0 @@ ---- -title: Register Chainhooks on devnet -description: In this guide, you'll learn how to automatically register Chainhooks on devnet using Clarinet. ---- - -## What you'll learn - -:::objectives -- Create Chainhook predicate files in your Clarinet project -- Register predicates automatically when starting devnet -- Monitor Chainhook execution during local development -::: - -## Prerequisites - -:::prerequisites -- Clarinet version 2.1.0 or higher installed -- A Clarinet project with smart contracts -- Basic understanding of Chainhook predicates -::: - -## Register Chainhooks on devnet - - - - ### Create predicate files - - Create your Chainhook predicate files in your Clarinet project directory. You can place them in the project root or organize them in a dedicated folder. - - ```json chainhooks/increment.json - { - "chain": "stacks", - "uuid": "1", - "name": "Increment Counter", - "version": 1, - "networks": { - "devnet": { - "if_this": { - "scope": "contract_call", - "contract_identifier": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter", - "method": "increment" - }, - "then_that": { - "http_post": { - "url": "http://localhost:3000/api/increment", - "authorization_header": "Bearer devnet-token" - } - } - } - } - } - ``` - - The predicate will trigger whenever the `increment` method is called on your counter contract. - - - - ### Organize project structure - - Structure your project with Chainhook files in a logical location. Here's a recommended project layout: - - ``` - my-project/ - ├── contracts/ - │ └── counter.clar - ├── chainhooks/ - │ ├── increment.json - │ └── decrement.json - ├── tests/ - │ └── counter.test.ts - └── Clarinet.toml - ``` - - Clarinet will automatically discover predicate files in your project directory when starting devnet. - - - - ### Start devnet with Chainhook registration - - Launch devnet from your project root. Clarinet automatically starts a Chainhook service and registers all predicate files it finds. - - ```terminal - $ clarinet devnet start - ``` - - Look for the registration confirmation in your terminal output: - - ```console - Computing deployment plan - ✔ Deployment plan completed - - INFO Feb 5 15:20:07.233382 2 chainhooks registered - ``` - - This message confirms your predicates are active and monitoring the local blockchain. - - - - ### Verify Chainhook execution - - Interact with your contracts to trigger the registered Chainhooks. Monitor the devnet terminal for execution confirmations. - - ```terminal - $ clarinet console - >> (contract-call? .counter increment) - ``` - - Watch the devnet terminal for triggered hooks: - - ```console - INFO Feb 5 15:21:07.233382 1 hooks triggered - ``` - - Your webhook endpoint or file output will receive the event payload based on your `then_that` configuration. - - - -## Next steps - -:::next-steps -- [Bitcoin scopes](/tools/chainhook/reference/bitcoin-scopes): Explore available event types for Bitcoin -- [Stacks scopes](/tools/chainhook/reference/stacks-scopes): Explore available event types for Stacks -::: \ No newline at end of file diff --git a/content/docs/en/tools/chainhook/(overview)/faq.mdx b/content/docs/en/tools/chainhook/(overview)/faq.mdx new file mode 100644 index 000000000..372f3d626 --- /dev/null +++ b/content/docs/en/tools/chainhook/(overview)/faq.mdx @@ -0,0 +1,319 @@ +--- +title: FAQ +description: Frequently asked questions about Chainhook 2.0 Beta +--- + +## Chainhook 2.0 Beta + + + + What is the goal/purpose of the Chainhook 2.0 Beta? + +Our goal during the Beta is to learn as much as possible about the reliability, performance and developer experience of Chainhooks 2.0 in anticipation of the full release. If you encounter any issues, have any questions, or would like to share feedback during the Beta, please reach out to [beta@hiro.so](mailto:beta@hiro.so). + + + + + Is the Chainhook 2.0 Beta free? + +Yes! The Chainhooks 2.0 Beta is free for all participants. + + + + + Will there be configuration or rate limits imposed during the Beta? + +All Beta users will initially be constrained to a limit of 10 Chainhook configurations. + + + + + How will Chainhooks be priced following the Beta? + +Chainhooks will be charged using a credit model for each delivery, with users able to select different credit limits depending on their usage. For Chainhooks 2.0 Beta users, your post-beta limits will initially be determined by your existing Hiro subscription tier. + + + + +## Version Management + + + + What will happen to existing legacy Chainhooks running on v1.0? + +Users with Chainhooks running on v1.0 will still be able to view them and receive deliveries, but once the beta launches, all new Chainhooks created in the Platform will run on v2.0. + +The API will also not support Chainhooks running on v1.0. + +:::callout +Learn how to migrate your v1 chainhooks to v2 in the [Migration Guide](/tools/chainhook/migration). +::: + + + + + If v2.0 and v1.0 will be live at the same time, how do we manage both? + +In the Platform, v1 chainhooks are read-only. You can view them and they will continue to deliver events, but you cannot modify them through the Platform UI. + +To modify v1 chainhooks programmatically, use the [Platform API](/apis/platform-api). However, we recommend migrating to v2 chainhooks instead. See the [Migration Guide](/tools/chainhook/migration) for a complete walkthrough. + + + + + How do I migrate my v1 chainhooks to v2? + + To migrate your v1 chainhooks to v2, follow the steps outlined in the [Migration Guide](/tools/chainhook/migration). + + + + +## Platform vs SDK + + + + Can I edit my v2 Chainhooks? + +The Platform currently does not support editing existing chainhooks. You must use the [Chainhook SDK](/tools/chainhook/chainhook-sdk) or [Chainhook API](/apis/chainhook-api) to edit your chainhooks. + + + + + When should I use the Platform vs the SDK? + +
+

Use the Platform when:

+
    +
  • Creating simple chainhooks with a UI
  • +
  • Viewing chainhook status and activity
  • +
  • Managing API keys
  • +
  • Getting started quickly
  • +
+

Use the SDK/API when:

+
    +
  • You need to edit existing chainhooks
  • +
  • Programmatic chainhook management
  • +
  • Automating chainhook operations
  • +
  • Integrating into CI/CD pipelines
  • +
  • Managing many chainhooks at scale
  • +
+
+
+
+
+ +## Getting Started + + + + How do I get a Hiro API key? + +
+
    +
  1. Visit platform.hiro.so
  2. +
  3. Sign in or create an account
  4. +
  5. Navigate to the API Keys section
  6. +
  7. Generate or copy your API key
  8. +
+

Store your API key securely in environment variables and never commit it to version control.

+
+
+
+ + + What's the difference between mainnet and testnet? + +
+
    +
  • Mainnet - The production Stacks blockchain with real economic value
  • +
  • Testnet - A test network for development and testing
  • +
+

Always test your chainhooks on testnet before deploying to mainnet. Use these base URLs:

+
{`import { CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client';
+
+// Testnet (for development)
+CHAINHOOKS_BASE_URL.testnet  // https://api.testnet.hiro.so
+
+// Mainnet (for production)
+CHAINHOOKS_BASE_URL.mainnet  // https://api.mainnet.hiro.so`}
+
+
+
+
+ +## Troubleshooting + + + + My webhook isn't receiving events. What should I check? + +
+

1. Verify chainhook is enabled

+
{`const chainhook = await client.getChainhook('uuid');
+console.log('Enabled:', chainhook.status.enabled);`}
+

2. Check your webhook URL is accessible

+
    +
  • URL must be publicly accessible over HTTPS
  • +
  • Test with tools like webhook.site or ngrok for local development
  • +
+

3. Verify your filters match blockchain events

+
    +
  • Use evaluateChainhook() against a block you know contains matching events
  • +
  • Check the Filter Reference for correct syntax
  • +
+

4. Check webhook endpoint logs

+
    +
  • Ensure your endpoint returns HTTP 200
  • +
  • Look for error messages in your application logs
  • +
+
+
+
+ + + I'm getting 401 Unauthorized errors + +
+

Your API key may be incorrect or missing:

+
{`// Verify API key is set
+if (!process.env.HIRO_API_KEY) {
+  throw new Error('HIRO_API_KEY environment variable is required');
+}
+
+// Test the key
+try {
+  await client.getChainhooks();
+  console.log('✅ API key is valid');
+} catch (error) {
+  console.error('❌ Invalid API key');
+}`}
+
+
+
+ + + How do I test my chainhook before enabling it? + +
+

Use the evaluateChainhook() method to test against known blocks:

+
{`// Create chainhook (disabled by default)
+const chainhook = await client.registerChainhook({
+  // ... your configuration
+  // Do NOT set enable_on_registration
+});
+
+// Test against a specific block
+await client.evaluateChainhook(chainhook.uuid, {
+  block_height: 150000,
+});
+
+// Check your webhook endpoint for the payload
+// If it works, enable the chainhook
+await client.enableChainhook(chainhook.uuid, true);`}
+
+
+
+ + + Why am I getting duplicate webhook deliveries? + +
+

Chainhook may retry webhook deliveries if your endpoint doesn't return HTTP 200. Make your webhook processing idempotent:

+
{`app.post('/webhooks', async (req, res) => {
+  const blockHeight = req.body.event.apply[0].block_identifier.index;
+
+  // Check if already processed
+  const exists = await db.isBlockProcessed(blockHeight);
+  if (exists) {
+    console.log(\`Block \${blockHeight} already processed, skipping\`);
+    return res.sendStatus(200);
+  }
+
+  // Process the webhook
+  await processWebhook(req.body);
+
+  // Return 200 immediately
+  res.sendStatus(200);
+});`}
+
+
+
+
+ +## Common Questions + + + + Can I use chainhooks for real-time notifications? + +Yes! Chainhooks are designed for real-time blockchain event delivery. Webhook payloads are delivered within seconds of events occurring on-chain. + + + + + How do chainhooks handle blockchain reorganizations? + +
+

Chainhook automatically detects reorgs and sends both rollback and apply events. Always process rollback events first to maintain data consistency.

+

Learn more in the Usage guide.

+
+
+
+ + + Can I filter by multiple event types? + +
+

Yes! You can include multiple filters in the events array. The chainhook will trigger if any filter matches:

+
{`{
+  filters: {
+    events: [
+      { type: 'ft_transfer', asset_identifier: 'SP...TOKEN::usdc' },
+      { type: 'nft_mint', asset_identifier: 'SP...NFT::collectible' },
+      { type: 'contract_call', contract_identifier: 'SP...DEX.swap' },
+    ],
+  }
+}`}
+
+
+
+ + + What happens if my webhook endpoint is down? + +Chainhook will retry webhook deliveries with exponential backoff. However, extended downtime may result in missed events. Implement proper error handling and monitoring for production applications. + + + + + Can I test chainhooks locally? + +
+

Yes! Use tools like ngrok or webhook.site to expose your local development server:

+
{`# Expose local port 3000
+ngrok http 3000
+
+# Use the ngrok URL in your chainhook
+# https://abc123.ngrok-free.app/webhooks`}
+
+
+
+
+ +--- + +## Where to Get Help + +- **Discord**: Join the **#chainhook** channel on [Discord](https://stacks.chat/) under Hiro Developer Tools +- **Weekly Office Hours**: Every Thursday at 11am ET ([add to calendar](https://www.addevent.com/event/oL21905919)) +- **Email Support**: [beta@hiro.so](mailto:beta@hiro.so) +- **GitHub Issues**: [hirosystems/chainhook](https://github.com/hirosystems/chainhook/issues) + +--- + +## Next Steps + +:::next-steps +- [Migration Guide](/tools/chainhook/migration): Migrate from v1 to v2 +- [SDK Quickstart](/tools/chainhook/quickstart): Get started with the SDK +::: diff --git a/content/docs/en/tools/chainhook/(overview)/meta.json b/content/docs/en/tools/chainhook/(overview)/meta.json index dabf1d9c5..1d584866a 100644 --- a/content/docs/en/tools/chainhook/(overview)/meta.json +++ b/content/docs/en/tools/chainhook/(overview)/meta.json @@ -1,4 +1,4 @@ { - "title": "Overview", - "pages": ["quickstart", "usage"] + "title": "Chainhook", + "pages": ["migration", "faq"] } diff --git a/content/docs/en/tools/chainhook/(overview)/migration.mdx b/content/docs/en/tools/chainhook/(overview)/migration.mdx new file mode 100644 index 000000000..d19cb52e5 --- /dev/null +++ b/content/docs/en/tools/chainhook/(overview)/migration.mdx @@ -0,0 +1,229 @@ +--- +title: Migration guide +description: Guide for migrating legacy chainhooks to v2 +--- + +:::callout +### Managing v1 chainhooks +Legacy v1 chainhooks remain read-only in the Platform UI; you manage them through the [Platform API](/apis/platform-api/reference/chainhooks/list). +::: + +## What you'll learn + +:::objectives +- Capture and analyze all existing v1 chainhooks. +- Convert predicate-based filters into explicit v2 event definitions. +- Register v2 chainhooks, test delivery, and retire the originals safely. +::: + +## Prerequisites + +:::prerequisites +- Hiro API key for access to both the Platform and Chainhook API. +::: + + + + +### Get a list of v1 chainhooks +Use the Platform API to fetch the chainhooks you want to migrate. + +```ts index.ts +const response = await fetch(`https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks`, { + headers: { + 'content-type': 'application/json' + } +}); + +const chainhooks = await response.json(); +``` + + + + +### Start mapping to the new v2 format +Translate v1 structures to v2 formats before creating new hooks. The following table shows the mapping between v1 and v2 structures: + +| v1 | v2 | Notes | +|------------|-----------|-------| +| `if_this.scope` | `filters.events[].type` | Replace `scope/action` combos with a single event type. | +| `if_this.actions` | `type` | `transfer` maps to `*_transfer`. | +| `then_that.http_post.url` | `action.url` | v2 handles secrets automatically. | +| `networks.mainnet` | `network: "mainnet"` | Create one v2 hook per network. | +| `authorization_header` | Webhook secret management | Use `rotateConsumerSecret()` after registration. | + +#### Example + + + +```json !! Legacy +{ + "name": "stx-transfers", + "networks": { + "mainnet": { + "if_this": { "scope": "stx_event", "actions": ["transfer"] }, + "then_that": { "http_post": { "url": "https://example.com/webhooks" } } + } + } +} +``` + +```json !! v2 +{ + "version": "1", + "name": "stx-transfers", + "chain": "stacks", + "network": "mainnet", + "filters": { + "events": [{ "type": "stx_transfer" }] + }, + "action": { + "type": "http_post", + "url": "https://example.com/webhooks" + }, + "options": { + "decode_clarity_values": true, + "enable_on_registration": true + } +} +``` + + +For more details on the format changes, see the [Filters](/tools/chainhook/reference/filters) reference guide. + + + + +### Create v2 chainhooks +Register each chainhook with the SDK or REST API, mirroring the mapped filters. + + + +```ts !! api.ts +const v2Chainhook = JSON.stringify({ + "version": "1", + "name": "stx-transfers", + "chain": "stacks", + "network": "mainnet", + "filters": { + "events": [{ "type": "stx_transfer" }] + }, + "action": { + "type": "http_post", + "url": "https://example.com/webhooks" + }, + "options": { + "decode_clarity_values": true, + "enable_on_registration": true + } +}); + +const response = await fetch('https://api.hiro.so/chainhooks/v1/me/', { + method: 'POST', + headers: { + 'x-api-key': process.env.HIRO_API_KEY!, + 'content-type': 'application/json', + }, + body: JSON.stringify(v2Chainhook), +}); +``` + +```ts !! sdk.ts +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, + apiKey: process.env.HIRO_API_KEY!, +}); +const chainhook = await client.registerChainhook( + { + "version": "1", + "name": "stx-transfers", + "chain": "stacks", + "network": "mainnet", + "filters": { + "events": [{ "type": "stx_transfer" }] + }, + "action": { + "type": "http_post", + "url": "https://example.com/webhooks" + }, + "options": { + "decode_clarity_values": true, + "enable_on_registration": true + } + } +); +``` + + + + + + +### Validate and cleanup legacy chainhooks +Stream events through both versions, confirm delivery, then clean up the legacy definitions. + +:::callout +type: info +### Best practices +Keep both v1 and v2 hooks active until you verify delivery parity. +::: + +#### Enablement checks on v2 + + + +```ts !! api.ts +const response = await fetch(`https://api.hiro.so/chainhooks/v1/me/${uuid}`, { + method: 'GET', + headers: { + 'x-api-key': process.env.HIRO_API_KEY!, + 'content-type': 'application/json', + }, +}); + +const chainhook = await response.json(); +console.log(chainhook.status.enabled); +``` + +```ts !! sdk.ts +const chainhook = await client.getChainhook(uuid); +console.log(chainhook.status.enabled); +``` + + + +#### Delete legacy chainhooks + +```ts +const response = await fetch( + `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks/${uuid}`,{ + method: 'DELETE', + headers: { + 'content-type': 'application/json' + } + } +); + +await response.json(); +``` + + + + + +## Filter reference + +### Common scopes +| v1 | Typical Actions | v2 | Extras | +|----------|-----------------|-----------|--------| +| `stx_event` | `transfer` | `stx_transfer` | Include `sender` or `recipient` filters as needed. | +| `contract_call` | n/a | `contract_call` | Add `contract_identifier` and `function_name`. | +| `ft_event` | `transfer` | `ft_transfer` | Specify `asset_identifier`. | +| `nft_event` | `transfer`, `mint` | `nft_transfer` · `nft_mint` | Provide `asset_identifier`. | + +For more details, check out the [Filters](/tools/chainhook/reference/filters) reference page. + +:::next-steps +- [SDK Documentation](/tools/chainhook/chainhook-sdk): SDK Documentation +- [Filter Reference](/tools/chainhook/reference/filters): Filter Reference +::: diff --git a/content/docs/en/tools/chainhook/(overview)/quickstart.mdx b/content/docs/en/tools/chainhook/(overview)/quickstart.mdx deleted file mode 100644 index 22002b9d8..000000000 --- a/content/docs/en/tools/chainhook/(overview)/quickstart.mdx +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: Quickstart -description: In this guide, you'll create your first Chainhook predicate to monitor STX transfers on the Stacks blockchain. ---- - -## What you'll learn - -:::objectives -- How to create a Chainhook predicate for tracking STX transfers -- How to scan historical blockchain data using Chainhook -::: - -## Prerequisites - -:::prerequisites -- For installation instructions, visit the [Chainhook installation guide](/tools/chainhook#installation). -::: - -## Quickstart - - - - ### Generate a predicate file - - Chainhook uses predicates to define what blockchain events to track. Generate a template predicate file: - - ```terminal - $ chainhook predicates new stx-transfer.json --stacks - ``` - - This creates a boilerplate JSON file with the basic predicate structure. The `--stacks` flag specifies you're tracking Stacks events (use `--bitcoin` for Bitcoin). - - - - ### Configure event tracking - - Open `stx-transfer.json` and update the `if_this` section to track STX transfer events: - - ```json stx-transfer.json - { - "chain": "stacks", - "uuid": "87ac9bff-1052-4d02-9c79-3768a6f08a09", - "name": "STX Transfer", - "version": 1, - "networks": { - "mainnet": { - "start_block": 154670, - "if_this": { - "scope": "stx_event", - "actions": ["transfer"] - }, - "then_that": { - "file_append": { - "path": "stx-transfers.txt" - } - } - } - } - } - ``` - - This predicate will: - - Track all STX transfer events (`scope: "stx_event"`, `actions: ["transfer"]`) - - Start scanning from block 154670 - - Append matching events to `stx-transfers.txt` - - - - ### Scan for events - - Run the scan command to search historical blockchain data: - - ```terminal - $ chainhook predicates scan stx-transfer.json --mainnet - ``` - - :::callout - The first `scan` downloads a chainstate archive from Hiro Archive. Subsequent scans use the cached data for faster performance. - ::: - - - - ### View results - - Check the output file to see the transfer events: - - ```terminal - $ head -5 stx-transfers.txt - ``` - - Each line contains a JSON payload with transfer details including sender, recipient, amount, and block information. - - - -## Next steps - -:::next-steps -- [Design custom predicates](/tools/chainhook/usage): Learn to create predicates for contract calls, NFT transfers, and more -- [Run Chainhook as a service](/tools/chainhook/service-mode): Set up continuous blockchain monitoring with webhook delivery -::: \ No newline at end of file diff --git a/content/docs/en/tools/chainhook/(overview)/usage.mdx b/content/docs/en/tools/chainhook/(overview)/usage.mdx deleted file mode 100644 index b0fb8e759..000000000 --- a/content/docs/en/tools/chainhook/(overview)/usage.mdx +++ /dev/null @@ -1,262 +0,0 @@ ---- -title: Usage examples -sidebarTitle: Usage -description: Learn Chainhook's core functionality through practical examples - predicates, event scopes, reorg handling, and operation modes. ---- - -## Predicates - -Predicates are JSON specifications that tell Chainhook what blockchain events to monitor and how to respond. They follow an if-this-then-that pattern. - -```json stx-transfer-predicate.json -{ - "chain": "stacks", - "uuid": "1", - "name": "Monitor STX Transfers", - "version": 1, - "networks": { - "mainnet": { - "if_this": { - "scope": "stx_event", - "actions": ["transfer"] - }, - "then_that": { - "http_post": { - "url": "https://api.example.com/stx-transfers", - "authorization_header": "Bearer secret-token" - } - }, - "start_block": 100000 - } - } -} -``` - -This predicate monitors all STX transfer events starting from block 100,000 and sends them to your webhook endpoint. - -### Predicate components - -```json -{ - "chain": "bitcoin", // Target blockchain: "bitcoin" or "stacks" - "uuid": "unique-id-123", // Unique identifier for tracking - "name": "My Bitcoin Monitor", // Human-readable name - "version": 1, // Predicate version - "networks": { // Network-specific configurations - "mainnet": { ... }, - "testnet": { ... } - } -} -``` - -## Event scopes - -Each blockchain supports different event types. Scopes define what specific events your predicate will match. - -### Stacks events - -```json contract-call-predicate.json -{ - "if_this": { - "scope": "contract_call", - "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-oracle-v2-3", - "method": "update-price" - } -} -``` - -Monitor specific contract function calls. Available Stacks scopes: -- `stx_event` - STX transfers, mints, burns -- `contract_call` - Smart contract function calls -- `contract_deployment` - New contract deployments -- `print_event` - Contract print statements -- `ft_event` - Fungible token operations -- `nft_event` - Non-fungible token operations - -### Bitcoin events - -```json bitcoin-ordinals-predicate.json -{ - "if_this": { - "scope": "ordinals_protocol", - "operation": "inscription_feed" - } -} -``` - -Track Bitcoin ordinal inscriptions. Available Bitcoin scopes: -- `p2pkh`, `p2sh`, `p2wpkh`, `p2tr` - Address types -- `ordinals_protocol` - Ordinal inscriptions -- `stacks_protocol` - Stacks-specific Bitcoin operations - -## Action handlers - -Define how Chainhook responds when events match your predicate criteria. - -### Webhook delivery - -```json webhook-config.json -{ - "then_that": { - "http_post": { - "url": "https://api.myapp.com/events", - "authorization_header": "Bearer ${WEBHOOK_SECRET}" - } - } -} -``` - -Chainhook POSTs matching events to your endpoint with the authorization header. - -### File output - -```json file-output-config.json -{ - "then_that": { - "file_append": { - "path": "/data/blockchain-events.jsonl" - } - } -} -``` - -Append events to a local file, one JSON object per line. - -## Blockchain reorganizations - -Chainhook automatically handles blockchain reorganizations (reorgs) by sending both rollback and apply events. - -```json reorg-event.json -{ - "apply": [ - { - "block_identifier": { - "index": 792101, - "hash": "0x123..." - }, - "transactions": [ - // New canonical chain transactions - ] - } - ], - "rollback": [ - { - "block_identifier": { - "index": 792100, - "hash": "0x456..." - }, - "transactions": [ - // Transactions to rollback - ] - } - ] -} -``` - -Your application must handle both event types to maintain data consistency during reorgs. - -### Handling reorg events - -```typescript webhook-handler.ts -app.post('/chainhook-events', (req, res) => { - const { apply, rollback } = req.body; - - // First, rollback orphaned transactions - if (rollback) { - for (const block of rollback) { - await removeTransactions(block.transactions); - } - } - - // Then apply new canonical transactions - if (apply) { - for (const block of apply) { - await processTransactions(block.transactions); - } - } - - res.status(200).send('OK'); -}); -``` - -## Operation modes - -Chainhook operates in two primary modes depending on your use case. - -### Scanning mode - -Analyze historical blockchain data by scanning archived blocks: - -```terminal -$ chainhook predicates scan my-predicate.json --mainnet -``` - -Use scanning mode for: -- Testing predicates before deployment -- Analyzing past blockchain events -- Backfilling historical data - -### Service mode - -Monitor live blockchain events in real-time: - -```terminal -$ chainhook service start --config-path=config.toml -``` - -```toml config.toml -[http_api] -http_port = 20456 -database_uri = "redis://localhost:6379" - -[network] -mode = "mainnet" -bitcoind_rpc_url = "http://localhost:8332" -stacks_node_rpc_url = "http://localhost:20443" -``` - -Service mode maintains persistent connections to blockchain nodes and processes new blocks as they arrive. - -## Network configuration - -Configure predicates differently for each network environment: - -```json multi-network-predicate.json -{ - "networks": { - "mainnet": { - "start_block": 100000, - "if_this": { - "scope": "stx_event", - "actions": ["transfer", "mint"] - } - }, - "testnet": { - "start_block": 1, - "decode_clarity_values": true, - "include_proof": true, - "if_this": { - "scope": "print_event" - } - } - } -} -``` - -### Configuration options - -```json -{ - "start_block": 100000, // Begin scanning from this block - "end_block": 200000, // Stop at this block (optional) - "expire_after_occurrence": 5000, // Stop after N matches - "decode_clarity_values": true, // Decode Clarity data types - "include_proof": false, // Include merkle proofs - "include_contract_abi": true // Include contract interfaces -} -``` - -## Further reading - -- [Design predicates](/tools/chainhook/usage) -- [Run Chainhook as a service](/tools/chainhook/service-mode) \ No newline at end of file diff --git a/content/docs/en/tools/chainhook/(platform-usage)/create-enable-chainhooks.mdx b/content/docs/en/tools/chainhook/(platform-usage)/create-enable-chainhooks.mdx new file mode 100644 index 000000000..cd4a8252c --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/create-enable-chainhooks.mdx @@ -0,0 +1,5 @@ +--- +title: Create & Enable Chainhooks +description: Create and enable chainhooks using the Platform UI +--- + diff --git a/content/docs/en/tools/chainhook/(platform-usage)/manage-api-keys.mdx b/content/docs/en/tools/chainhook/(platform-usage)/manage-api-keys.mdx new file mode 100644 index 000000000..258830eb8 --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/manage-api-keys.mdx @@ -0,0 +1,5 @@ +--- +title: Manage API Keys +description: Generate and manage Hiro API keys in the Platform UI +--- + diff --git a/content/docs/en/tools/chainhook/(platform-usage)/meta.json b/content/docs/en/tools/chainhook/(platform-usage)/meta.json new file mode 100644 index 000000000..de89b7c43 --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/meta.json @@ -0,0 +1,11 @@ +{ + "title": "Platform", + "pages": [ + "---Platform---", + "platform-usage", + "platform-quickstart", + "create-enable-chainhooks", + "view-chainhooks", + "manage-api-keys" + ] +} diff --git a/content/docs/en/tools/chainhook/(platform-usage)/platform-quickstart.mdx b/content/docs/en/tools/chainhook/(platform-usage)/platform-quickstart.mdx new file mode 100644 index 000000000..d59e81e49 --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/platform-quickstart.mdx @@ -0,0 +1,5 @@ +--- +title: Platform Quickstart +description: Create your first chainhook in the Hiro Platform in 5 minutes +--- + diff --git a/content/docs/en/tools/chainhook/(platform-usage)/platform-usage.mdx b/content/docs/en/tools/chainhook/(platform-usage)/platform-usage.mdx new file mode 100644 index 000000000..ba8cc5d7b --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/platform-usage.mdx @@ -0,0 +1,59 @@ +--- +title: Platform Usage +description: Manage chainhooks visually with the Hiro Platform web interface +--- + +## Overview + +The Hiro Platform provides a web-based interface for managing chainhooks without writing code. + +| Feature | Platform UI | Chainhook SDK | +|---------|------------|---------------| +| Create chainhooks | ✓ Visual builder | ✓ Programmatic | +| View status & activity | ✓ Dashboard view | ✓ API calls | +| Manage API keys | ✓ UI management | - | +| Edit chainhooks | - | ✓ Full control | +| Automation | - | ✓ CI/CD integration | +| Get started quickly | ✓ No code needed | Requires setup | + +## When to Use the Platform UI + +**Choose the Platform UI when you:** +- Want to create chainhooks visually without code +- Need to view chainhook status and activity in a dashboard +- Are managing API keys +- Want to get started quickly + +**Choose the [Chainhook SDK](/tools/chainhook/chainhook-sdk) when you:** +- Need to edit existing chainhooks (not available in Platform UI) +- Want to automate chainhook operations +- Are integrating chainhook management into your application +- Manage chainhooks programmatically at scale + +## Accessing the Platform + +### Sign In + +1. Visit [platform.hiro.so](https://platform.hiro.so) +2. Sign in with your account +3. Navigate to the Chainhooks section + +:::callout +If you don't have an account, sign up at [platform.hiro.so](https://platform.hiro.so) to get started. +::: + +### Platform Features + +The Platform UI provides: + +- **Visual chainhook builder** - Create chainhooks with form inputs and dropdowns +- **Activity dashboard** - Monitor chainhook status and trigger counts +- **API key management** - Generate and rotate API keys +- **Webhook testing** - Test your webhook endpoints + +## Next Steps + +:::next-steps +* [Platform Quickstart](/tools/chainhook/platform-quickstart): Create your first chainhook in minutes +* [Create & Enable Chainhooks](/tools/chainhook/create-enable-chainhooks): Learn how to set up chainhooks in the UI +::: diff --git a/content/docs/en/tools/chainhook/(platform-usage)/view-chainhooks.mdx b/content/docs/en/tools/chainhook/(platform-usage)/view-chainhooks.mdx new file mode 100644 index 000000000..468a021a0 --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/view-chainhooks.mdx @@ -0,0 +1,5 @@ +--- +title: View Chainhooks +description: View and monitor chainhook status and activity in the Platform UI +--- + diff --git a/content/docs/en/tools/chainhook/index.mdx b/content/docs/en/tools/chainhook/index.mdx index 186eb4185..6f3944a67 100644 --- a/content/docs/en/tools/chainhook/index.mdx +++ b/content/docs/en/tools/chainhook/index.mdx @@ -1,57 +1,38 @@ --- title: Chainhook sidebarTitle: Overview -description: Chainhook is a reorg-aware indexer that serves reliable blockchain data for Bitcoin and Stacks. +description: Chainhook is a webhook service for the Stacks blockchain that lets you register event streams and define precise filters to capture on-chain data as it happens. llm: false --- -## Overview +:::callout +type: warn +### Chainhook 2.0 Beta +Chainhook 2.0 is currently in beta. If you encounter issues or have feedback, please reach out to [beta@hiro.so](mailto:beta@hiro.so). +::: -Chainhook is a reorg-aware indexer that lets you build custom event streams from Bitcoin and Stacks blockchains. Unlike traditional indexers, Chainhook automatically handles blockchain reorganizations, ensuring your data stays accurate without manual reindexing. +## Overview -To explore Chainhook features with AI, copy and paste [llms.txt](/tools/chainhook/llms.txt) into your LLM of choice. +Chainhook makes it easy to subscribe to blockchain activity on Stacks by registering event streams, filtering for exactly the data you care about, and sending it straight to your app in real time. -![Chainhook overview](/images/tools/chainhook/overview.svg) +With Chainhook 2.0, you can manage chainhooks through: +- **[Chainhook SDK](/tools/chainhook/chainhook-sdk)** - TypeScript/JavaScript client for programmatic management +- **[Hiro Platform](/tools/chainhook/platform-usage)** - Web-based UI for visual chainhook creation +- **[Chainhook API](/apis/chainhook-api)** - Direct REST API access -## Key features +## Key Features - **Reorg-aware indexing** - Automatically handles blockchain forks and reorganizations -- **If-this-then-that predicates** - Define custom logic to trigger actions on specific blockchain events -- **Multi-chain support** - Works with both Bitcoin and Stacks blockchains -- **Local development** - Test predicates against historical blockchain data - -## Installation - - - -```terminal !! macOS -$ brew install chainhook -``` - -```terminal !! Linux -$ sudo snap install chainhook -``` - -```terminal !! Windows -$ winget install HiroSystems.Chainhook -``` - -```terminal !! Cargo -$ git clone https://github.com/hirosystems/chainhook.git -$ cd chainhook && cargo chainhook-install -``` - - - -## Next steps +- **Event filtering** - Define custom logic to trigger actions on specific blockchain events +- **Historical evaluation** - Test chainhooks against past blocks for indexing or debugging :::next-steps -- [Quickstart](/tools/chainhook/quickstart): Get started with Chainhook. -- [Register chainhooks locally](/tools/chainhook/register-chainhooks-on-devnet): Register chainhooks on your local blockchain. +- [SDK introduction](/tools/chainhook/introduction): Get started with the Chainhook SDK +- [Migration guide](/tools/chainhook/migration): Migration guide for upgrading to Chainhook 2.0 ::: :::callout type: help -### Need help building with Chainhook? -Reach out to us on the **#chainhook** channel on [Discord](https://stacks.chat/) under the Hiro Developer Tools section. There's also a [weekly office hours](https://www.addevent.com/event/oL21905919) call every Thursday at 11am ET. +### Need help with Chainhook? +Reach out to us on the #chainhook channel on [Discord](https://stacks.chat/) under the Hiro Developer Tools section. ::: diff --git a/content/docs/en/tools/chainhook/meta.json b/content/docs/en/tools/chainhook/meta.json index 4ce4bb6a9..31a8590c4 100644 --- a/content/docs/en/tools/chainhook/meta.json +++ b/content/docs/en/tools/chainhook/meta.json @@ -1,15 +1,5 @@ { "title": "Chainhook", "root": true, - "pages": [ - "---Chainhook---", - "index", - "...(overview)", - "---Chainhook CLI---", - "...(chainhook-cli)", - "---Integrations---", - "...(integrations)", - "---Reference---", - "...reference" - ] + "pages": ["---Chainhook|beta---", "index", "...(overview)", "...(chainhook-sdk)", "...reference"] } diff --git a/content/docs/en/tools/chainhook/reference/bitcoin-scopes.mdx b/content/docs/en/tools/chainhook/reference/bitcoin-scopes.mdx deleted file mode 100644 index 106e3419e..000000000 --- a/content/docs/en/tools/chainhook/reference/bitcoin-scopes.mdx +++ /dev/null @@ -1,281 +0,0 @@ ---- -title: Bitcoin scopes -sidebarTitle: Bitcoin scopes -description: Reference for all available Bitcoin event scopes in Chainhook predicates. ---- - -Bitcoin scopes define the `if_this` conditions in your Chainhook predicates. Each scope type monitors specific on-chain events on the Bitcoin blockchain. - -## txid [#txid] - -`txid` matches transactions by their transaction ID. - -### Signature - -```json -{ - "scope": "txid", - "equals": string -} -``` - -### Parameters - -| Name | Type | Required | Description | -|------|------|----------|-------------| -| `equals` | string | Yes | 32-byte hex encoded transaction ID to match | - -### Example - -```json -{ - "if_this": { - "scope": "txid", - "equals": "0xfaaac1833dc4883e7ec28f61e35b41f896c395f8d288b1a177155de2abd6052f" - } -} -``` - -## outputs [#outputs] - -`outputs` matches transactions based on their output scripts. - -### Available operations - -| Operation | Description | -|-----------|-------------| -| `op_return` | Data embedded in transactions | -| `p2pkh` | Pay-to-Public-Key-Hash outputs | -| `p2sh` | Pay-to-Script-Hash outputs | -| `p2wpkh` | Pay-to-Witness-Public-Key-Hash outputs | -| `p2wsh` | Pay-to-Witness-Script-Hash outputs | -| `descriptor` | Output descriptors for address derivation | - -### op_return - -```json -{ - "scope": "outputs", - "op_return": { - "equals" | "starts_with" | "ends_with": string - } -} -``` - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `equals` | string | No* | Match exact data | -| `starts_with` | string | No* | Match data prefix | -| `ends_with` | string | No* | Match data suffix | - -*One of these parameters is required - -```json -{ - "if_this": { - "scope": "outputs", - "op_return": { - "starts_with": "0xbtc2100" - } - } -} -``` - -### p2pkh - -```json -{ - "scope": "outputs", - "p2pkh": { - "equals": string - } -} -``` - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `equals` | string | Yes | Bitcoin address to match | - -```json -{ - "if_this": { - "scope": "outputs", - "p2pkh": { - "equals": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" - } - } -} -``` - -### p2sh - -```json -{ - "scope": "outputs", - "p2sh": { - "equals": string - } -} -``` - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `equals` | string | Yes | Script hash address to match | - -```json -{ - "if_this": { - "scope": "outputs", - "p2sh": { - "equals": "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy" - } - } -} -``` - -### p2wpkh - -```json -{ - "scope": "outputs", - "p2wpkh": { - "equals": string - } -} -``` - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `equals` | string | Yes | Native SegWit address to match | - -```json -{ - "if_this": { - "scope": "outputs", - "p2wpkh": { - "equals": "bc1qexampleaddress" - } - } -} -``` - -### p2wsh - -```json -{ - "scope": "outputs", - "p2wsh": { - "equals": string - } -} -``` - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `equals` | string | Yes | Native SegWit script address to match | - -```json -{ - "if_this": { - "scope": "outputs", - "p2wsh": { - "equals": "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3" - } - } -} -``` - -### descriptor - -```json -{ - "scope": "outputs", - "descriptor": { - "expression": string, - "range": [number, number] - } -} -``` - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `expression` | string | Yes | Output descriptor string | -| `range` | array | Yes | Index range to derive [start, end] | - -```json -{ - "if_this": { - "scope": "outputs", - "descriptor": { - "expression": "wpkh(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/0/*)", - "range": [0, 10] - } - } -} -``` - -This monitors addresses derived from index 0 to 10 of the provided extended public key. - -## stacks_protocol [#stacks_protocol] - -`stacks_protocol` matches Bitcoin transactions related to Stacks Proof of Transfer operations. - -### Signature - -```json -{ - "scope": "stacks_protocol", - "operation": string -} -``` - -### Parameters - -| Name | Type | Required | Description | -|------|------|----------|-------------| -| `operation` | string | Yes | PoT operation type to match | - -### Available operations - -| Operation | Description | -|-----------|-------------| -| `block_committed` | Stacks block commitments | -| `leader_registered` | Mining leader registrations | -| `inscription_feed` | Ordinal inscription events | -| `stx_transferred` | STX token transfers | -| `stx_locked` | STX token locking events | - -### Examples - -#### Monitor block commitments - -```json -{ - "if_this": { - "scope": "stacks_protocol", - "operation": "block_committed" - } -} -``` - -#### Track STX transfers - -```json -{ - "if_this": { - "scope": "stacks_protocol", - "operation": "stx_transferred" - } -} -``` - -#### Monitor inscription events - -```json -{ - "if_this": { - "scope": "stacks_protocol", - "operation": "inscription_feed" - } -} -``` \ No newline at end of file diff --git a/content/docs/en/tools/chainhook/reference/filters.mdx b/content/docs/en/tools/chainhook/reference/filters.mdx new file mode 100644 index 000000000..2275d39ae --- /dev/null +++ b/content/docs/en/tools/chainhook/reference/filters.mdx @@ -0,0 +1,390 @@ +--- +title: Filters +description: Complete reference for all Chainhook filter types +--- + +Filters define which blockchain events will trigger your chainhook. + +Here is a complete reference for all Chainhook filter types: + +| Filter | When to use | +|--------|-------------| +| `ft_event` | Catch *every* SIP-010 transfer, mint, or burn across assets. | +| `ft_transfer` | Follow a single asset such as USDC; optionally add `sender`/`receiver` for wallet-level triggers. | +| `ft_mint` | Track supply expansions or bridge inflows for one asset (set `asset_identifier`). | +| `ft_burn` | Track redemptions or supply contractions for one asset (set `asset_identifier`). | +| `nft_event` | Monitor every transfer, mint, or burn for all collections. | +| `nft_transfer` | Follow a SIP-009 collection; add `sender`, `receiver`, or `value` for owner/token targeting. | +| `nft_mint` | Watch every new mint for a collection (set `asset_identifier`). | +| `nft_burn` | Catch burns or redemptions (set `asset_identifier`). | +| `stx_event` | Capture all native transfers, mints, or burns. | +| `stx_transfer` | Track every STX transfer; add `sender` or `receiver` to spotlight specific principals. | +| `contract_deploy` | React to new contracts entering the network. | +| `contract_call` | Observe every invocation; narrow with `contract_identifier` and `function_name`. | +| `contract_log` | Catch `print`/log output from a contract (set `contract_identifier`). | +| `coinbase` | Watch miner rewards hitting the chain. | +| `tenure_change` | Track Proof-of-Transfer tenure updates; add `cause` (`"block_found"` or `"extended"`) for specificity. | + +## Fungible Token Events (FT) + +### Any FT Event + +Match any fungible token event (transfer, burn, or mint): + +```json +{ + "type": "ft_event" +} +``` + +### FT Transfer + +Match FT transfers for a specific asset: + +```json +{ + "type": "ft_transfer", + // !mark + "asset_identifier": "SP...ABC.ft::usdc" +} +``` + +Filter by sender: + +```json +{ + "type": "ft_transfer", + "asset_identifier": "SP...ABC.ft::usdc", + // !mark + "sender": "SP...FROM" +} +``` + +Filter by receiver: + +```json +{ + "type": "ft_transfer", + "asset_identifier": "SP...ABC.ft::usdc", + // !mark + "receiver": "SP...TO" +} +``` + +### FT Mint + +Match FT mint events: + +```json +{ + "type": "ft_mint", + "asset_identifier": "SP...ABC.ft::usdc" +} +``` + +### FT Burn + +Match FT burn events: + +```json +{ + "type": "ft_burn", + "asset_identifier": "SP...ABC.ft::usdc" +} +``` + +--- + +## Non-Fungible Token Events (NFT) + +### Any NFT Event + +Match any NFT event (transfer, burn, or mint): + +```json +{ + "type": "nft_event" +} +``` + +### NFT Transfer + +Match NFT transfers for a specific collection: + +```json +{ + "type": "nft_transfer", + // !mark + "asset_identifier": "SP...COLL.nft::collectible" +} +``` + +Filter by sender: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + // !mark + "sender": "SP...FROM" +} +``` + +Filter by receiver: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + // !mark + "receiver": "SP...TO" +} +``` + +Filter by specific token ID: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + // !mark + "value": "u123" +} +``` + +### NFT Mint + +Match NFT mint events: + +```json +{ + "type": "nft_mint", + "asset_identifier": "SP...COLL.nft::collectible" +} +``` + +### NFT Burn + +Match NFT burn events: + +```json +{ + "type": "nft_burn", + "asset_identifier": "SP...COLL.nft::collectible" +} +``` + +--- + +## STX Events + +### Any STX Event + +Match any STX event (transfer, burn, or mint): + +```json +{ + "type": "stx_event" +} +``` + +### STX Transfer + +Match any STX transfer: + +```json +{ + "type": "stx_transfer" +} +``` + +Filter by sender: + +```json +{ + "type": "stx_transfer", + // !mark + "sender": "SP...SENDER" +} +``` + +Filter by receiver: + +```json +{ + "type": "stx_transfer", + // !mark + "receiver": "SP...RECEIVER" +} +``` + +--- + +## Contract Events + +### Contract Deploy + +Match any contract deployment: + +```json +{ + "type": "contract_deploy" +} +``` + +Filter by deployer: + +```json +{ + "type": "contract_deploy", + // !mark + "sender": "SP...DEPLOYER" +} +``` + +### Contract Call + +Match any contract call: + +```json +{ + "type": "contract_call" +} +``` + +Match calls to a specific contract: + +```json +{ + "type": "contract_call", + // !mark + "contract_identifier": "SP...XYZ.counter" +} +``` + +Match calls to a specific function: + +```json +{ + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter", + // !mark + "function_name": "increment" +} +``` + +Filter by caller: + +```json +{ + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter", + "function_name": "increment", + // !mark + "sender": "SP...CALLER" +} +``` + +### Contract Log + +Match any print events: + +```json +{ + "type": "contract_log" +} +``` + +Match contract print events: + +```json +{ + "type": "contract_log", + // !mark + "contract_identifier": "SP...XYZ.counter" +} +``` + +Filter by transaction sender: + +```json +{ + "type": "contract_log", + "contract_identifier": "SP...XYZ.counter", + // !mark + "sender": "SP...SENDER" +} +``` + +--- + +## System Events + +### Coinbase + +Match coinbase events (block rewards): + +```json +{ + "type": "coinbase" +} +``` + +### Tenure Change + +Match any tenure change: + +```json +{ + "type": "tenure_change" +} +``` + +Match tenure changes by cause (block found): + +```json +{ + "type": "tenure_change", + // !mark[/"block_found"/] + "cause": "block_found" +} +``` + +Match tenure changes by cause (extended): + +```json +{ + "type": "tenure_change", + // !mark[/"extended"/] + "cause": "extended" +} +``` + +--- + +## Combining Filters + +You can combine multiple filters in the `filters.events` array. A chainhook will trigger if **any** of the filters match: + +```json +{ + "filters": { + "events": [ + { + "type": "ft_transfer", + "asset_identifier": "SP...ABC.token::diko" + }, + { + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter", + "function_name": "increment" + } + ] + } +} +``` + +:::next-steps +- [Options Reference](/tools/chainhook/reference/options): Configure payload enrichment and evaluation windows +- [Register & Enable](/tools/chainhook/register-enable): Create your first chainhook +::: diff --git a/content/docs/en/tools/chainhook/reference/meta.json b/content/docs/en/tools/chainhook/reference/meta.json new file mode 100644 index 000000000..16999a7fb --- /dev/null +++ b/content/docs/en/tools/chainhook/reference/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Reference", + "pages": ["---Reference---", "filters", "options", "payload-anatomy"] +} diff --git a/content/docs/en/tools/chainhook/reference/options.mdx b/content/docs/en/tools/chainhook/reference/options.mdx new file mode 100644 index 000000000..f10449244 --- /dev/null +++ b/content/docs/en/tools/chainhook/reference/options.mdx @@ -0,0 +1,212 @@ +--- +title: Options +description: Complete reference for all Chainhook configuration options +--- + +Options control payload enrichment and evaluation windows for your chainhook. The `options` field is optional and can be omitted or set to `null`. + +:::callout +All boolean options default to `false` if omitted. Integer options are optional. +::: + +--- + +## Payload options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `decode_clarity_values` | boolean | `false` | Include human-readable Clarity values | +| `include_contract_abi` | boolean | `false` | Include contract ABI on deployments | +| `include_contract_source_code` | boolean | `false` | Include source code on deployments | +| `include_post_conditions` | boolean | `false` | Include post-conditions in metadata | +| `include_raw_transactions` | boolean | `false` | Include raw transaction hex | +| `include_block_metadata` | boolean | `false` | Include block metadata (Stacks & Bitcoin) | +| `include_block_signatures` | boolean | `false` | Include block signatures and signers | +| `enable_on_registration` | boolean | `false` | Enable chainhook immediately on creation | +| `expire_after_evaluations` | integer | none | Expire after N blocks evaluated | +| `expire_after_occurrences` | integer | none | Expire after N matches | + +### decode_clarity_values + +Include human-readable `repr` (and inferred types) alongside `hex` for arguments, logs, and results. + +- **Type**: `boolean` +- **Default**: `false` + +```json +{ + "options": { + "decode_clarity_values": true + } +} +``` + +--- + +### include_contract_abi + +Include the contract ABI on deployment operations. This improves argument typing when decoding. + +- **Type**: `boolean` +- **Default**: `false` + +```json +{ + "options": { + "include_contract_abi": true + } +} +``` + +--- + +### include_contract_source_code + +Include the contract source code on deployment operations. + +- **Type**: `boolean` +- **Default**: `false` + +```json +{ + "options": { + "include_contract_source_code": true + } +} +``` + +--- + +### include_post_conditions + +Include decoded post-conditions in transaction metadata. + +- **Type**: `boolean` +- **Default**: `false` + +```json +{ + "options": { + "include_post_conditions": true + } +} +``` + +--- + +### include_raw_transactions + +Include raw transaction hex in transaction metadata. + +- **Type**: `boolean` +- **Default**: `false` + +```json +{ + "options": { + "include_raw_transactions": true + } +} +``` + +--- + +### include_block_metadata + +Include block metadata for both Stacks and Bitcoin blocks, with execution costs, transaction count, etc. + +- **Type**: `boolean` +- **Default**: `false` + +```json +{ + "options": { + "include_block_metadata": true + } +} +``` + +--- + +### include_block_signatures + +Include signer information and signatures in block metadata. + +- **Type**: `boolean` +- **Default**: `false` + +```json +{ + "options": { + "include_block_signatures": true + } +} +``` + +--- + +## Activation Options + +### enable_on_registration + +Enable the chainhook immediately upon registration/creation. By default, a new chainhook is disabled upon creation. + +- **Type**: `boolean` +- **Default**: `false` + +```json +{ + "options": { + "enable_on_registration": true + } +} +``` + +When set to `true`, the response will include `status.enabled = true` and the chainhook will start processing events immediately. + +--- + +## Expiration Options + +### expire_after_evaluations + +Automatically expire the chainhook after evaluating this many blocks. + +- **Type**: `integer` +- **Default**: None (no expiration) + +```json +{ + "options": { + "expire_after_evaluations": 10000 + } +} +``` + +This chainhook will expire after evaluating 10,000 blocks. + +--- + +### expire_after_occurrences + +Automatically expire the chainhook after this many matching occurrences. + +- **Type**: `integer` +- **Default**: None (no expiration) + +```json +{ + "options": { + "expire_after_occurrences": 250 + } +} +``` + +This chainhook will expire after triggering 250 times. + +--- + +:::next-steps +- [Filter Reference](/tools/chainhook/reference/filters): Learn about all available event filters +- [Payload Anatomy](/tools/chainhook/reference/payload-anatomy): Understand the structure of chainhook payloads +::: diff --git a/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx b/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx new file mode 100644 index 000000000..b7354ca41 --- /dev/null +++ b/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx @@ -0,0 +1,250 @@ +--- +title: Payload anatomy +description: Understanding the structure of Chainhook webhook payloads +--- + +## Payload Overview + +A Chainhook payload consists of two main sections: **event** and **chainhook**: + +- `event` contains the blockchain data (blocks, transactions, operations) +- `chainhook` contains metadata about the chainhook that triggered + +--- + +## Basic Structure + +```json +{ + "event": { + "apply": [...], // Blocks being applied to the chain + "rollback": [...], // Blocks being rolled back (during reorgs) + "chain": "stacks", + "network": "mainnet" + }, + "chainhook": { + "name": "my-chainhook", + "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185" + } +} +``` + +--- + +## Event Section + +### Apply Array + +The `apply` array contains blocks being added to the canonical chain. Each block includes: + +```json +{ + "timestamp": 1757977309, + "block_identifier": { + "hash": "0xa204da7...", + "index": 3549902 + }, + "parent_block_identifier": { + "hash": "0xad0acff...", + "index": 3549901 + }, + "transactions": [...] +} +``` + +### Rollback Array + +The `rollback` array contains blocks being removed during a chain reorganization. Same structure as `apply`. + +:::callout +Chainhook automatically handles reorgs by sending rollback events. Your application should reverse any state changes from rolled-back blocks. +::: + +--- + +## Transaction Structure + +Each transaction in the `transactions` array contains: + +### Metadata + +Transaction-level information: + +```json +{ + "metadata": { + "type": "contract_call", + "nonce": 6689, + "result": { + "hex": "0x0703", + "repr": "(ok true)" + }, + "status": "success", + "fee_rate": "3000", + "position": { + "index": 0, + "microblock_identifier": null + }, + "execution_cost": { + "runtime": "7807", + "read_count": "5", + "read_length": "2441", + "write_count": "2", + "write_length": "1" + }, + "sender_address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9", + "sponsor_address": null, + "canonical": true, + "sponsored": false, + "microblock_canonical": true + } +} +``` + +### Transaction Identifier + +Unique identifier for the transaction: + +```json +{ + "transaction_identifier": { + "hash": "0x09defc9a6cd9318b5c458389d4dd57597203ec539818aec0de3cfcfd7af0c2ab" + } +} +``` + +--- + +## Operations Array + +Each transaction includes an `operations` array describing state changes. Operations are indexed sequentially. + +### Fee Operation + +Transaction fee paid: + +```json +{ + "type": "fee", + "amount": { + "value": "-3000", + "currency": { + "symbol": "STX", + "decimals": 6 + } + }, + "status": "success", + "account": { + "address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9" + }, + "metadata": { + "sponsored": false + }, + "operation_identifier": { + "index": 0 + } +} +``` + +### Contract Call Operation + +Contract function invocation: + +```json +{ + "type": "contract_call", + "status": "success", + "account": { + "address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9" + }, + "metadata": { + "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token", + "function_name": "transfer", + "args": [ + { + "hex": "0x01000000000000000000000002690ed9fe", + "name": "amount", + "repr": "u10352515582", + "type": "uint" + }, + { + "hex": "0x0516362f36a4c7ca0318a59fe6448fd3a0c32bda724d", + "name": "sender", + "repr": "'SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9", + "type": "principal" + }, + { + "hex": "0x05161740f4690c79466e3389a13476586e0cb3e1dfbf", + "name": "recipient", + "repr": "'SPBM1X391HWMCVHKH6GK8XJRDR6B7REZQYQ8KBCK", + "type": "principal" + }, + { + "hex": "0x09", + "name": "memo", + "repr": "none", + "type": "(optional (buff 34))" + } + ] + }, + "operation_identifier": { + "index": 1 + } +} +``` + +:::callout +The `args` array includes `repr` and `type` fields when `decode_clarity_values` is enabled in options. +::: + +### Token Transfer Operation + +FT/NFT/STX transfers: + +```json +{ + "type": "token_transfer", + "amount": { + "value": "-10352515582", + "currency": { + "symbol": "", + "decimals": 0, + "metadata": { + "token_type": "ft", + "asset_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token::diko" + } + } + }, + "status": "success", + "account": { + "address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9" + }, + "operation_identifier": { + "index": 2 + } +} +``` + +### Contract Log Operation + +Print statements from contracts: + +```json +{ + "type": "contract_log", + "status": "success", + "metadata": { + "topic": "print", + "value": "0x09", + "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token" + }, + "operation_identifier": { + "index": 4 + } +} +``` + +:::next-steps +- [Filter Reference](/tools/chainhook/reference/filters): Configure which events trigger your chainhook +- [Options Reference](/tools/chainhook/reference/options): Customize payload content +::: diff --git a/content/docs/en/tools/chainhook/reference/stacks-scopes.mdx b/content/docs/en/tools/chainhook/reference/stacks-scopes.mdx deleted file mode 100644 index 505555cc9..000000000 --- a/content/docs/en/tools/chainhook/reference/stacks-scopes.mdx +++ /dev/null @@ -1,351 +0,0 @@ ---- -title: Stacks scopes -sidebarTitle: Stacks scopes -description: Reference for all available Stacks event scopes in Chainhook predicates. ---- - -Stacks scopes define the `if_this` conditions in your Chainhook predicates. Each scope type monitors specific on-chain events on the Stacks blockchain. - -## txid [#txid] - -`txid` matches transactions by their transaction ID. - -### Signature - -```json -{ - "scope": "txid", - "equals": string -} -``` - -### Parameters - -| Name | Type | Required | Description | -|------|------|----------|-------------| -| `equals` | string | Yes | 32-byte hex encoded transaction ID to match | - -### Example - -```json -{ - "if_this": { - "scope": "txid", - "equals": "0xfaaac1833dc4883e7ec28f61e35b41f896c395f8d288b1a177155de2abd6052f" - } -} -``` - -## block_height [#block_height] - -`block_height` matches blocks by their height. - -### Signature - -```json -{ - "scope": "block_height", - "equals" | "higher_than" | "lower_than" | "between": value -} -``` - -### Parameters - -| Name | Type | Required | Description | -|------|------|----------|-------------| -| `equals` | integer | No* | Match exact block height | -| `higher_than` | integer | No* | Match blocks above height | -| `lower_than` | integer | No* | Match blocks below height | -| `between` | [integer, integer] | No* | Match blocks in range [start, end] | - -*One of these parameters is required - -### Examples - -#### Match exact height - -```json -{ - "if_this": { - "scope": "block_height", - "equals": 141200 - } -} -``` - -#### Match range of blocks - -```json -{ - "if_this": { - "scope": "block_height", - "between": [100000, 110000] - } -} -``` - -## ft_transfer [#ft_transfer] - -`ft_transfer` matches fungible token events. - -### Signature - -```json -{ - "scope": "ft_transfer", - "asset_identifier": string, - "actions": string[] -} -``` - -### Parameters - -| Name | Type | Required | Description | -|------|------|----------|-------------| -| `asset_identifier` | string | Yes | Fully qualified token identifier | -| `actions` | string[] | Yes | Token actions: `mint`, `transfer`, `burn` | - -### Examples - -#### Monitor token transfers - -```json -{ - "if_this": { - "scope": "ft_transfer", - "asset_identifier": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.cbtc-token::cbtc", - "actions": ["transfer"] - } -} -``` - -#### Track all token activities - -```json -{ - "if_this": { - "scope": "ft_transfer", - "asset_identifier": "SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-10-token::token", - "actions": ["mint", "transfer", "burn"] - } -} -``` - -## nft_transfer [#nft_transfer] - -`nft_transfer` matches non-fungible token events. - -### Signature - -```json -{ - "scope": "nft_transfer", - "asset_identifier": string, - "actions": string[] -} -``` - -### Parameters - -| Name | Type | Required | Description | -|------|------|----------|-------------| -| `asset_identifier` | string | Yes | Fully qualified NFT identifier | -| `actions` | string[] | Yes | NFT actions: `mint`, `transfer`, `burn` | - -### Examples - -#### Monitor NFT mints - -```json -{ - "if_this": { - "scope": "nft_transfer", - "asset_identifier": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.monkey-sip09::monkeys", - "actions": ["mint"] - } -} -``` - -#### Track all NFT activities - -```json -{ - "if_this": { - "scope": "nft_transfer", - "asset_identifier": "SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.megapont-ape-club::apes", - "actions": ["mint", "transfer", "burn"] - } -} -``` - -## stx_transfer [#stx_transfer] - -`stx_transfer` matches STX token events. - -### Signature - -```json -{ - "scope": "stx_transfer", - "actions": string[] -} -``` - -### Parameters - -| Name | Type | Required | Description | -|------|------|----------|-------------| -| `actions` | string[] | Yes | STX actions: `mint`, `transfer`, `burn`, `lock` | - -### Examples - -#### Monitor STX transfers - -```json -{ - "if_this": { - "scope": "stx_transfer", - "actions": ["transfer"] - } -} -``` - -#### Track all STX activities - -```json -{ - "if_this": { - "scope": "stx_transfer", - "actions": ["mint", "transfer", "burn", "lock"] - } -} -``` - -## print_event [#print_event] - -`print_event` matches contract print events. - -### Signature - -```json -{ - "scope": "print_event", - "contract_identifier": string, - "contains" | "matches_regex": string -} -``` - -### Parameters - -| Name | Type | Required | Description | -|------|------|----------|-------------| -| `contract_identifier` | string | Yes | Fully qualified contract identifier | -| `contains` | string | No* | Match events containing string | -| `matches_regex` | string | No* | Match events by regex pattern | - -*One of these parameters is required - -### Examples - -#### Match by substring - -```json -{ - "if_this": { - "scope": "print_event", - "contract_identifier": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.monkey-sip09", - "contains": "monkey" - } -} -``` - -#### Match by regex pattern - -```json -{ - "if_this": { - "scope": "print_event", - "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-dao", - "matches_regex": "vault-liquidated-.*" - } -} -``` - -## contract_call [#contract_call] - -`contract_call` matches specific contract function calls. - -### Signature - -```json -{ - "scope": "contract_call", - "contract_identifier": string, - "method": string -} -``` - -### Parameters - -| Name | Type | Required | Description | -|------|------|----------|-------------| -| `contract_identifier` | string | Yes | Fully qualified contract identifier | -| `method` | string | Yes | Contract method name | - -### Example - -```json -{ - "if_this": { - "scope": "contract_call", - "contract_identifier": "SP000000000000000000002Q6VF78.pox", - "method": "stack-stx" - } -} -``` - -## contract_deployment [#contract_deployment] - -`contract_deployment` matches contract deployments. - -### Signature - -```json -{ - "scope": "contract_deployment", - "deployer" | "implement_trait": string -} -``` - -### Parameters - -| Name | Type | Required | Description | -|------|------|----------|-------------| -| `deployer` | string | No* | STX address of deployer | -| `implement_trait` | string | No* | Trait the contract must implement | - -*One of these parameters is required - -### Examples - -#### Monitor deployments by address - -```json -{ - "if_this": { - "scope": "contract_deployment", - "deployer": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" - } -} -``` - -#### Monitor trait implementations - -```json -{ - "if_this": { - "scope": "contract_deployment", - "implement_trait": "SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-09-trait" - } -} -``` \ No newline at end of file diff --git a/content/docs/en/tools/contract-monitoring/index.mdx b/content/docs/en/tools/contract-monitoring/index.mdx index 10d06ea79..8ad4b527c 100644 --- a/content/docs/en/tools/contract-monitoring/index.mdx +++ b/content/docs/en/tools/contract-monitoring/index.mdx @@ -7,8 +7,6 @@ llm: false ## Overview -To explore Contract monitoring features with AI, copy and paste [llms.txt](/tools/contract-monitoring/llms.txt) into your LLM of choice. - ## Key features - **Real-time notifications** - Target specific events and receive webhooks within seconds diff --git a/content/docs/es/apis/chainhook-api/index.mdx b/content/docs/es/apis/chainhook-api/index.mdx new file mode 100644 index 000000000..1525d4c06 --- /dev/null +++ b/content/docs/es/apis/chainhook-api/index.mdx @@ -0,0 +1,102 @@ +--- +title: Chainhook API +sidebarTitle: Descripción general +description: API REST para gestionar Chainhooks programáticamente +--- +## Descripción general + +The Chainhook API is a REST API that allows you to programmatically manage chainhooks, configure event filters, and control webhook delivery. It provides the same functionality as the [SDK de Chainhook](/tools/chainhook/chainhook-sdk) through direct HTTP requests. + +Usa la API de Chainhook cuando: + +* Estás trabajando en un lenguaje sin soporte de SDK +* You need direct HTTP control over chainhook operations +* Estás construyendo integraciones personalizadas o flujos de trabajo de automatización +* You prefer REST APIs over client libraries + +## Características Principales + +* **Gestión completa de chainhook** - Crear, leer, actualizar y eliminar chainhooks +* **Filtrado de eventos** - Configurar filtros para eventos FT, NFT, STX, de contrato y del sistema +* **Configuración de webhook** - Set up HTTP POST endpoints for event delivery +* **Endpoints de evaluación** - Probar chainhooks contra bloques históricos +* **Operaciones en lote** - Habilitar o deshabilitar múltiples chainhooks a la vez +* **Gestión de secretos** - Rotar secretos del consumidor de webhook por seguridad + +## URLs Base + +La API de Chainhook está disponible tanto en mainnet como en testnet: + +``` +Testnet: https://api.testnet.hiro.so/chainhooks/v1 +Mainnet: https://api.mainnet.hiro.so/chainhooks/v1 +``` + +## Autenticación + +Todas las solicitudes de API requieren autenticación usando una clave API de Hiro en el `x-api-key` header: + +```bash +curl https://api.testnet.hiro.so/chainhooks/v1/me/ \ + -H "x-api-key: YOUR_API_KEY" +``` + +Obtén tu clave de API de [platform.hiro.so](https://platform.hiro.so). + +## Ejemplo Rápido + +Registrar un nuevo chainhook para monitorear transferencias de FT: + +```bash +curl -X POST https://api.testnet.hiro.so/chainhooks/v1/me/ \ + -H "x-api-key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "ft-transfer-monitor", + "version": "1", + "chain": "stacks", + "network": "testnet", + "filters": { + "events": [{ + "type": "ft_transfer", + "asset_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.usda-token::usda" + }] + }, + "action": { + "type": "http_post", + "url": "https://example.com/webhooks" + }, + "options": { + "enable_on_registration": true + } + }' +``` + +## SDK vs REST API + +Both the SDK and REST API provide identical functionality: + +| Feature | SDK | REST API | +|---------|-----|----------| +| Language | TypeScript/JavaScript | Any language | +| Type Safety | Yes | No | +| Authentication | Automatic | Manual headers | +| Error Handling | Built-in | Custom | +| Best For | Node.js/Bun apps | Other languages, scripts | + +Para proyectos de TypeScript/JavaScript, recomendamos usar el [SDK de Chainhook](/tools/chainhook/chainhook-sdk) para una mejor experiencia de desarrollador. + +## Próximos Pasos + +:::next-steps +* [Guía de Uso](/apis/chainhook-api/usage): Autenticación y mejores prácticas +* [Referencia de API](/apis/chainhook-api/reference): Documentación completa del endpoint +::: + +:::callout +type: help + +### ¿Necesitas ayuda construyendo con la API de Chainhook? + +Contáctanos en el **#chainhook** canal activado [Discord](https://stacks.chat/) bajo Herramientas de Desarrollador de Hiro. También hay un [horario de oficina semanal](https://www.addevent.com/event/oL21905919) llamada todos los jueves a las 11am ET. +::: diff --git a/content/docs/es/apis/chainhook-api/reference/chainhooks/bulk-enable-chainhooks.mdx b/content/docs/es/apis/chainhook-api/reference/chainhooks/bulk-enable-chainhooks.mdx new file mode 100644 index 000000000..aab03f9aa --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/chainhooks/bulk-enable-chainhooks.mdx @@ -0,0 +1,7 @@ +--- +title: Habilitar/deshabilitar estado en lote +sidebarTitle: Habilitar/deshabilitar estado en lote +description: Actualiza el estado habilitado de chainhooks que coinciden con los filtros proporcionados +full: true +--- + diff --git a/content/docs/es/apis/chainhook-api/reference/chainhooks/delete-chainhook.mdx b/content/docs/es/apis/chainhook-api/reference/chainhooks/delete-chainhook.mdx new file mode 100644 index 000000000..849a0ca1e --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/chainhooks/delete-chainhook.mdx @@ -0,0 +1,7 @@ +--- +title: Eliminar +sidebarTitle: Eliminar +description: Elimina un chainhook por su UUID +full: true +--- + diff --git a/content/docs/es/apis/chainhook-api/reference/chainhooks/evaluate-chainhook.mdx b/content/docs/es/apis/chainhook-api/reference/chainhooks/evaluate-chainhook.mdx new file mode 100644 index 000000000..9d44c880c --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/chainhooks/evaluate-chainhook.mdx @@ -0,0 +1,7 @@ +--- +title: Evaluar un bloque específico +sidebarTitle: Evaluar un bloque específico +description: Pone en cola un trabajo de evaluación bajo demanda para un bloque específico +full: true +--- + diff --git a/content/docs/es/apis/chainhook-api/reference/chainhooks/get-chainhook.mdx b/content/docs/es/apis/chainhook-api/reference/chainhooks/get-chainhook.mdx new file mode 100644 index 000000000..4c2e4fd93 --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/chainhooks/get-chainhook.mdx @@ -0,0 +1,7 @@ +--- +title: Obtener un chainhook específico +sidebarTitle: Obtener un chainhook específico +description: Devuelve un chainhook por su UUID +full: true +--- + diff --git a/content/docs/es/apis/chainhook-api/reference/chainhooks/get-chainhooks.mdx b/content/docs/es/apis/chainhook-api/reference/chainhooks/get-chainhooks.mdx new file mode 100644 index 000000000..727cf93b1 --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/chainhooks/get-chainhooks.mdx @@ -0,0 +1,7 @@ +--- +title: Obtener todos los chainhooks +sidebarTitle: Obtener todos los chainhooks +description: Devuelve todos los chainhooks registrados por el usuario actual +full: true +--- + diff --git a/content/docs/es/apis/chainhook-api/reference/chainhooks/index.mdx b/content/docs/es/apis/chainhook-api/reference/chainhooks/index.mdx new file mode 100644 index 000000000..c23248ba7 --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/chainhooks/index.mdx @@ -0,0 +1,6 @@ +--- +title: Chainhooks +index: true +full: true +description: Gestionar endpoints de la API Chainhook para crear, actualizar y evaluar chainhooks. +--- diff --git a/content/docs/es/apis/chainhook-api/reference/chainhooks/register-chainhook.mdx b/content/docs/es/apis/chainhook-api/reference/chainhooks/register-chainhook.mdx new file mode 100644 index 000000000..16705801e --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/chainhooks/register-chainhook.mdx @@ -0,0 +1,7 @@ +--- +title: Crear +sidebarTitle: Crear +description: Permite a los usuarios crear/registrar un nuevo Chainhook +full: true +--- + diff --git a/content/docs/es/apis/chainhook-api/reference/chainhooks/update-chainhook-enabled.mdx b/content/docs/es/apis/chainhook-api/reference/chainhooks/update-chainhook-enabled.mdx new file mode 100644 index 000000000..7a7309479 --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/chainhooks/update-chainhook-enabled.mdx @@ -0,0 +1,7 @@ +--- +title: Habilitar/deshabilitar estado +sidebarTitle: Habilitar/deshabilitar estado +description: Cambia el estado habilitado de un chainhook por su UUID +full: true +--- + diff --git a/content/docs/es/apis/chainhook-api/reference/chainhooks/update-chainhook.mdx b/content/docs/es/apis/chainhook-api/reference/chainhooks/update-chainhook.mdx new file mode 100644 index 000000000..980b25ed6 --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/chainhooks/update-chainhook.mdx @@ -0,0 +1,7 @@ +--- +title: Actualizar +sidebarTitle: Actualizar +description: Actualiza un chainhook por su UUID +full: true +--- + diff --git a/content/docs/es/apis/chainhook-api/reference/info/index.mdx b/content/docs/es/apis/chainhook-api/reference/info/index.mdx new file mode 100644 index 000000000..51d7272c9 --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/info/index.mdx @@ -0,0 +1,6 @@ +--- +title: Información +index: true +full: true +description: Verificar el estado actual de la API Chainhook. +--- diff --git a/content/docs/es/apis/chainhook-api/reference/info/status.mdx b/content/docs/es/apis/chainhook-api/reference/info/status.mdx new file mode 100644 index 000000000..5975dfab1 --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/info/status.mdx @@ -0,0 +1,7 @@ +--- +title: Estado de la API +sidebarTitle: Estado de la API +description: Muestra el estado de la API y su carga de trabajo actual +full: true +--- + diff --git a/content/docs/es/apis/chainhook-api/reference/secrets/delete-consumer-secret.mdx b/content/docs/es/apis/chainhook-api/reference/secrets/delete-consumer-secret.mdx new file mode 100644 index 000000000..fcfc3a49d --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/secrets/delete-consumer-secret.mdx @@ -0,0 +1,7 @@ +--- +title: Eliminar secreto del consumidor +sidebarTitle: Eliminar secreto del consumidor +description: Elimina el secreto del consumidor de carga útil de eventos de Chainhooks +full: true +--- + diff --git a/content/docs/es/apis/chainhook-api/reference/secrets/index.mdx b/content/docs/es/apis/chainhook-api/reference/secrets/index.mdx new file mode 100644 index 000000000..b470ead08 --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/secrets/index.mdx @@ -0,0 +1,6 @@ +--- +title: Secretos +index: true +full: true +description: Rota o elimina los secretos del consumidor de Chainhook que aseguran tus webhooks. +--- diff --git a/content/docs/es/apis/chainhook-api/reference/secrets/rotate-consumer-secret.mdx b/content/docs/es/apis/chainhook-api/reference/secrets/rotate-consumer-secret.mdx new file mode 100644 index 000000000..f54f7cdb6 --- /dev/null +++ b/content/docs/es/apis/chainhook-api/reference/secrets/rotate-consumer-secret.mdx @@ -0,0 +1,7 @@ +--- +title: Rotar secreto del consumidor +sidebarTitle: Rotar secreto del consumidor +description: Genera y devuelve un nuevo secreto de consumidor de payload de eventos Chainhooks +full: true +--- + diff --git a/content/docs/es/apis/chainhook-api/usage.mdx b/content/docs/es/apis/chainhook-api/usage.mdx new file mode 100644 index 000000000..1b75cd042 --- /dev/null +++ b/content/docs/es/apis/chainhook-api/usage.mdx @@ -0,0 +1,287 @@ +--- +title: Uso +description: Aprende cómo autenticar, realizar solicitudes y manejar respuestas con la API de Chainhook +--- +## Autenticación + +Todas las solicitudes de la API de Chainhook requieren autenticación usando una clave API de Hiro. + +### Obtén Tu Clave API + +1. Visita [platform.hiro.so](https://platform.hiro.so) +2. Inicia sesión o crea una cuenta +3. Navegar a Claves de API +4. Genera o copia tu clave API + +### Usando tu Clave de API + +Incluye tu clave de API en el `x-api-key` encabezado para todas las solicitudes: + +```bash +curl https://api.testnet.hiro.so/chainhooks/v1/me/ \ + -H "x-api-key: YOUR_API_KEY" +``` + +**Mejores prácticas de seguridad:** + +* Almacena las claves de API en variables de entorno, nunca en el código +* Use diferentes claves para desarrollo y producción +* Rotar claves periódicamente +* Nunca confirmes claves al control de versiones + +## URLs Base + +Usa la URL base apropiada para tu entorno: + +``` +Testnet: https://api.testnet.hiro.so/chainhooks/v1 +Mainnet: https://api.mainnet.hiro.so/chainhooks/v1 +``` + +**Siempre prueba primero en testnet** antes de hacer el despliegue en la red principal. + +## Formato de Solicitud + +### Encabezados + +Todas las solicitudes deben incluir: + +```http +Content-Type: application/json +x-api-key: YOUR_API_KEY +``` + +### Cuerpo de la Solicitud + +Most endpoints accept JSON request bodies. Example for registering a chainhook: + +```bash +curl -X POST https://api.testnet.hiro.so/chainhooks/v1/me/ \ + -H "x-api-key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "my-chainhook", + "version": "1", + "chain": "stacks", + "network": "testnet", + "filters": { + "events": [{ + "type": "stx_transfer", + "sender": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM" + }] + }, + "action": { + "type": "http_post", + "url": "https://example.com/webhooks" + } + }' +``` + +## Formato de Respuesta + +### Respuestas de Éxito + +Successful responses return JSON with relevant data: + +```json +{ + "uuid": "123e4567-e89b-12d3-a456-426614174000", + "definition": { + "name": "my-chainhook", + "version": "1", + "chain": "stacks", + "network": "testnet", + ... + }, + "status": { + "status": "new", + "enabled": false, + "created_at": 1234567890, + ... + } +} +``` + +### HTTP Status Codes + +| Code | Meaning | Description | +|------|---------|-------------| +| 200 | OK | Request succeeded, response body contains data | +| 204 | No Content | Request succeeded, no response body | +| 400 | Bad Request | Invalid request format or parameters | +| 401 | Unauthorized | Missing or invalid API key | +| 404 | Not Found | Chainhook UUID not found | +| 429 | Too Many Requests | Rate limit exceeded | +| 500 | Server Error | Internal server error | + +### Respuestas de Error + +Las respuestas de error incluyen detalles sobre lo que salió mal: + +```json +{ + "error": "Invalid request body", + "details": "filters.events is required" +} +``` + +## Patrones Comunes + +### Listar Todos los Chainhooks + +```bash +curl https://api.testnet.hiro.so/chainhooks/v1/me/ \ + -H "x-api-key: YOUR_API_KEY" +``` + +### Obtener un Chainhook Específico + +```bash +curl https://api.testnet.hiro.so/chainhooks/v1/me/{uuid} \ + -H "x-api-key: YOUR_API_KEY" +``` + +### Habilitar un Chainhook + +```bash +curl -X PATCH https://api.testnet.hiro.so/chainhooks/v1/me/{uuid}/enabled \ + -H "x-api-key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"enabled": true}' +``` + +### Eliminar un Chainhook + +```bash +curl -X DELETE https://api.testnet.hiro.so/chainhooks/v1/me/{uuid} \ + -H "x-api-key: YOUR_API_KEY" +``` + +### Evaluar Contra un Bloque + +Prueba tu chainhook contra un bloque específico: + +```bash +curl -X POST https://api.testnet.hiro.so/chainhooks/v1/me/{uuid}/evaluate \ + -H "x-api-key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"block_height": 150000}' +``` + +### Activar/Desactivar en Lote + +Habilitar o deshabilitar múltiples chainhooks que coinciden con filtros: + +```bash +curl -X PATCH https://api.testnet.hiro.so/chainhooks/v1/me/enabled \ + -H "x-api-key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "enabled": false, + "filters": { + "webhook_url": "https://old-endpoint.com/webhooks" + } + }' +``` + +## Límites de Tasa + +La API de Chainhook aplica límites de velocidad basados en tu nivel de suscripción: + +* Nivel gratuito: 100 solicitudes por minuto +* Nivel Pro: 1000 solicitudes por minuto +* Enterprise: Límites personalizados + +Cuando esté limitado por la tasa, recibirá un `429 Too Many Requests` respuesta. Implementa retroceso exponencial en tu aplicación: + +```javascript +async function makeRequestWithRetry(url, options, maxRetries = 3) { + for (let i = 0; i < maxRetries; i++) { + const response = await fetch(url, options); + + if (response.status === 429) { + const delay = Math.pow(2, i) * 1000; + await new Promise(resolve => setTimeout(resolve, delay)); + continue; + } + + return response; + } + + throw new Error('Max retries exceeded'); +} +``` + +## Seguridad de Webhook + +### Secreto del Consumidor + +Cuando registras un chainhook, las cargas útiles del webhook incluyen un `x-chainhook-consumer-secret` header. Valida este secreto en tu endpoint de webhook: + +```javascript +app.post('/webhooks', (req, res) => { + const receivedSecret = req.headers['x-chainhook-consumer-secret']; + const expectedSecret = process.env.CHAINHOOK_CONSUMER_SECRET; + + if (receivedSecret !== expectedSecret) { + return res.status(401).send('Unauthorized'); + } + + // Process webhook payload + res.sendStatus(200); +}); +``` + +### Rotar Secreto + +Rota periódicamente tu secreto de consumidor: + +```bash +curl -X POST https://api.testnet.hiro.so/chainhooks/v1/me/secret \ + -H "x-api-key: YOUR_API_KEY" +``` + +Almacena el nuevo secreto de forma segura y actualiza tu endpoint de webhook. + +## Mejores Prácticas + +1. **Comenzar en testnet** - Siempre prueba los chainhooks en testnet antes de mainnet +2. **Habilitar gradualmente** - Crear chainhooks deshabilitados, probar con `evaluate`, luego habilita +3. **Manejar errores** - Implementar manejo adecuado de errores y reintentos +4. **Validar webhooks** - Siempre verificar el encabezado del secreto del consumidor +5. **Use HTTPS** - Webhook URLs must use HTTPS for security +6. **Supervisar uso** - Rastrea el uso de la API para mantenerte dentro de los límites de velocidad +7. **Control de versiones** - Documenta tus configuraciones de chainhook +8. **Limpiar** - Eliminar chainhooks no utilizados para reducir costos + +## Paginación + +Los endpoints de lista admiten paginación a través de parámetros de consulta: + +```bash +# Get first page (default: 20 results) +curl "https://api.testnet.hiro.so/chainhooks/v1/me/?limit=20&offset=0" \ + -H "x-api-key: YOUR_API_KEY" + +# Get next page +curl "https://api.testnet.hiro.so/chainhooks/v1/me/?limit=20&offset=20" \ + -H "x-api-key: YOUR_API_KEY" +``` + +La respuesta incluye metadatos de paginación: + +```json +{ + "limit": 20, + "offset": 0, + "total": 45, + "results": [...] +} +``` + +## Próximos Pasos + +:::next-steps +* [Referencia de API](/apis/chainhook-api/reference): Documentación completa del endpoint +* [Referencia de Filtros](/tools/chainhook/reference/filters): Sintaxis de filtro de eventos +::: diff --git a/content/docs/es/resources/archive/download-guide.mdx b/content/docs/es/resources/archive/download-guide.mdx index 08141c98e..a926bdca5 100644 --- a/content/docs/es/resources/archive/download-guide.mdx +++ b/content/docs/es/resources/archive/download-guide.mdx @@ -1,31 +1,31 @@ --- -title: Cómo descargar los archivos de Hiro -description: Guía completa para descargar archivos de archivo grandes de manera confiable con consejos para solucionar problemas. +title: Cómo descargar archivos de Hiro +description: Guía completa para descargar archivos de archivo grandes de manera confiable con consejos de resolución de problemas. --- -## Visión general +## Descripción general -Los archivos de Hiro Archive son grandes conjuntos de datos (que van desde varios GB hasta varios *cien* ES) alojado en Google Cloud Storage. Debido a su tamaño, las descargas pueden verse interrumpidas por problemas de red, límites de velocidad o tiempos de espera de conexión. Esta guía proporciona múltiples métodos de descarga y soluciones para resolver problemas. +Los archivos Hiro Archive son conjuntos de datos grandes (que van desde varios GB a varios *cien* GB) alojados en Google Cloud Storage. Debido a su tamaño, las descargas pueden verse interrumpidas por problemas de red, límites de velocidad o tiempos de espera de conexión. Esta guía proporciona múltiples métodos de descarga y soluciones de resolución de problemas. -## Tamaños y requisitos de archivos +## Tamaños de archivos y requisitos Antes de descargar, asegúrate de tener suficiente: * **Espacio en disco**: Los archivos van desde 10GB (APIs) hasta varios cientos de GB+ (datos de blockchain) -* **Ancho de banda**: Las descargas pueden tardar horas o días dependiendo de tu conexión -* **Almacenamiento para extracción**: Los archivos comprimidos se expanden significativamente cuando se extraen +* **Ancho de banda**: Las descargas pueden tomar horas o días dependiendo de tu conexión +* **Almacenamiento para extracción**: Los archivos se expanden significativamente cuando se extraen ## Métodos de descarga -### Método 1: wget con reanudación (Recomendado para la mayoría de los usuarios) +### Método 1: wget con reanudación (Recomendado para la mayoría de usuarios) -El `wget` comando con el `-c` la bandera permite reanudar descargas interrumpidas: +El `wget` comando con el `-c` flag permite reanudar descargas interrumpidas: :::callout type: info ### Usuarios de macOS -Es posible que necesite instalar wget primero: `brew install wget`. Alternativamente, utilice el método curl que se muestra a continuación, el cual viene preinstalado en macOS. +Es posible que necesites instalar wget primero: `brew install wget`. Alternativamente, usa el método curl a continuación que está preinstalado en macOS. ::: ```terminal @@ -35,8 +35,8 @@ $ wget -c --progress=bar:force:noscroll https://archive.hiro.so/mainnet/stacks-b **Ventajas:** -* Reanuda automáticamente las descargas interrumpidas -* Incorporado en la mayoría de los sistemas Unix +* Reanuda descargas interrumpidas automáticamente +* Integrado en la mayoría de sistemas Unix * Fácil de usar **Desventajas:** @@ -46,7 +46,7 @@ $ wget -c --progress=bar:force:noscroll https://archive.hiro.so/mainnet/stacks-b ### Método 2: curl con reintentos -Usar `curl` con reintentos automáticos para descargas robustas. El `--continue-at -` la bandera reanuda las descargas parciales, mientras que `--output` especifica el nombre del archivo: +Usar `curl` con reintentos automáticos para descargas robustas. El `--continue-at -` flag reanuda descargas parciales, mientras `--output` especifica el nombre del archivo: ```terminal $ curl --continue-at - --retry 10 --retry-delay 5 --retry-max-time 0 \ @@ -61,9 +61,9 @@ $ curl --continue-at - --retry 10 --retry-delay 5 --retry-max-time 0 \ * Reanudar capacidad con `-C -` * Más opciones de configuración -### Método 3: gcloud storage cp (El más rápido, requiere autenticación) +### Método 3: gcloud storage cp (Más rápido, requiere autenticación) -Google Cloud CLI proporciona las velocidades de descarga más rápidas con transferencias paralelas. Primero autentícate con `gcloud auth login`, luego descargue el archivo al disco o transmita directamente a la extracción: +Google Cloud CLI proporciona las velocidades de descarga más rápidas con transferencias paralelas. Primero autentica con `gcloud auth login`, entonces descarga el archivo al disco o transmite directamente a la extracción: #### Descargar archivo al directorio actual @@ -79,54 +79,54 @@ $ gcloud storage cp gs://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stack **Ventajas:** -* Descargas significativamente más rápidas (mejora de velocidad de 2 a 3 veces) -* Transferencias paralelas incorporadas +* Descargas significativamente más rápidas (mejora de velocidad de 2-3x) +* Transferencias paralelas integradas * Manejo automático de reintentos * Puede transmitir directamente a la extracción (útil cuando el espacio en disco es limitado, pero desactiva las transferencias paralelas) **Desventajas:** * Requiere autenticación de cuenta de Google -* Se requiere la instalación de software adicional +* Instalación de software adicional necesaria -### Método 4: Gestores de descargas (JDownloader) +### Método 4: Gestores de descarga (JDownloader) Para usuarios que prefieren aplicaciones con interfaz gráfica o necesitan gestión avanzada de descargas: 1. Descargar e instalar [JDownloader](https://jdownloader.org/download/index) -2. Copie la URL del archivo en JDownloader +2. Copia la URL del archivo en JDownloader 3. Configurar conexiones paralelas para descargas más rápidas **Ventajas:** * Interfaz gráfica -* Descarga en paralelo -* Mecanismos avanzados de reintento +* Descarga paralela +* Mecanismos de reintento avanzados * Soporte multiplataforma ## Verificación y extracción -Después de descargar, verifique la integridad del archivo: +Después de descargar, verifica la integridad del archivo: :::callout type: info -### Disponibilidad de Suma de Comprobación +### Disponibilidad de suma de verificación SHA256 checksum files are available for **todos los archivos** para verificar la integridad de la descarga. ::: -1. **Descargue el archivo de suma de comprobación:** +1. **Descarga el archivo de suma de verificación:** ```terminal $ wget https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.sha256 ``` -2. **Verifique la descarga:** +2. **Verifica la descarga:** ```terminal $ echo "$(cat mainnet-stacks-blockchain-latest.sha256 | awk '{print $1}') mainnet-stacks-blockchain-latest.tar.gz" | shasum -a 256 -c ``` -3. **Extraer el archivo:** +3. **Extrae el archivo:** ```terminal $ tar -zxvf mainnet-stacks-blockchain-latest.tar.gz -C /target/directory ``` @@ -136,25 +136,25 @@ type: info ### Extracción de archivos grandes -El `marf.sqlite.blobs` El archivo puede ser muy grande y puede llevar un tiempo considerable extraerlo. Asegúrese de tener suficiente espacio en disco y sea paciente durante la extracción. +El `marf.sqlite.blobs` el archivo puede ser muy grande y puede tomar un tiempo considerable para extraer. Asegúrese de tener suficiente espacio en disco y sea paciente durante la extracción. ::: ## Consejos de rendimiento -1. **Utiliza gcloud para las descargas más rápidas** - requiere autenticación pero proporciona mejoras significativas de velocidad -2. **Descargar durante horas de menor demanda** - típicamente tarde en la noche o temprano en la mañana -3. **Utilizar conexiones por cable** - evitar el Wi-Fi para descargas grandes cuando sea posible -4. **Monitorear el espacio en disco** - los archivos extraídos pueden ser de 2 a 3 veces más grandes que los archivos comprimidos -5. **Considere la extracción por transmisión** con gcloud para ahorrar espacio en disco +1. **Usa gcloud para descargas más rápidas** - requiere autenticación pero proporciona mejoras significativas de velocidad +2. **Descargar durante las horas de menor actividad** - típicamente tarde en la noche o temprano en la mañana +3. **Usar conexiones por cable** - evita el Wi-Fi para descargas grandes cuando sea posible +4. **Monitorear espacio en disco** - los archivos extraídos pueden ser 2-3x más grandes que los archivos comprimidos +5. **Considera la extracción por streaming** con gcloud para ahorrar espacio en disco -## Preguntas frecuentes +## Preguntas Frecuentes - - + + ¿Por qué siguen fallando las descargas? - Las descargas de archivos grandes desde el almacenamiento en la nube pueden interrumpirse debido a problemas de red, limitación de velocidad o tiempos de espera de conexión. Utilice herramientas con capacidad de reanudación como `wget -c` o `gcloud storage cp`. + Las descargas de archivos grandes desde el almacenamiento en la nube pueden interrumpirse debido a problemas de red, limitación de velocidad o tiempos de espera de conexión. Utiliza herramientas con capacidad de reanudación como `wget -c` o `gcloud storage cp`. @@ -162,31 +162,31 @@ El `marf.sqlite.blobs` El archivo puede ser muy grande y puede llevar un tiempo ¿Qué método de descarga debo usar? - Para obtener las velocidades más rápidas, use `gcloud storage cp` (requiere autenticación de Google). Para simplificar, use `wget -c`Para obtener fiabilidad sin autenticación, pruebe un gestor de descargas como JDownloader. + Para obtener las velocidades más rápidas, usa `gcloud storage cp` (requiere autenticación de Google). Para simplificar, use `wget -c`. Para mayor confiabilidad sin autenticación, prueba un gestor de descargas como JDownloader. - ¿Cuánto tiempo debería tardar una descarga? + ¿Cuánto tiempo debería tomar una descarga? - El tiempo de descarga varía según el tamaño del archivo y la velocidad de conexión. El archivo de la cadena de bloques de la red principal (varios cientos de GB o más) puede tardar entre 6 y 24 horas o más en conexiones de banda ancha típicas. + El tiempo de descarga varía según el tamaño del archivo y la velocidad de conexión. El archivo de blockchain de mainnet (varios cientos de GB+) puede tomar de 6 a 24+ horas en conexiones de banda ancha típicas. - + ¿Puedo reanudar una descarga fallida? - Sí, use `wget -c`, `curl -C -`, o `gcloud storage cp` para reanudar las descargas interrumpidas desde donde se detuvieron. + Sí, usar `wget -c`, `curl -C -`, o `gcloud storage cp` para reanudar descargas interrumpidas desde donde se detuvieron. - - ¿Por qué gcloud es más rápido? + + ¿Por qué es gcloud más rápido? - Google Cloud CLI utiliza transferencias paralelas y protocolos optimizados al descargar desde Google Cloud Storage, lo que resulta en mejoras de velocidad de 2 a 3 veces. + Google Cloud CLI utiliza transferencias paralelas y protocolos optimizados al descargar desde Google Cloud Storage, lo que resulta en mejoras de velocidad de 2-3x. diff --git a/content/docs/es/resources/guides/index.mdx b/content/docs/es/resources/guides/index.mdx index 400a9476d..3ac27a5e6 100644 --- a/content/docs/es/resources/guides/index.mdx +++ b/content/docs/es/resources/guides/index.mdx @@ -1,25 +1,23 @@ --- title: Guías -sidebarTitle: Visión general +sidebarTitle: Descripción general description: Guías para construir en Stacks. llm: false --- -## Visión general - -Para explorar Guías con IA, copie y pegue [llms.txt](/resources/guides/llms.txt) en tu LLM de preferencia. +## Resumen ## Construyendo Proyectos -* [Construye un Mercado de NFT](/resources/guides/build-an-nft-marketplace) - Guía para construir un mercado de NFT descentralizado en Stacks. -* [Construye un Kickstarter Descentralizado](/resources/guides/build-a-decentralized-kickstarter) - Guía para crear una plataforma de financiación colectiva descentralizada. +* [Construir un mercado de NFT](/resources/guides/build-an-nft-marketplace) - Guía para construir un mercado NFT descentralizado en Stacks. +* [Construir un Kickstarter Descentralizado](/resources/guides/build-a-decentralized-kickstarter) - Guía para crear una plataforma de financiación colectiva descentralizada. ## Stacks.js -* [Uso de los valores de Clarity](/resources/guides/using-clarity-values) - Aprende cómo usar los valores de Clarity en tus aplicaciones. +* [Usando Valores de Clarity](/resources/guides/using-clarity-values) - Aprende cómo usar valores de Clarity en tus aplicaciones. ## Aplicaciones -* [Lotería sin Pérdidas](/resources/guides/no-loss-lottery) - Guía para construir una aplicación de lotería sin pérdidas. +* [Lotería Sin Pérdidas](/resources/guides/no-loss-lottery) - Guía para construir una aplicación de lotería sin pérdidas. ## Infraestructura diff --git a/content/docs/es/resources/guides/using-pyth-price-feeds.mdx b/content/docs/es/resources/guides/using-pyth-price-feeds.mdx deleted file mode 100644 index 5fbb2635d..000000000 --- a/content/docs/es/resources/guides/using-pyth-price-feeds.mdx +++ /dev/null @@ -1,426 +0,0 @@ ---- -title: Uso de fuentes de precios de Pyth -description: Guía completa para integrar datos de precios de mercado en tiempo real de Pyth Network en sus aplicaciones de Stacks. ---- -import { File, Folder, Files } from 'fumadocs-ui/components/files'; -import { Steps, Step } from '@/components/steps'; -import { ArrowRight, Check } from 'lucide-react'; - -## Visión general - -Esta guía completa le guía a través de la integración [Red Pyth](https://pyth.network)'s oráculo descentralizado para datos de precios en tiempo real en tus aplicaciones de Stacks. Construiremos un ejemplo completo: un NFT que solo se puede acuñar pagando exactamente $100 en valor de sBTC. - -:::callout -The Pyth protocol integration is available as a Beta on both testnet and mainnet networks. It's maintained by Trust Machines and provides access to real-time price feeds for BTC, STX, ETH, and USDC. -::: - -## Descripción general de la arquitectura - -Pyth Network utiliza una única **basado en pull** diseño de oráculo: - - - -A diferencia de los oráculos basados en push que actualizan continuamente los precios en la cadena, Pyth permite a los usuarios obtener y enviar actualizaciones de precios solo cuando es necesario, lo que lo hace más eficiente en términos de gas. - -## Lo que estamos construyendo - -Crearemos un "Benjamin Club" - un NFT exclusivo que cuesta exactamente $100 en valor de sBTC para acuñar. Esto demuestra: - -* Lectura de precios BTC/USD en tiempo real desde Pyth -* Conversión entre cantidades de USD y criptomonedas -* Manejo de aritmética de punto fijo -* Construyendo una integración frontend completa -* Probando contratos dependientes de oráculo - - - - - - - - - - - - - - - - - - - - - - - - - -## Pasos de implementación - - - - ### Escribir el contrato inteligente - - Primero, implemente el contrato de Clarity que lee los datos de precios de Pyth: - - ```clarity contracts/benjamin-club.clar - ;; Benjamin Club - $100 NFT minting contract - (define-constant CONTRACT-OWNER tx-sender) - (define-constant BENJAMIN-COST u100) ;; $100 USD - (define-constant ERR-INSUFFICIENT-FUNDS (err u100)) - (define-constant ERR-PRICE-UPDATE-FAILED (err u101)) - (define-constant ERR-STALE-PRICE (err u102)) - - ;; Pyth oracle contracts - (define-constant PYTH-ORACLE 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3) - (define-constant PYTH-STORAGE 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3) - (define-constant PYTH-DECODER 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-pnau-decoder-v2) - (define-constant WORMHOLE-CORE 'SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.wormhole-core-v3) - - ;; BTC price feed ID - (define-constant BTC-USD-FEED-ID 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43) - - ;; NFT definition - (define-non-fungible-token benjamin-nft uint) - (define-data-var last-token-id uint u0) - - (define-public (mint-for-hundred-dollars (price-feed-bytes (buff 8192))) - (let ( - ;; Update price feed with fresh VAA data - (update-result (try! (contract-call? PYTH-ORACLE - verify-and-update-price-feeds price-feed-bytes { - pyth-storage-contract: PYTH-STORAGE, - pyth-decoder-contract: PYTH-DECODER, - wormhole-core-contract: WORMHOLE-CORE - }))) - - ;; Get the updated BTC price - (price-data (try! (contract-call? PYTH-ORACLE - get-price BTC-USD-FEED-ID PYTH-STORAGE))) - - ;; Process the price data - (btc-price (process-price-data price-data)) - - ;; Calculate required sBTC amount for $100 - (required-sbtc (calculate-sbtc-amount btc-price)) - - ;; Get user's sBTC balance - (user-balance (unwrap! - (contract-call? .sbtc-token get-balance tx-sender) - ERR-INSUFFICIENT-FUNDS)) - ) - ;; Verify price is fresh (less than 5 minutes old) - (try! (verify-price-freshness price-data)) - - ;; Verify user has enough sBTC - (asserts! (>= user-balance required-sbtc) ERR-INSUFFICIENT-FUNDS) - - ;; Transfer sBTC from user - (try! (contract-call? .sbtc-token transfer - required-sbtc tx-sender (as-contract tx-sender) none)) - - ;; Mint the NFT - (let ((token-id (+ (var-get last-token-id) u1))) - (try! (nft-mint? benjamin-nft token-id tx-sender)) - (var-set last-token-id token-id) - (ok { token-id: token-id, price-paid: required-sbtc })) - ) - ) - - (define-private (process-price-data (price-data { - price-identifier: (buff 32), - price: int, - conf: uint, - expo: int, - ema-price: int, - ema-conf: uint, - publish-time: uint, - prev-publish-time: uint - })) - (let ( - ;; Convert fixed-point to regular number - ;; For expo = -8, divide by 10^8 - (denominator (pow u10 (to-uint (* (get expo price-data) -1)))) - (price-uint (to-uint (get price price-data))) - ) - (/ price-uint denominator) - ) - ) - - (define-private (calculate-sbtc-amount (btc-price-usd uint)) - ;; $100 in sats = (100 * 10^8) / btc-price-usd - (/ (* BENJAMIN-COST u100000000) btc-price-usd) - ) - - (define-private (verify-price-freshness (price-data (tuple))) - (let ( - (current-time (unwrap-panic (get-block-info? time block-height))) - (publish-time (get publish-time price-data)) - (max-age u300) ;; 5 minutes - ) - (if (<= (- current-time publish-time) max-age) - (ok true) - ERR-STALE-PRICE) - ) - ) - ``` - - Para una explicación detallada de los componentes del contrato, consulte la [referencia de contratos inteligentes de Stacks](https://docs.stacks.co/reference). - - - - ### Construir la integración del frontend - - Crear un servicio para obtener datos de precios de Pyth: - - ```typescript frontend/pyth-service.ts - import { PriceServiceConnection } from '@pythnetwork/price-service-client'; - import { Buffer } from 'buffer'; - - const PRICE_FEEDS = { - BTC_USD: '0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43' - }; - - export async function fetchBTCPriceVAA(): Promise { - const pythClient = new PriceServiceConnection( - 'https://hermes.pyth.network', - { priceFeedRequestConfig: { binary: true } } - ); - - const vaas = await pythClient.getLatestVaas([PRICE_FEEDS.BTC_USD]); - const messageBuffer = Buffer.from(vaas[0], 'base64'); - - return `0x${messageBuffer.toString('hex')}`; - } - ``` - - Luego cree un componente de React para acuñar: - - ```typescript frontend/MintButton.tsx - import { request } from '@stacks/connect'; - import { Cl, Pc } from '@stacks/transactions'; - import { fetchBTCPriceVAA } from './pyth-service'; - import { useState } from 'react'; - - export function MintBenjaminNFT() { - const [loading, setLoading] = useState(false); - - const handleMint = async () => { - setLoading(true); - try { - // Fetch fresh price data - const priceVAA = await fetchBTCPriceVAA(); - - // Create post-conditions for safety - const postConditions = [ - // Oracle fee (1 uSTX max) - Pc.principal('SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R').willSendLte(1).ustx() - ]; - - // Call contract using request - const response = await request('stx_callContract', { - contract: 'SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R.benjamin-club', - functionName: 'mint-for-hundred-dollars', - functionArgs: [Cl.bufferFromHex(priceVAA.slice(2))], - postConditions, - postConditionMode: 'deny', - network: 'mainnet' - }); - - alert(`NFT minted! Transaction ID: ${response.txid}`); - } catch (error) { - console.error('Minting failed:', error); - alert('Failed to mint NFT'); - } finally { - setLoading(false); - } - }; - - return ( - - ); - } - ``` - - Para obtener detalles completos sobre la integración del frontend, consulte la [referencia para desarrolladores de Stacks](https://docs.stacks.co/reference). - - - - ### Prueba tu implementación - - Escriba pruebas exhaustivas utilizando Clarinet: - - ```typescript tests/benjamin-club.test.ts - import { describe, expect, it } from "vitest"; - import { Cl } from '@stacks/transactions'; - - describe("Benjamin Club Tests", () => { - it("should calculate correct sBTC amount", () => { - // Set mock BTC price to $100,000 - simnet.callPublicFn( - "mock-pyth-oracle", - "set-mock-price", - [ - Cl.bufferFromHex(BTC_FEED_ID), - Cl.int(10000000000000), // $100,000 with 8 decimals - Cl.int(-8) - ], - deployer - ); - - // Test price calculation - const response = simnet.callReadOnlyFn( - "benjamin-club", - "get-required-sbtc-amount", - [], - wallet1 - ); - - // $100 at $100k/BTC = 0.001 BTC = 100,000 sats - expect(response.result).toBeOk(Cl.uint(100000)); - }); - }); - ``` - - Para estrategias avanzadas de pruebas, incluyendo simulación de mainnet, consulte la [referencia oficial de Stacks](https://docs.stacks.co/reference). - - - -## Mejores prácticas - -### Frescura del precio - -Siempre verifique que los datos de precios sean lo suficientemente recientes para su caso de uso: - -```clarity -(define-constant MAX-PRICE-AGE u300) ;; 5 minutes - -(define-private (verify-price-freshness (price-data (tuple))) - (let ((age (- block-height (get publish-time price-data)))) - (asserts! (<= age MAX-PRICE-AGE) ERR-STALE-PRICE) - (ok true))) -``` - -### Manejo de errores - -Implementar un manejo integral de errores para fallos del oráculo: - -```typescript -try { - const vaa = await fetchBTCPriceVAA(); - // Process VAA... -} catch (error) { - if (error.message.includes('Network')) { - // Retry with exponential backoff - await retryWithBackoff(() => fetchBTCPriceVAA()); - } else { - // Handle other errors - throw error; - } -} -``` - -### Optimización de gas - -Agrupe múltiples actualizaciones de precios cuando sea posible: - -```clarity -(define-public (update-multiple-prices - (btc-vaa (buff 8192)) - (eth-vaa (buff 8192)) - (stx-vaa (buff 8192))) - (let ((all-vaas (concat btc-vaa (concat eth-vaa stx-vaa)))) - (contract-call? PYTH-ORACLE verify-and-update-price-feeds all-vaas params))) -``` - -## Solución de problemas - -### Problemas comunes - - - - La verificación de VAA falla - - - Asegúrate de obtener datos VAA con `binary: true` opción y convirtiendo de base64 a hexadecimal correctamente. El VAA debe ser reciente (típicamente dentro de 5 minutos). - - - - - Los cálculos de precios son incorrectos - - - Verifica que estés manejando el exponente correctamente. Pyth utiliza una representación de punto fijo donde el precio real = precio\_bruto \* 10^exponente. Para exponentes negativos, divide por 10^(-exponente). - - - - - Se agota el gas de la transacción - - - Las actualizaciones del oráculo pueden consumir mucho gas. Asegúrese de que sus límites de gas tengan en cuenta tanto la actualización del oráculo como la lógica de su contrato. Considere almacenar en caché los precios cuando múltiples operaciones necesiten el mismo precio. - - - - -## Consideraciones de seguridad - -1. **Manipulación de precios**: Utiliza siempre intervalos de confianza e implementa comprobaciones de cordura -2. **Adelantamiento**: Considere utilizar esquemas de compromiso-revelación para operaciones sensibles al precio -3. **Tarifas del oráculo**: Establecer condiciones posteriores apropiadas para limitar la exposición a tarifas -4. **Obsolescencia**: Rechazar precios más antiguos que tu umbral de seguridad - -## Referencia rápida - -### Direcciones de contratos - -| Red | Contrato | Dirección | -|---------|----------|---------| -| Mainnet | pyth-oracle-v3 | `SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3` | -| Mainnet | pyth-storage-v3 | `SP3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-storage-v3` | -| Red de pruebas | pyth-oracle-v3 | `ST3R4F6C1J3JQWWCVZ3S7FRRYPMYG6ZW6RZK31FXY.pyth-oracle-v3` | - -### IDs de fuentes de precios - -| Activo | ID del Feed | -|-------|---------| -| BTC/USD | `0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43` | -| STX/USD | `0xec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c17` | -| ETH/USD | `0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace` | -| USDC/USD | `0xeaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a` | - -## Recursos adicionales - -* [Documentación de Pyth Network](https://docs.pyth.network) -* [Integración de Pyth en Trust Machines](https://github.com/Trust-Machines/stacks-pyth-bridge) -* [Especificación VAA de Wormhole](https://wormhole.com/docs/protocol/infrastructure/vaas/) - -## Próximos pasos - -:::next-steps -* [Inmersión profunda en Clarity](https://docs.stacks.co/reference): Patrones avanzados de oráculos y optimizaciones -* [Inmersión profunda en el frontend](https://docs.stacks.co/reference): Construyendo interfaces de usuario de oráculos listas para producción -::: diff --git a/content/docs/es/resources/snippets/build-a-stx-pc.mdx b/content/docs/es/resources/snippets/build-a-stx-pc.mdx deleted file mode 100644 index 37a0ab4a8..000000000 --- a/content/docs/es/resources/snippets/build-a-stx-pc.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Construir una post-condición STX -description: Una función auxiliar que crea una post-condición para transferencias de tokens STX utilizando la clase constructora Pc, asegurando que se transfieran las cantidades exactas según lo esperado. -full: true ---- -```typescript -import { - AnchorMode, - broadcastTransaction, - makeSTXTokenTransfer, - Pc, - PostConditionMode, -} from "@stacks/transactions"; - -// Create a post-condition that ensures exactly 10 STX is sent -const pc = Pc.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM") - .willSendEq(10000000) // 10 STX in micro-STX - .ustx(); - -const txOptions = { - recipient: "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5", - amount: 10000000, - senderKey: "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601", - network: "testnet", - postConditions: [pc], - postConditionMode: PostConditionMode.Deny, - anchorMode: AnchorMode.Any, -}; - -const transaction = await makeSTXTokenTransfer(txOptions); -const broadcastResponse = await broadcastTransaction(transaction); -console.log("Transaction ID:", broadcastResponse.txid); -``` - -## Casos de uso - -* Asegurando las transferencias de tokens STX con validación del monto de transferencia -* Protegiendo a los usuarios de transferencias de tokens inesperadas -* Asegurando que las interacciones del contrato se comporten como se espera - -## Conceptos clave - -El `Pc` builder proporciona una interfaz fluida para crear post-condiciones: - -* `Pc.principal()` - Especificar el principal que enviará los tokens -* `.willSendEq()` - Asegúrate de que se envíe exactamente esta cantidad (también admite `willSendGte`, `willSendLte`, `willSendGt`, `willSendLt`) -* `.ustx()` - Especificar el tipo de token (micro-STX) diff --git a/content/docs/es/resources/snippets/build-an-ft-pc.mdx b/content/docs/es/resources/snippets/build-an-ft-pc.mdx deleted file mode 100644 index e2ed8a2ec..000000000 --- a/content/docs/es/resources/snippets/build-an-ft-pc.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Construir una post-condición FT -description: Crear post-condiciones para transferencias de tokens fungibles para garantizar que se transfieran las cantidades exactas según lo esperado ---- -```typescript -import { Pc } from '@stacks/transactions'; - -// Create a post-condition for fungible token transfers -const postCondition = Pc.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM") - .willSendGte(500) // Amount in token's smallest unit - .ft("ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE.token-ft", "my-token"); - -// Use in transaction options -const txOptions = { - // ... other transaction options - postConditions: [postCondition], - postConditionMode: PostConditionMode.Deny, -}; -``` - -## Casos de uso - -* Asegurando transferencias de tokens fungibles con validación de cantidad -* Protegiendo a los usuarios de transferencias inesperadas de tokens en protocolos DeFi -* Asegurando que los intercambios de tokens ocurran con las cantidades esperadas - -## Conceptos clave - -El `Pc` constructor para tokens fungibles acepta: - -* `.ft()` - Toma dos parámetros: - * Dirección del contrato con el nombre del activo (p. ej., `"contract.asset-name"`) - * Nombre del token según se define en el contrato diff --git a/content/docs/es/resources/snippets/build-an-nft-pc.mdx b/content/docs/es/resources/snippets/build-an-nft-pc.mdx deleted file mode 100644 index 9c85422a2..000000000 --- a/content/docs/es/resources/snippets/build-an-nft-pc.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: Construir una post-condición NFT -description: Crear condiciones posteriores para transferencias de NFT para garantizar que tokens específicos sean o no sean transferidos ---- -```typescript -import { Pc, Cl } from '@stacks/transactions'; - -// Ensure a specific NFT will be sent -const sendPC = Pc.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM") - .willSendAsset() - .nft('ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE.cool-nfts::nft-token', Cl.uint(42)); - -// Ensure a specific NFT will NOT be sent -const keepPC = Pc.principal("ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM") - .willNotSendAsset() - .nft('ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE.cool-nfts::nft-token', Cl.uint(1)); -``` - -## Casos de uso - -* Protegiendo NFTs valiosos de transferencias accidentales -* Asegurando que NFTs específicos sean transferidos en transacciones de mercado -* Protegiendo colecciones NFT durante interacciones con contratos - -## Conceptos clave - -Las post-condiciones de NFT utilizan el `.nft()` método que requiere: - -* **Identificador de activo**: Dirección del contrato + nombre del activo con `::` separator -* **ID del token**: El ID específico del NFT como un valor de Clarity (usando `Cl.uint()`) diff --git a/content/docs/es/resources/snippets/build-an-unsigned-tx.mdx b/content/docs/es/resources/snippets/build-an-unsigned-tx.mdx deleted file mode 100644 index a9f1ea8ab..000000000 --- a/content/docs/es/resources/snippets/build-an-unsigned-tx.mdx +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: Construir una transacción sin firmar -description: Crear transacciones sin firmar para billeteras de hardware o escenarios de firma múltiple ---- -```typescript -import { getPublicKeyFromPrivate } from "@stacks/encryption"; -import { - makeUnsignedSTXTokenTransfer, - makeUnsignedContractCall, - Cl, - AnchorMode, - PostConditionMode -} from "@stacks/transactions"; -import { STACKS_TESTNET } from "@stacks/network"; - -// Get public key from private key -const privateKey = "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601"; -const publicKey = getPublicKeyFromPrivate(privateKey); - -// Build unsigned STX transfer -const unsignedTx = await makeUnsignedSTXTokenTransfer({ - recipient: "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5", - amount: 1000000n, // 1 STX in micro-STX - fee: 200n, - nonce: 0n, - network: STACKS_TESTNET, - memo: "Test transfer", - publicKey, - anchorMode: AnchorMode.Any, - postConditionMode: PostConditionMode.Deny, -}); - -// Transaction is ready for external signing -console.log("Unsigned transaction created:", unsignedTx.txid()); -``` - -## Casos de uso - -* Integración de billetera hardware (Ledger, Trezor) -* Transacciones de billetera multifirma -* Firma de transacciones sin conexión -* Sistemas de gestión de claves seguras - -## Conceptos clave - -Las transacciones sin firmar separan la creación de la transacción de la firma: - -* **Clave pública solamente**: No se necesita clave privada para la creación -* **Firma externa**: Firmar con cartera de hardware o enclave seguro -* **Serialización**: Se puede transportar y firmar en otro lugar diff --git a/content/docs/es/resources/snippets/check-for-duplicates.mdx b/content/docs/es/resources/snippets/check-for-duplicates.mdx deleted file mode 100644 index 8d60cc73d..000000000 --- a/content/docs/es/resources/snippets/check-for-duplicates.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: Comprobar duplicados -description: Detectar caracteres duplicados en cadenas y elementos duplicados en listas usando Clarity ---- -```clarity -;; Check for duplicate characters in a string -(define-read-only (has-duplicate-chars? (input (string-ascii 200))) - (is-none (fold dup input (slice? (concat input "|END") u1 (+ (len input) u4)))) -) - -(define-private (dup (ch (string-ascii 1)) (out (optional (string-ascii 204)))) - (match out out_some - (match (index-of? (unwrap-panic (slice? out_some u0 (- (len out_some) u4))) ch) - found none - (slice? out_some u1 (len out_some)) - ) - out - ) -) - -;; Example usage -(has-duplicate-chars? "hello") ;; Returns true (duplicate 'l') -(has-duplicate-chars? "world") ;; Returns false (no duplicates) -``` - -## Casos de uso - -* Validando nombres de usuario para la unicidad de caracteres -* Verificando la singularidad de los rasgos de NFT en colecciones -* Previniendo entradas duplicadas en sistemas de votación -* Asegurando identificadores únicos en listas - -## Conceptos clave - -La detección de duplicados utiliza diferentes estrategias: - -* **Cadenas**: Usos `fold` con `index-of?` para encontrar caracteres repetidos -* **Listas**: Comprueba si los elementos aparecen de nuevo en el resto de la lista -* **Optimización**: Salida temprana en el primer duplicado encontrado diff --git a/content/docs/es/resources/snippets/convert-btc-to-stx-address.mdx b/content/docs/es/resources/snippets/convert-btc-to-stx-address.mdx deleted file mode 100644 index 6f335488e..000000000 --- a/content/docs/es/resources/snippets/convert-btc-to-stx-address.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Convertir dirección BTC a STX -description: Convierte direcciones de Bitcoin a sus correspondientes direcciones de Stacks utilizando decodificación base58 en Clarity ---- -```clarity -(define-read-only (btc-to-stx (input (string-ascii 60))) - (let ( - ;; Decode base58 string to numbers - (b58-numbers (map unwrap-uint (filter is-some-uint (map b58-to-uint input)))) - ;; Validate all characters are valid base58 - (t1 (asserts! (>= (len b58-numbers) (len input)) ERR_INVALID_CHAR)) - ;; Count leading '1's (zeros in base58) - (leading-ones-count (default-to (len input) (index-of? (map is-zero b58-numbers) false))) - ;; Convert to bytes - (decoded (concat (fold decode-outer to-decode LST) leading-zeros)) - (decoded-hex (fold to-hex-rev decoded 0x)) - ;; Verify checksum - (actual-checksum (unwrap-panic (slice? (sha256 (sha256 (unwrap-panic (slice? decoded-hex u0 (- decoded-hex-len u4))))) u0 u4))) - (expected-checksum (unwrap-panic (slice? decoded-hex (- decoded-hex-len u4) decoded-hex-len))) - (t3 (asserts! (is-eq actual-checksum expected-checksum) ERR_BAD_CHECKSUM)) - ;; Extract version and construct principal - (version (unwrap-panic (element-at? STX_VER (unwrap! (index-of? BTC_VER (unwrap-panic (element-at? decoded-hex u0))) ERR_INVALID_VERSION)))) - ) - (principal-construct? version (unwrap-panic (as-max-len? (unwrap-panic (slice? decoded-hex u1 (- decoded-hex-len u4))) u20))) - ) -) - -;; Example usage -(btc-to-stx "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa") ;; Returns Stacks address -``` - -## Casos de uso - -* Mapeo de direcciones entre cadenas para puentes Bitcoin-Stacks -* Verificando la propiedad en ambas cadenas -* Convirtiendo direcciones de Bitcoin heredadas al formato de Stacks -* Construyendo sistemas de autenticación entre cadenas - -## Conceptos clave - -El proceso de conversión implica: - -1. **Decodificación Base58**: Las direcciones de Bitcoin utilizan codificación base58 -2. **Verificación de suma de comprobación**: Los últimos 4 bytes son un checksum de doble SHA-256 -3. **Mapeo de versiones**: Los bytes de versión de Bitcoin se asignan a los bytes de versión de Stacks -4. **Construcción principal**: Construir Stacks principal a partir de datos decodificados diff --git a/content/docs/es/resources/snippets/convert-string-to-principal.mdx b/content/docs/es/resources/snippets/convert-string-to-principal.mdx deleted file mode 100644 index c75e122cc..000000000 --- a/content/docs/es/resources/snippets/convert-string-to-principal.mdx +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: Convertir cadena a principal -description: Analizar direcciones de cadena en tipos principales utilizando decodificación c32 en Clarity ---- -```clarity -(define-read-only (string-to-principal? (input (string-ascii 82))) - (let ( - ;; Find the dot separator for contract addresses - (dot (default-to (len input) (index-of? input "."))) - ;; Extract address part (skip first char which is version) - (addr (unwrap! (slice? input u1 dot) ERR_INVALID_LENGTH)) - ;; Decode c32 characters to numbers - (addressc32 (map unwrap-panic-uint (filter is-some-uint (map c32-index addr)))) - ;; Validate all characters are valid c32 - (isValidChars (asserts! (is-eq (len addr) (len addressc32)) ERR_INVALID_CHAR)) - ;; Extract version and decode address data - (version (unwrap-panic (element-at? addressc32 u0))) - (decoded (decode-address addressc32)) - ;; Verify checksum - (checksum (verify-checksum decoded version)) - ) - ;; Construct principal with or without contract name - (match (slice? input (+ u1 dot) (len input)) contract - (principal-construct? (to-byte version) (get-address-bytes decoded) contract) - (principal-construct? (to-byte version) (get-address-bytes decoded)) - ) - ) -) - -;; Example usage -(string-to-principal? "SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7") -;; Returns (some SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7) - -(string-to-principal? "SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7.my-contract") -;; Returns (some SP2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKNRV9EJ7.my-contract) -``` - -## Casos de uso - -* Análisis de direcciones de entrada del usuario en contratos -* Convirtiendo direcciones de cadena almacenadas a principales -* Validando formatos de direcciones antes de su uso -* Construcción de llamadas de contrato dinámicas con entradas de cadena - -## Conceptos clave - -Las direcciones de Stacks utilizan codificación c32: - -* **alfabeto c32**: `0123456789ABCDEFGHJKMNPQRSTVWXYZ` (sin I, L, O, U) -* **Suma de verificación**: Los últimos 4 bytes verifican la integridad de la dirección -* **Byte de versión**: El primer carácter indica el tipo de dirección -* **Direcciones de contrato**: Incluir `.contract-name` sufijo diff --git a/content/docs/es/resources/snippets/create-a-random-burn-address.mdx b/content/docs/es/resources/snippets/create-a-random-burn-address.mdx deleted file mode 100644 index c0f0218d2..000000000 --- a/content/docs/es/resources/snippets/create-a-random-burn-address.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: Crear una dirección de quemado aleatoria -description: Generar direcciones de quemado para eliminar permanentemente tokens de la circulación ---- -```clarity -(define-read-only (generate-burn-address (entropy (string-ascii 40))) - (let ( - ;; Hash the entropy to create address bytes - (hash-bytes (hash160 (unwrap-panic (to-consensus-buff? entropy)))) - ;; Use version byte for current network - (version-byte (if is-in-mainnet 0x16 0x1a)) - ) - ;; Construct a valid principal that no one controls - (principal-construct? version-byte hash-bytes) - ) -) - -;; Example: Generate unique burn address -(generate-burn-address "BURN-2024-01-15-PROJECT-XYZ") -;; Returns: (ok SP1FJPSG7V4QMA7D4XVPZ3B2HQ8GY3EK8GC0NGNT3) -``` - -## Casos de uso - -* Mecanismos de quema de tokens -* Implementaciones de prueba de quemado -* Creación de direcciones no gastables para tarifas de protocolo -* Economía de tokens deflacionaria - -## Conceptos clave - -Las direcciones de quemado son principales válidos que: - -* **Sin clave privada**: Generado a partir de datos arbitrarios, no un par de claves -* **Verificable**: Cualquiera puede verificar los tokens enviados a estas direcciones -* **Único**: La entropía diferente crea direcciones diferentes -* **Permanente**: Los fondos enviados se pierden irremediablemente diff --git a/content/docs/es/resources/snippets/create-a-sponsored-tx.mdx b/content/docs/es/resources/snippets/create-a-sponsored-tx.mdx deleted file mode 100644 index b7efa5859..000000000 --- a/content/docs/es/resources/snippets/create-a-sponsored-tx.mdx +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Crear una transacción patrocinada -description: Construye transacciones donde un patrocinador paga las tarifas en nombre de los usuarios ---- -```typescript -import { STACKS_TESTNET } from "@stacks/network"; -import { bytesToHex } from "@stacks/common"; -import { - broadcastTransaction, - deserializeTransaction, - makeContractCall, - sponsorTransaction, - BufferReader, - AnchorMode, - Cl, -} from "@stacks/transactions"; - -// Step 1: User creates the transaction with sponsored flag -const userTxOptions = { - contractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM", - contractName: "my-contract", - functionName: "my-function", - functionArgs: [Cl.uint(123)], - fee: 0, // User doesn't pay fees - senderKey: "b244296d5907de9864c0b0d51f98a13c52890be0404e83f273144cd5b9960eed01", - network: STACKS_TESTNET, - sponsored: true, // Mark as sponsored - anchorMode: AnchorMode.Any, -}; - -const transaction = await makeContractCall(userTxOptions); -const serializedTx = bytesToHex(transaction.serialize()); - -// Step 2: Send serialized transaction to sponsor -// (In practice, this would be sent to a sponsorship service) - -// Step 3: Sponsor signs and pays fees -const sponsorKey = "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601"; -const deserializedTx = deserializeTransaction(serializedTx); -const sponsoredTx = await sponsorTransaction({ - transaction: deserializedTx, - sponsorPrivateKey: sponsorKey, - fee: 1000, // Sponsor pays the fee - sponsorNonce: 0, -}); - -// Step 4: Broadcast the sponsored transaction -const broadcastResponse = await broadcastTransaction({ - transaction: sponsoredTx, - network: STACKS_TESTNET, -}); - -console.log("Sponsored transaction ID:", broadcastResponse.txid); -``` - -## Casos de uso - -* Incorporación de nuevos usuarios sin STX para tarifas -* Subsidiando los costos de transacción para usuarios de dApps -* Aplicaciones empresariales que pagan por transacciones de usuarios -* Aplicaciones de juegos con una experiencia de usuario fluida - -## Conceptos clave - -Las transacciones patrocinadas tienen dos partes: - -* **Usuario**: Crea y firma la transacción con `sponsored: true` -* **Patrocinador**: Paga las tarifas y transmite la transacción diff --git a/content/docs/es/resources/snippets/create-sha256-hash-clarity.mdx b/content/docs/es/resources/snippets/create-sha256-hash-clarity.mdx deleted file mode 100644 index 79a33844e..000000000 --- a/content/docs/es/resources/snippets/create-sha256-hash-clarity.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Crear hash SHA256 en Clarity -description: Generar hashes SHA-256 a partir de datos de buffer en contratos inteligentes de Clarity ---- -```clarity -(define-read-only (create-sha256-hash (data (buff 4096))) - (sha256 (unwrap-panic (to-consensus-buff? data))) -) - -;; Example usage -(define-read-only (hash-message (message (string-utf8 200))) - (create-sha256-hash (unwrap-panic (to-consensus-buff? message))) -) - -;; Hash a simple string -(print (hash-message u"Hello World")) -``` - -## Casos de uso - -* Creando identificadores únicos a partir de datos -* Verificando la integridad de datos en contratos -* Implementación de esquemas de compromiso-revelación -* Construyendo árboles de Merkle para pruebas - -## Conceptos clave - -La implementación de SHA-256 en Clarity: - -* Toma un búfer como entrada (máximo 1MB) -* Devuelve un hash de búfer de 32 bytes -* Usos `to-consensus-buff?` para garantizar una codificación coherente -* Produce el mismo hash que las implementaciones fuera de la cadena diff --git a/content/docs/es/resources/snippets/create-sha256-hash-stacks-js.mdx b/content/docs/es/resources/snippets/create-sha256-hash-stacks-js.mdx deleted file mode 100644 index 686fba542..000000000 --- a/content/docs/es/resources/snippets/create-sha256-hash-stacks-js.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Crear hash SHA256 en Stacks.js -description: Generar hashes SHA-256 que coincidan con la salida de hash de Clarity ---- -```typescript -import { sha256 } from "@noble/hashes/sha256"; -import { bytesToHex, hexToBytes, utf8ToBytes } from "@stacks/common"; -import { bufferCV, stringUtf8CV, serializeCV } from "@stacks/transactions"; - -// Hash a string (matching Clarity's sha256 output) -function hashString(text: string) { - const clarityValue = stringUtf8CV(text); - const serialized = serializeCV(clarityValue); - return bytesToHex(sha256(serialized)); -} - -// Hash hex data (matching Clarity's sha256 output) -function hashHexData(hexData: string) { - const clarityValue = bufferCV(hexToBytes(hexData)); - const serialized = serializeCV(clarityValue); - return bytesToHex(sha256(serialized)); -} - -// Example usage -const hash1 = hashString("Hello World"); -console.log("String hash:", hash1); - -const hash2 = hashHexData("0x1234567890abcdef"); -console.log("Hex hash:", hash2); -``` - -## Casos de uso - -* Creando identificadores deterministas -* Verificando la integridad de los datos entre on-chain y off-chain -* Implementación de esquemas de compromiso-revelación fuera de la cadena -* Construyendo árboles de Merkle compatibles con Clarity - -## Conceptos clave - -Para que coincida con la salida SHA-256 de Clarity: - -1. **Convertir a valor de Clarity**: Utilice el tipo de CV apropiado (`stringUtf8CV`, `bufferCV`, etc.) -2. **Serializar**: Uso `serializeCV` para coincidir con la codificación de Clarity -3. **Hash**: Aplicar SHA-256 a los bytes serializados diff --git a/content/docs/es/resources/snippets/deploy-a-contract.mdx b/content/docs/es/resources/snippets/deploy-a-contract.mdx deleted file mode 100644 index fe78ce09c..000000000 --- a/content/docs/es/resources/snippets/deploy-a-contract.mdx +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Desplegar un contrato -description: Desplegar un contrato inteligente de Clarity en la cadena de bloques de Stacks utilizando Stacks.js ---- -```typescript -import { STACKS_TESTNET } from "@stacks/network"; -import { - makeContractDeploy, - broadcastTransaction, - AnchorMode, - PostConditionMode -} from "@stacks/transactions"; - -const contractCode = ` -(define-data-var counter uint u0) - -(define-public (increment) - (ok (var-set counter (+ (var-get counter) u1)))) - -(define-read-only (get-counter) - (ok (var-get counter))) -`; - -const txOptions = { - contractName: "my-counter", - codeBody: contractCode, - senderKey: "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601", - network: STACKS_TESTNET, - anchorMode: AnchorMode.Any, - postConditionMode: PostConditionMode.Allow, - fee: 100000n, // Set an appropriate fee -}; - -const transaction = await makeContractDeploy(txOptions); -const broadcastResponse = await broadcastTransaction({ transaction }); -console.log("Contract deployed!"); -console.log("Transaction ID:", broadcastResponse.txid); -``` - -## Casos de uso - -* Desplegando nuevos contratos inteligentes en la red principal o en la red de pruebas -* Automatizando despliegues de contratos en pipelines de CI/CD -* Despliegue programático de contratos para la inicialización de dApps -* Implementando actualizaciones o nuevas versiones de contratos - -## Conceptos clave - -El despliegue del contrato requiere: - -* **Nombre del contrato**: Identificador único para su contrato (letras, números, guiones) -* **Cuerpo del código**: El código del contrato Clarity como una cadena -* **Clave del remitente**: Clave privada de la cuenta que despliega el contrato -* **Red**: Red objetivo (mainnet, testnet o devnet) diff --git a/content/docs/es/resources/snippets/derive-principal-addresses-between-networks.mdx b/content/docs/es/resources/snippets/derive-principal-addresses-between-networks.mdx deleted file mode 100644 index df6e5346a..000000000 --- a/content/docs/es/resources/snippets/derive-principal-addresses-between-networks.mdx +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Derivar direcciones principales entre redes -description: Convierte direcciones entre mainnet y testnet extrayendo y reconstruyendo con diferentes bytes de versión ---- -```clarity -;; Extract hash bytes from an address -(define-read-only (get-address-hash-bytes (address principal)) - (get hash-bytes (unwrap-panic (principal-destruct? address))) -) - -;; Convert testnet address to mainnet -(define-read-only (testnet-to-mainnet (testnet-address principal)) - (let ( - ;; Extract the hash bytes from testnet address - (hash-bytes (get-address-hash-bytes testnet-address)) - ;; Mainnet version byte - (mainnet-version 0x16) - ) - ;; Reconstruct with mainnet version - (principal-construct? mainnet-version hash-bytes) - ) -) - -;; Convert mainnet address to testnet -(define-read-only (mainnet-to-testnet (mainnet-address principal)) - (let ( - ;; Extract the hash bytes from mainnet address - (hash-bytes (get-address-hash-bytes mainnet-address)) - ;; Testnet version byte - (testnet-version 0x1a) - ) - ;; Reconstruct with testnet version - (principal-construct? testnet-version hash-bytes) - ) -) - -;; Example usage -(testnet-to-mainnet 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM) -;; Returns: (ok SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRCBGD7R) -``` - -## Casos de uso - -* Verificación de direcciones entre redes -* Construyendo dApps multi-red -* Herramientas de validación de direcciones -* Utilidades de migración de red - -## Conceptos clave - -Las direcciones de Stacks consisten en: - -* **Byte de versión**: Indica el tipo de red y dirección -* **Bytes de hash**: hash de 20 bytes de la clave pública -* **Suma de verificación**: Incorporado en la codificación c32 - -### Referencia de bytes de versión - -| Versión | Red | Tipo | Prefijo | -|---------|------|------|--------| -| 0x16 | Red principal | Estándar | SP | -| 0x17 | Red principal | Contrato | SM | -| 0x1a | Red de pruebas | Estándar | ST | -| 0x1b | Red de pruebas | Contrato | SN | diff --git a/content/docs/es/resources/snippets/derive-stacks-address-from-keys.mdx b/content/docs/es/resources/snippets/derive-stacks-address-from-keys.mdx deleted file mode 100644 index 9e1a816ac..000000000 --- a/content/docs/es/resources/snippets/derive-stacks-address-from-keys.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: Obtener dirección de Stacks a partir de claves -description: Generar direcciones de Stacks a partir de claves privadas o públicas utilizando múltiples métodos ---- -```typescript -import { getPublicKeyFromPrivate } from "@stacks/encryption"; -import { - getAddressFromPrivateKey, - getAddressFromPublicKey -} from "@stacks/transactions"; - -// Derive address from private key -const privateKey = process.env.PRIVATE_KEY; // Keep this secret! -const addressFromPrivate = getAddressFromPrivateKey(privateKey, "testnet"); - -// Derive public key and address -const publicKey = getPublicKeyFromPrivate(privateKey); -const addressFromPublic = getAddressFromPublicKey(publicKey, "testnet"); - -console.log("Address:", addressFromPrivate); -console.log("Public key:", publicKey); -console.log("Same address:", addressFromPrivate === addressFromPublic); // true -``` - -## Casos de uso - -* Generación de dirección de billetera -* Validación de par de claves -* Recuperación de dirección a partir de claves de respaldo -* Configuración de billetera multifirma - -## Conceptos clave - -Las direcciones de Stacks se derivan a través de: - -* **Clave privada**: número aleatorio de 32 bytes (¡mantenlo en secreto!) -* **Clave pública**: Derivado de la clave privada utilizando ECDSA -* **Dirección**: Hash de clave pública codificado en Base58check -* **Red**: Diferentes prefijos para mainnet (SP/SM) frente a testnet (ST/SN) diff --git a/content/docs/es/resources/snippets/fetch-testnet-bitcoin-on-regtest.mdx b/content/docs/es/resources/snippets/fetch-testnet-bitcoin-on-regtest.mdx deleted file mode 100644 index 1069135fd..000000000 --- a/content/docs/es/resources/snippets/fetch-testnet-bitcoin-on-regtest.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Obtener Bitcoin de testnet en regtest -description: Solicita BTC de testnet desde el faucet de Hiro para desarrollo y pruebas locales ---- -```typescript -const TESTNET_ADDRESS = 'bcrt1q728h29ejjttmkupwdkyu2x4zcmkuc3q29gvwaa'; - -try { - const response = await fetch( - `https://api.testnet.hiro.so/extended/v1/faucets/btc?address=${TESTNET_ADDRESS}`, - { - method: 'POST', - headers: { - "Content-Type": "application/json", - }, - } - ); - - const result = await response.json(); - console.log("Faucet response:", result); - - if (result.success) { - console.log(`Transaction ID: ${result.txid}`); - console.log(`Amount sent: ${result.amount} sats`); - } -} catch (error) { - console.error("Faucet request failed:", error); -} -``` - -## Casos de uso - -* Desarrollo local con transacciones de Bitcoin -* Probando operaciones de sBTC -* Pruebas de integración para aplicaciones de cadenas cruzadas -* Desarrollando contratos inteligentes conscientes de Bitcoin - -## Conceptos clave - -El faucet de la testnet de Hiro: - -* **Limitado por tasa**: Una solicitud por dirección por hora -* **Cantidad**: Envía 0.5 BTC de testnet por solicitud -* **Red**: Funciona con direcciones de testnet/regtest de Bitcoin -* **Formato**: Admite direcciones legacy, segwit y taproot diff --git a/content/docs/es/resources/snippets/filter-items-from-a-list.mdx b/content/docs/es/resources/snippets/filter-items-from-a-list.mdx deleted file mode 100644 index 9c391185b..000000000 --- a/content/docs/es/resources/snippets/filter-items-from-a-list.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: Filtrar elementos de una lista -description: Eliminar elementos específicos de listas usando fold en Clarity ---- -```clarity -(define-read-only (filter-item (l (list 100 uint)) (remove uint)) - (get newList (fold remove-value l { compareTo: remove, newList: (list) })) -) - -(define-private (remove-value (listValue uint) (trackerTuple { compareTo: uint, newList: (list 100 uint) })) - (merge trackerTuple {newList: - (if (is-eq listValue (get compareTo trackerTuple)) - (get newList trackerTuple) - (unwrap-panic (as-max-len? (append (get newList trackerTuple) listValue) u100)) - ) - }) -) - -;; Example usage -(filter-item (list u1 u2 u3 u2 u4) u2) ;; Returns (u1 u3 u4) -``` - -## Casos de uso - -* Eliminando direcciones en la lista negra de las listas de acceso -* Filtrando tareas completadas de las listas de tareas pendientes -* Excluyendo tokens específicos de los portafolios -* Limpieza de datos en contratos inteligentes - -## Conceptos clave - -Este patrón utiliza `fold` para iterar a través de una lista y construir una nueva lista: - -* **Acumulador**: Rastrea el valor a eliminar y construye la nueva lista -* **Anexión condicional**: Solo agrega elementos que no coinciden con el filtro -* **Seguridad de tipos**: Mantiene el tipo de lista y la longitud máxima diff --git a/content/docs/es/resources/snippets/generate-a-secret-key.mdx b/content/docs/es/resources/snippets/generate-a-secret-key.mdx deleted file mode 100644 index c82fdf0e3..000000000 --- a/content/docs/es/resources/snippets/generate-a-secret-key.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Generar una clave secreta -description: Crear frases semilla mnemotécnicas para la generación de billeteras ---- -```typescript -import { generateSecretKey } from '@stacks/wallet-sdk'; - -// Generate a 24-word mnemonic (256 bits of entropy) -const mnemonic24 = generateSecretKey(); -// Example: "aunt birth lounge misery utility blind holiday walnut fuel make gift parent gap picnic exact various express sphere family nerve oil drill engage youth" - -// Generate a 12-word mnemonic (128 bits of entropy) -const mnemonic12 = generateSecretKey(128); -// Example: "winter crash infant long upset beauty cram tank short remain obtain sauce" -``` - -## Casos de uso - -* Creando nuevas frases semilla de billetera -* Generando entropía segura para aplicaciones -* Creación de flujos de creación de billeteras -* Probando la funcionalidad de la billetera - -## Conceptos clave - -Las frases semilla mnemotécnicas siguen el estándar BIP39: - -* **Entropía**: Datos aleatorios utilizados para generar la frase -* **Recuento de palabras**: 12 palabras (128 bits) o 24 palabras (256 bits) -* **Lista de palabras**: Lista estandarizada de 2048 palabras -* **Suma de verificación**: Detección de errores incorporada diff --git a/content/docs/es/resources/snippets/generate-a-wallet.mdx b/content/docs/es/resources/snippets/generate-a-wallet.mdx deleted file mode 100644 index 29bbf91c7..000000000 --- a/content/docs/es/resources/snippets/generate-a-wallet.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Generar una cartera -description: Crea una nueva billetera con frase mnemotécnica o restaura desde una semilla existente ---- -```typescript -import { generateWallet, generateSecretKey } from '@stacks/wallet-sdk'; - -// Generate a new wallet with a new 24-word seed phrase -const secretKey = generateSecretKey(256); // 256 bits = 24 words -const wallet = await generateWallet({ - secretKey, - password: 'your-secure-password', -}); - -// Access the first account -const account = wallet.accounts[0]; -console.log('Address:', account.stxAddress); -console.log('Mnemonic:', secretKey); -``` - -## Casos de uso - -* Creando nuevas billeteras para usuarios -* Restauración de billeteras a partir de frases semilla -* Generando direcciones de billetera deterministas -* Creando aplicaciones de billetera - -## Conceptos clave - -El SDK de la billetera genera billeteras determinísticas jerárquicas (HD) siguiendo los estándares BIP32/BIP39: - -* **Clave secreta**: Puede ser una frase mnemotécnica o clave privada -* **Contraseña**: Cifra la cartera (diferente de la frase de contraseña mnemónica) -* **Cuentas**: Se pueden derivar múltiples cuentas de una sola semilla diff --git a/content/docs/es/resources/snippets/generate-random-number.mdx b/content/docs/es/resources/snippets/generate-random-number.mdx deleted file mode 100644 index b8b6cba8c..000000000 --- a/content/docs/es/resources/snippets/generate-random-number.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Generar número aleatorio -description: Crear números pseudo-aleatorios utilizando hashes de bloques para obtener aleatoriedad en contratos inteligentes ---- -```clarity -(define-read-only (generate-random (block-height uint)) - (let ( - ;; Get block header hash - (block-hash (unwrap! (get-stacks-block-info? id-header-hash block-height) (err u1001))) - ;; Take a slice of the hash for randomness - (hash-slice (unwrap-panic (slice? block-hash u16 u32))) - ;; Convert to uint - (random-value (buff-to-uint-be (unwrap-panic (as-max-len? hash-slice u16)))) - ) - (ok random-value) - ) -) - -;; Generate random number in range -(define-read-only (random-in-range (block-height uint) (min uint) (max uint)) - (let ( - (random (try! (generate-random block-height))) - (range (- max min)) - ) - (ok (+ min (mod random (+ u1 range)))) - ) -) - -;; Example: Random between 1-100 -(random-in-range block-height u1 u100) -``` - -## Casos de uso - -* Contratos de lotería y juegos de azar -* Generación aleatoria de rasgos de NFT -* Mecanismos de distribución justa -* Selección aleatoria de listas - -## Conceptos clave - -La aleatoriedad de la cadena de bloques es determinista pero impredecible: - -* **Hashes de bloque**: Usar datos históricos de bloques como fuente de entropía -* **Bloques futuros**: No se pueden predecir los futuros hashes de bloques -* **Esquemas de compromiso**: Combinar con commit-reveal para equidad diff --git a/content/docs/es/resources/snippets/get-account-details-from-wallet.mdx b/content/docs/es/resources/snippets/get-account-details-from-wallet.mdx deleted file mode 100644 index 641f1533b..000000000 --- a/content/docs/es/resources/snippets/get-account-details-from-wallet.mdx +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: Obtener detalles de la cuenta desde la billetera -description: Extraer direcciones, claves y otra información de cuenta de una instancia de billetera ---- -```typescript -import { STACKS_TESTNET, STACKS_MAINNET } from "@stacks/network"; -import { generateWallet, getStxAddress } from "@stacks/wallet-sdk"; - -// Generate or restore wallet -const wallet = await generateWallet({ - secretKey: "your twenty four word mnemonic phrase goes here...", - password: "wallet-password", -}); - -// Get first account -const account = wallet.accounts[0]; - -// Get addresses for different networks -const testnetAddress = getStxAddress({ - account, - transactionVersion: 0 // testnet -}); - -const mainnetAddress = getStxAddress({ - account, - transactionVersion: 1 // mainnet -}); - -// Get keys -const privateKey = account.stxPrivateKey; -const publicKey = account.stxPublicKey; - -console.log("Testnet address:", testnetAddress); -console.log("Mainnet address:", mainnetAddress); -console.log("Public key:", publicKey); -``` - -## Casos de uso - -* Mostrando direcciones de usuario en interfaces de billeteras -* Obtención de claves privadas para la firma de transacciones -* Derivando direcciones para diferentes redes -* Construyendo herramientas de gestión de billeteras - -## Conceptos clave - -Las cuentas de billetera contienen: - -* **Clave privada**: Se utiliza para firmar transacciones -* **Clave pública**: Derivado de la clave privada -* **Direcciones**: Específico de la red (red principal vs red de pruebas) -* **Ruta de derivación**: Ruta BIP44 utilizada para generar la cuenta diff --git a/content/docs/es/resources/snippets/helper-function-to-restrict-contract-calls.mdx b/content/docs/es/resources/snippets/helper-function-to-restrict-contract-calls.mdx deleted file mode 100644 index ccf3ed392..000000000 --- a/content/docs/es/resources/snippets/helper-function-to-restrict-contract-calls.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Función auxiliar para restringir llamadas a contratos -description: Implementar control de acceso para garantizar que las funciones solo puedan ser llamadas por usuarios, no por otros contratos ---- -```clarity -;; Check if caller is a standard principal (user wallet) -(define-private (is-standard-principal-call) - (is-none (get name (unwrap! (principal-destruct? contract-caller) false))) -) - -;; Public function restricted to direct user calls -(define-public (user-only-function (amount uint)) - (begin - (asserts! (is-standard-principal-call) (err u401)) - ;; Function logic here - (ok true) - ) -) -``` - -## Casos de uso - -* Prevención de ataques de reentrada de contrato a contrato -* Garantizando transacciones iniciadas por humanos para la gobernanza -* Restringir la acuñación de tokens a acciones directas del usuario -* Protegiendo las funciones de administrador de llamadas automatizadas - -## Conceptos clave - -Tipos principales en Clarity: - -* **Directores estándar**: Billeteras de usuario (direcciones SP/ST) -* **Principales del contrato**: Contratos desplegados (dirección.nombre-del-contrato) -* **contrato-llamador**: El llamante inmediato de la función actual -* **tx-remitente**: El iniciador original de la transacción diff --git a/content/docs/es/resources/snippets/index.mdx b/content/docs/es/resources/snippets/index.mdx deleted file mode 100644 index 565a0db2f..000000000 --- a/content/docs/es/resources/snippets/index.mdx +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: Fragmentos -sidebarTitle: Visión general -description: Ejemplos de código listos para usar para construir en Stacks y Bitcoin -llm: false ---- -## Visión general - -Para explorar Snippets con IA, copie y pegue [llms.txt](/resources/snippets/llms.txt) en tu LLM de preferencia. - -## Transacciones - -### Transferencias de Tokens - -* [Transferir STX](/resources/snippets/transfer-stx) - Enviar tokens STX entre direcciones -* [Transferir un token SIP-10](/resources/snippets/transfer-a-sip10-token) - Transferir tokens fungibles - -### Construcción de Transacciones - -* [Construir una transacción sin firmar](/resources/snippets/build-an-unsigned-tx) - Crear transacciones para billeteras de hardware -* [Crear una transacción patrocinada](/resources/snippets/create-a-sponsored-tx) - Pagar tarifas en nombre de los usuarios - -### Poscondiciones - -* [Construir una post-condición STX](/resources/snippets/build-a-stx-pc) - Transferencias seguras de STX -* [Construir una post-condición FT](/resources/snippets/build-an-ft-pc) - Transferencias seguras de tokens fungibles -* [Construir una post-condición NFT](/resources/snippets/build-an-nft-pc) - Transferencias seguras de NFT - -## Contratos Inteligentes - -### Despliegue de Contratos - -* [Desplegar un contrato](/resources/snippets/deploy-a-contract) - Desplegar contratos en la cadena de bloques - -### Funciones de Clarity - -* [Crear hash SHA256](/resources/snippets/create-sha256-hash-clarity) - Generar hashes en Clarity -* [Filtrar elementos de una lista](/resources/snippets/filter-items-from-a-list) - Manipulación de listas en Clarity -* [Generar número aleatorio](/resources/snippets/generate-random-number) - Números pseudo-aleatorios en contratos -* [Comprobar duplicados](/resources/snippets/check-for-duplicates) - Encontrar elementos duplicados en listas -* [Devolver una entrada de un mapa](/resources/snippets/return-an-entry-from-a-map) - Acceder a estructuras de datos de mapas -* [Función auxiliar para restringir llamadas a contratos](/resources/snippets/helper-function-to-restrict-contract-calls) - Patrones de control de acceso - -## Cuentas y Direcciones - -### Gestión de Billetera - -* [Generar una cartera](/resources/snippets/generate-a-wallet) - Crear nueva cartera con mnemónico -* [Generar una clave secreta](/resources/snippets/generate-a-secret-key) - Crear claves privadas -* [Obtener detalles de la cuenta desde la billetera](/resources/snippets/get-account-details-from-wallet) - Extraer información de la cuenta - -### Utilidades de Dirección - -* [Convertir dirección BTC a STX](/resources/snippets/convert-btc-to-stx-address) - Conversión de direcciones entre cadenas -* [Convertir cadena a principal](/resources/snippets/convert-string-to-principal) - Analizar direcciones principales -* [Obtener dirección de Stacks a partir de claves](/resources/snippets/derive-stacks-address-from-keys) - Generar direcciones a partir de pares de claves -* [Derivar direcciones principales entre redes](/resources/snippets/derive-principal-addresses-between-networks) - Mapeo de direcciones de red -* [Crear una dirección de quemado aleatoria](/resources/snippets/create-a-random-burn-address) - Generar direcciones de quemado - -## Criptografía y Seguridad - -### Hashing - -* [Crear hash SHA256 (Stacks.js)](/resources/snippets/create-sha256-hash-stacks-js) - Generar hashes en JavaScript -* [Crear hash SHA256 (Clarity)](/resources/snippets/create-sha256-hash-clarity) - Generar hashes en contratos inteligentes - -### Integración de API - -* [Integrar claves de API usando Stacks.js](/resources/snippets/integrate-api-keys-using-stacksjs) - Uso seguro de claves API - -## Herramientas de Desarrollo - -### Probando - -* [Obtener Bitcoin de testnet en regtest](/resources/snippets/fetch-testnet-bitcoin-on-regtest) - Obtener BTC de prueba para desarrollo diff --git a/content/docs/es/resources/snippets/integrate-api-keys-using-stacksjs.mdx b/content/docs/es/resources/snippets/integrate-api-keys-using-stacksjs.mdx deleted file mode 100644 index d273e0859..000000000 --- a/content/docs/es/resources/snippets/integrate-api-keys-using-stacksjs.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: Integrar claves de API usando Stacks.js -description: Configurar Stacks.js para usar claves API para límites de tasa mejorados y monitoreo ---- -```typescript -import { createApiKeyMiddleware, createFetchFn } from "@stacks/common"; -import { StacksMainnet, StacksTestnet } from "@stacks/network"; - -// Create middleware with your API key -const apiMiddleware = createApiKeyMiddleware({ - apiKey: process.env.HIRO_API_KEY -}); - -// Create custom fetch function -const customFetch = createFetchFn(apiMiddleware); - -// Configure network with API key -const network = new StacksMainnet({ - fetchFn: customFetch -}); -``` - -## Casos de uso - -* Aumento de los límites de tasa de API para aplicaciones en producción -* Monitoreo y análisis del uso de API -* Acceso prioritario durante períodos de alto tráfico -* Funciones de soporte empresarial personalizadas - -## Conceptos clave - -Beneficios de la clave API: - -* **Límites de tasa más altos**: 500 solicitudes/minuto frente a 50 para anónimos -* **Seguimiento de uso**: Monitorea tu consumo de API -* **Cola de prioridad**: Mejor rendimiento durante las horas pico -* **Soporte**: Acceso a canales de soporte dedicados diff --git a/content/docs/es/resources/snippets/meta.json b/content/docs/es/resources/snippets/meta.json deleted file mode 100644 index 6c1e693f1..000000000 --- a/content/docs/es/resources/snippets/meta.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "title": "Snippets", - "root": true, - "pages": [ - "---Snippets---", - "index", - "---Transactions---", - "transfer-stx", - "transfer-a-sip10-token", - "build-an-unsigned-tx", - "create-a-sponsored-tx", - "build-a-stx-pc", - "build-an-ft-pc", - "build-an-nft-pc", - "---Smart Contracts---", - "deploy-a-contract", - "create-sha256-hash-clarity", - "filter-items-from-a-list", - "generate-random-number", - "check-for-duplicates", - "return-an-entry-from-a-map", - "helper-function-to-restrict-contract-calls", - "---Accounts & Addresses---", - "generate-a-wallet", - "generate-a-secret-key", - "get-account-details-from-wallet", - "convert-btc-to-stx-address", - "convert-string-to-principal", - "derive-stacks-address-from-keys", - "derive-principal-addresses-between-networks", - "create-a-random-burn-address", - "---Cryptography & Security---", - "create-sha256-hash-stacks-js", - "integrate-api-keys-using-stacksjs", - "---Development Tools---", - "fetch-testnet-bitcoin-on-regtest" - ] -} diff --git a/content/docs/es/resources/snippets/return-an-entry-from-a-map.mdx b/content/docs/es/resources/snippets/return-an-entry-from-a-map.mdx deleted file mode 100644 index bdd5e16aa..000000000 --- a/content/docs/es/resources/snippets/return-an-entry-from-a-map.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Devolver una entrada de un mapa -description: Consultar datos del mapa de contratos utilizando el endpoint map_entry de la API de Stacks ---- -```typescript -import { Cl, cvToHex } from "@stacks/transactions"; - -// Query a map entry from a contract -const contractAddress = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM"; -const contractName = "my-contract"; -const mapName = "user-balances"; - -// Create the map key (e.g., a principal) -const mapKey = Cl.standardPrincipal("ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5"); - -const response = await fetch( - `https://api.hiro.so/v2/map_entry/${contractAddress}/${contractName}/${mapName}`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(cvToHex(mapKey)), - } -); - -const result = await response.json(); -const data = result.data ? Cl.deserialize(result.data) : null; - -console.log("Map value:", data); -``` - -## Casos de uso - -* Leyendo saldos de usuarios desde contratos de tokens -* Verificando registros de propiedad de NFT -* Recuperando valores de configuración de contratos -* Monitoreando el estado del contrato sin transacciones - -## Conceptos clave - -La API map\_entry: - -* **Solicitud POST**: Enviar la clave del mapa serializado -* **Codificación hexadecimal**: Las claves deben ser valores de Clarity codificados en hexadecimal -* **Formato de respuesta**: Devuelve el valor de Clarity codificado en hexadecimal o null diff --git a/content/docs/es/resources/snippets/transfer-a-sip10-token.mdx b/content/docs/es/resources/snippets/transfer-a-sip10-token.mdx deleted file mode 100644 index 444da7175..000000000 --- a/content/docs/es/resources/snippets/transfer-a-sip10-token.mdx +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Transferir un token SIP-10 -description: Transferir tokens fungibles utilizando el estándar SIP-10 con post-condiciones ---- -```typescript -import { STACKS_MAINNET } from "@stacks/network"; -import { - AnchorMode, - broadcastTransaction, - Cl, - makeContractCall, - Pc, - PostConditionMode, -} from "@stacks/transactions"; - -// Token contract details -const tokenAddress = "SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9"; -const tokenName = "wrapped-bitcoin"; -const contractIdentifier = `${tokenAddress}.${tokenName}`; - -// Create post-condition to ensure exact amount is transferred -const postConditions = [ - Pc.principal("SP2C20XGZBAYFZ1NYNHT1J6MGBGVX9X7X3P7LAX7K") - .willSendEq(100000000) // 1 wBTC (8 decimals) - .ft(contractIdentifier, tokenName) -]; - -const txOptions = { - contractAddress: tokenAddress, - contractName: tokenName, - functionName: "transfer", - functionArgs: [ - Cl.uint(100000000), // amount (with decimals) - Cl.principal("SP2C20XGZBAYFZ1NYNHT1J6MGBGVX9X7X3P7LAX7K"), // sender - Cl.principal("SP31DA84DWTF6510EW6DCTC3GB3XH1EEBGP7MYT2"), // recipient - Cl.none(), // optional memo - ], - senderKey: "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601", - validateWithAbi: true, - network: STACKS_MAINNET, - postConditions, - postConditionMode: PostConditionMode.Deny, - anchorMode: AnchorMode.Any, -}; - -const transaction = await makeContractCall(txOptions); -const broadcastResponse = await broadcastTransaction({ - transaction, - network: STACKS_MAINNET, -}); - -console.log("Transaction ID:", broadcastResponse.txid); -``` - -## Casos de uso - -* Transferir tokens fungibles entre billeteras -* Integrando transferencias de tokens en dApps -* Construyendo funcionalidad de DEX o intercambio -* Implementación de sistemas de pago con tokens personalizados - -## Conceptos clave - -SIP-10 es el estándar de token fungible en Stacks, similar a ERC-20: - -* **Interfaz estándar**: Todos los tokens SIP-10 implementan `transfer`, `get-balance`, etc. -* **Postcondiciones**: Proteger a los usuarios asegurando que se transfieran cantidades exactas -* **Memorandos**: Campo opcional para incluir notas de transferencia diff --git a/content/docs/es/resources/snippets/transfer-stx.mdx b/content/docs/es/resources/snippets/transfer-stx.mdx deleted file mode 100644 index 3e825e9ed..000000000 --- a/content/docs/es/resources/snippets/transfer-stx.mdx +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: Transferir STX -description: Envía tokens STX entre direcciones con post-condiciones para transferencias seguras ---- -```typescript -import { STACKS_TESTNET } from "@stacks/network"; -import { - AnchorMode, - broadcastTransaction, - makeSTXTokenTransfer, - Pc, - PostConditionMode, -} from "@stacks/transactions"; - -// Define sender and recipient -const senderAddress = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM"; -const recipientAddress = "ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5"; - -// Create post-condition to ensure exact amount is sent -const postConditions = Pc.principal(senderAddress) - .willSendEq(1000000) // 1 STX in micro-STX - .ustx(); - -// Configure transaction options -const txOptions = { - recipient: recipientAddress, - amount: 1000000, // 1 STX in micro-STX - senderKey: "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601", - network: STACKS_TESTNET, - memo: "Transfer memo", // Optional memo field - postConditions: [postConditions], - postConditionMode: PostConditionMode.Deny, - anchorMode: AnchorMode.Any, -}; - -// Create and broadcast the transaction -const transaction = await makeSTXTokenTransfer(txOptions); -const broadcastResponse = await broadcastTransaction({ transaction }); -console.log("Transaction ID:", broadcastResponse.txid); -``` - -## Casos de uso - -* Transferir tokens STX de forma segura entre direcciones -* Validando montos de transacciones para transferencias seguras - -## Conceptos clave - -* **Postcondiciones**: Asegúrate de que se transfiera la cantidad exacta de STX diff --git a/content/docs/es/resources/templates/index.mdx b/content/docs/es/resources/templates/index.mdx deleted file mode 100644 index 6c2b10c5c..000000000 --- a/content/docs/es/resources/templates/index.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Plantillas de proyectos -description: Plantillas de proyectos para construir en Stacks. -llm: false ---- -## Plantillas de proyectos diff --git a/content/docs/es/resources/templates/meta.json b/content/docs/es/resources/templates/meta.json deleted file mode 100644 index 1fe5c96f2..000000000 --- a/content/docs/es/resources/templates/meta.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title": "Project templates", - "root": true, - "pages": ["index", "..."] -} diff --git a/content/docs/es/tools/bitcoin-indexer/(indexer)/full-sync.mdx b/content/docs/es/tools/bitcoin-indexer/(indexer)/full-sync.mdx index d3e98d810..d53be62a5 100644 --- a/content/docs/es/tools/bitcoin-indexer/(indexer)/full-sync.mdx +++ b/content/docs/es/tools/bitcoin-indexer/(indexer)/full-sync.mdx @@ -1,7 +1,7 @@ --- title: Sincronización completa sidebarTitle: Sincronización completa -description: Ejecutar el Indexador de Bitcoin desde cero. +description: Ejecute el Indexador de Bitcoin desde cero. isNew: true --- import { ArrowRight, Check } from 'lucide-react'; @@ -9,21 +9,23 @@ import { ArrowRight, Check } from 'lucide-react'; ## Lo que aprenderás :::objectives -* Configurar e inicializar el Indexador de Bitcoin +* Configurar y configurar el Indexador de Bitcoin * Ejecutar el indexador desde cero * Monitorear el progreso de indexación de manera efectiva ::: +## Requisitos previos + :::prerequisites -* Bitcoin Core nodo ejecutándose con `txindex=1` +* nodo de Bitcoin Core ejecutándose con `txindex=1` * PostgreSQL ejecutándose con bases de datos creadas * Espacio de almacenamiento suficiente ::: :::callout -### Archivo bootstrap recomendado +### Arranque de archivo recomendado -Si aún no lo has hecho, considera [arranque inicial desde archivos](/tools/bitcoin-indexer/archive-bootstrap) para ahorrar días de tiempo de indexación. +Si aún no lo has hecho, considera [inicializando desde archivos](/tools/bitcoin-indexer/archive-bootstrap) para ahorrar días de tiempo de indexación. ::: ## Iniciar servicios de indexación @@ -34,13 +36,13 @@ El Indexador de Bitcoin ejecuta servicios separados para cada metaprotocolo. Nav $ cd ~/bitcoin-indexer/bitcoin-indexer ``` -### Ordinales de Índice (incluye BRC-20) +### Índice de Ordinales (incluye BRC-20) ```terminal $ ./target/release/bitcoin-indexer ordinals service start --config-path=bitcoin-indexer-config.toml ``` -### Runas de Índice +### Runas Índice En una terminal separada: @@ -51,22 +53,22 @@ $ ./target/release/bitcoin-indexer runes service start --config-path=bitcoin-ind :::callout type: info -### Compilando desde el código fuente +### Construyendo desde el código fuente -Si está descargando una versión de nuestro extremo, ejecute `cargo build --release` después de navegar a la carpeta para compilar el proyecto. Este paso es necesario para usar `/target/release/bitcoin-indexer` para iniciar servicios. +Si estás descargando una versión desde nuestro extremo, ejecuta `cargo build --release` después de navegar a la carpeta para compilar el proyecto. Este paso es necesario para usar `/target/release/bitcoin-indexer` para iniciar servicios. ::: ## Monitorear el progreso de indexación -### Revisar los registros del servicio +### Verificar registros del servicio -El indexador genera información detallada sobre el progreso: +El indexador produce información detallada del progreso: ```terminal $ tail -f ~/bitcoin-indexer/bitcoin-indexer/ordinals.log ``` -### Consultar el estado del indexador a través de la API +### Consultar estado del indexador mediante API Una vez que el servidor API se inicia (generalmente después de unos pocos bloques): @@ -83,25 +85,25 @@ $ curl http://localhost:3000/ordinals/v1/status ## Gestión de servicios -### Detener servicios de manera controlada +### Detener servicios de manera elegante ```terminal $ ps aux | grep bitcoin-indexer ``` -### Reiniciar después de la interrupción +### Reiniciar después de interrupción -El indexador se reanuda automáticamente desde el último bloque procesado: +El indexador reanuda automáticamente desde el último bloque procesado: ```terminal $ ./target/release/bitcoin-indexer ordinals service start --config-path=bitcoin-indexer-config.toml ``` -## Configuración del servicio systemd +## Configuración del servicio Systemd -Para implementaciones de producción, utilice systemd para gestionar servicios: +Para despliegues en producción, usa systemd para gestionar servicios: -### Crear servicio de Ordinales +### Crear servicio de Ordinals ```bash title="/etc/systemd/system/bitcoin-indexer-ordinals.service" [Unit] @@ -120,7 +122,7 @@ RestartSec=30 WantedBy=multi-user.target ``` -### Crear servicio de Runas +### Crear servicio de Runes ```bash title="/etc/systemd/system/bitcoin-indexer-runes.service" [Unit] @@ -152,7 +154,7 @@ $ sudo systemctl status bitcoin-indexer-ordinals ### Durante la sincronización inicial -Optimizar el rendimiento durante la puesta al día: +Optimizar para el rendimiento durante la actualización: ```toml title="bitcoin-indexer-config.toml" [resources] @@ -163,7 +165,7 @@ bitcoind_rpc_threads = 10 ### Después de alcanzar la punta de la cadena -Reducir el uso de recursos para la operación en estado estable: +Reduce el uso de recursos para la operación en estado estable: ```toml [resources] @@ -172,9 +174,9 @@ cpu_core_available = 4 bitcoind_rpc_threads = 4 ``` -## Verificar la indexación exitosa +## Verificar indexación exitosa -### Verificar puntos finales de API +### Verificar endpoints de API Prueba que las APIs estén devolviendo datos: @@ -182,9 +184,7 @@ Prueba que las APIs estén devolviendo datos: $ curl http://localhost:3000/ordinals/v1/inscriptions/0 ``` -## Próximos pasos - :::next-steps -* [Archivos de Hiro](/tools/bitcoin-indexer/archive-bootstrap): Salta semanas de indexación al iniciar desde los archivos preindexados de Hiro. -* [Configuración avanzada](/tools/bitcoin-indexer/configuration): Configura el Indexador de Bitcoin para un rendimiento óptimo y personaliza la configuración del metaprotocolo. +* [Archivos Hiro](/tools/bitcoin-indexer/archive-bootstrap): Omite semanas de indexación iniciando desde los archivos pre-indexados de Hiro. +* [Configuración avanzada](/tools/bitcoin-indexer/configuration): Configure el indexador de Bitcoin para un rendimiento óptimo y personaliza la configuración de metaprotocolo. ::: diff --git a/content/docs/es/tools/chainhook/(chainhook-cli)/cli-reference.mdx b/content/docs/es/tools/chainhook/(chainhook-cli)/cli-reference.mdx deleted file mode 100644 index ba69a50c9..000000000 --- a/content/docs/es/tools/chainhook/(chainhook-cli)/cli-reference.mdx +++ /dev/null @@ -1,208 +0,0 @@ ---- -title: Referencia de CLI -sidebarTitle: Referencia de CLI -description: Referencia completa de todos los comandos y opciones de Chainhook CLI. ---- -El CLI de Chainhook proporciona herramientas para crear, probar e implementar observadores de eventos de blockchain. Desde la creación de predicados hasta la gestión de servicios, Chainhook simplifica su flujo de trabajo de monitoreo de blockchain. - -* Generar configuración: [`chainhook config new`](#chainhook-config) -* Crear predicados: [`chainhook predicates new`](#chainhook-predicates) -* Probar predicados: [`chainhook predicates scan`](#chainhook-predicates) -* Ejecutar como un servicio: [`chainhook service start`](#chainhook-service) -* Administrar la base de datos de Stacks: [`chainhook stacks db`](#chainhook-stacks) - -## Gestión de configuración \[#configuration-management] - -### configuración de chainhook - -`chainhook config` genera archivos de configuración. - -| Comando | Descripción | -|---------|-------------| -| `new` | Generar nueva configuración | - -**Uso con `new`** - -```console -chainhook config new [OPTIONS] -``` - -```terminal -$ chainhook config new --mainnet -``` - -| Opción | Descripción | -|--------|-------------| -| `--mainnet` | Red Mainnet objetivo | -| `--testnet` | Red de prueba objetivo | -| `--devnet` | Red Devnet objetivo | - -## Gestión de predicados \[#predicate-management] - -### predicados de chainhook - -`chainhook predicates` genera y prueba predicados. - -| Comando | Descripción | -|---------|-------------| -| `new` | Generar nuevo predicado | -| `check` | Comprobar predicado dado | -| `scan` | Escanear bloques (una sola vez) desde la red especificada y aplicar el predicado proporcionado | - -**Uso con `new`** - -```console -chainhook predicates new [OPTIONS] -``` - -```terminal -$ chainhook predicates new my-predicate --stacks -$ chainhook predicates new bitcoin-transfers --bitcoin -``` - -| Opción | Descripción | -|--------|-------------| -| `--stacks` | Generar un predicado de Stacks | -| `--bitcoin` | Generar un predicado de Bitcoin | - -**Uso con `scan`** - -```console -chainhook predicates scan [OPTIONS] -``` - -```terminal -$ chainhook predicates scan my-predicate.json --mainnet -$ chainhook predicates scan transfers.json --testnet --config-path ./Chainhook.toml -``` - -| Opción | Descripción | -|--------|-------------| -| `--mainnet` | Red Mainnet objetivo | -| `--testnet` | Red de prueba objetivo | -| `--config-path ` | Ruta del archivo de configuración de carga | - -**Uso con `check`** - -```console -chainhook predicates check [OPTIONS] -``` - -```terminal -$ chainhook predicates check my-predicate.json --mainnet -``` - -| Opción | Descripción | -|--------|-------------| -| `--config-path ` | Ruta del archivo de configuración de carga | -| `--mainnet` | Red Mainnet objetivo | -| `--testnet` | Red de prueba objetivo | - -## Gestión de servicios \[#service-management] - -### servicio chainhook - -`chainhook service` ejecuta un servicio que transmite bloques y evalúa predicados registrados. - -| Comando | Descripción | -|---------|-------------| -| `start` | Iniciar chainhook-cli | - -**Uso con `start`** - -```console -chainhook service start [OPTIONS] -``` - -```terminal -$ chainhook service start --config-path=./Chainhook.toml -$ chainhook service start --predicate-path=./my-predicate.json --mainnet -``` - -| Opción | Descripción | -|--------|-------------| -| `--config-path ` | Ruta del archivo de configuración de carga | -| `--predicate-path ` | Especificar la ruta relativa de los chainhooks (formato yaml) a evaluar | -| `--start-http-api` | Iniciar API REST para gestionar predicados | -| `--prometheus-port ` | Si se proporciona, sirve métricas de Prometheus en `localhost:{port}/metrics` | -| `--mainnet` | Red Mainnet objetivo | -| `--testnet` | Red de prueba objetivo | -| `--devnet` | Red Devnet objetivo | - -## Integración de Stacks \[#stacks-integration] - -### chainhook stacks - -`chainhook stacks` proporciona comandos específicos de Stacks. - -| Comando | Descripción | -|---------|-------------| -| `db` | Comandos relacionados con el mantenimiento de la base de datos | - -**Uso con `db`** - -```console -chainhook stacks db -``` - -```terminal -$ chainhook stacks db check -$ chainhook stacks db update --config-path ./Chainhook.toml -``` - -| Subcomando | Descripción | -|------------|-------------| -| `check` | Comprobar integridad | -| `drop` | Actualizar bloques desde la base de datos | -| `get` | Recuperar un bloque de la base de datos de Stacks | -| `get-latest` | Obtener los bloques más recientes de la base de datos de bloques no confirmados y confirmados | -| `unconfirm` | Elimina un bloque de la base de datos de bloques confirmados y lo mueve a la base de datos de bloques no confirmados | -| `update` | Actualizar la base de datos utilizando el archivo de archivo Stacks más reciente | - -| Opción | Descripción | -|--------|-------------| -| `--config-path ` | Ruta del archivo de configuración de carga | - -## Utilidades \[#utilities] - -### documentación de chainhook - -`chainhook docs` genera documentación. - -| Comando | Descripción | -|---------|-------------| -| `api` | Generar nueva documentación para la API de registro de predicados | - -**Uso con `api`** - -```console -chainhook docs api -``` - -```terminal -$ chainhook docs api -``` - -### chainhook ayuda - -`chainhook help` imprime información de ayuda. - -**Uso** - -```console -chainhook help [SUBCOMMAND] -``` - -```terminal -$ chainhook help -$ chainhook help predicates -``` - -## Opciones globales \[#global-options] - -Estas opciones se pueden utilizar con el comando principal: - -| Opción | Corto | Descripción | -|--------|-------|-------------| -| `--help` | `-h` | Imprimir información de ayuda | -| `--version` | `-V` | Imprimir información de versión | diff --git a/content/docs/es/tools/chainhook/(chainhook-cli)/event-scanning.mdx b/content/docs/es/tools/chainhook/(chainhook-cli)/event-scanning.mdx deleted file mode 100644 index 240cc3259..000000000 --- a/content/docs/es/tools/chainhook/(chainhook-cli)/event-scanning.mdx +++ /dev/null @@ -1,201 +0,0 @@ ---- -title: Escaneando eventos de blockchain -sidebarTitle: Escanear eventos -description: Prueba los predicados escaneando datos históricos de la cadena de bloques antes de implementarlos en producción. ---- -Esta guía te muestra cómo usar el modo de escaneo de Chainhook para probar predicados contra datos históricos de blockchain. El escaneo ayuda a validar la lógica de tu predicado y entender qué eventos serán capturados antes de entrar en funcionamiento. - -## Escaneo básico - -Prueba tu predicado contra datos históricos escaneando la cadena de bloques: - -```terminal -$ chainhook predicates scan my-predicate.json --mainnet -``` - -El comando scan descarga datos de la cadena de bloques desde Hiro Archive (almacenados en caché después de la primera ejecución) y evalúa tu predicado contra cada bloque en el rango especificado. - -### Escanear con redes específicas - -```terminal -$ chainhook predicates scan predicate.json --mainnet -$ chainhook predicates scan predicate.json --testnet -$ chainhook predicates scan predicate.json --devnet -``` - -## Escaneo de rango de bloques - -Limitar el escaneo a bloques específicos para pruebas más rápidas y análisis dirigido: - -```terminal -$ chainhook predicates scan my-predicate.json \ - --start-block 150000 \ - --end-block 150100 \ - --mainnet -``` - -### Configurar rangos en predicados - -```json block-range-predicate.json -{ - "chain": "stacks", - "networks": { - "mainnet": { - "start_block": 150000, - "end_block": 151000, - "if_this": { - "scope": "contract_call", - "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-dao" - } - } - } -} -``` - -Este predicado escanea solo los bloques 150.000 a 151.000, reduciendo significativamente el tiempo de escaneo. - -## Configuración de salida - -Controla dónde se escriben los resultados del escaneo usando el `then_that` sección. - -### Salida de archivo - -```json file-output-predicate.json -{ - "then_that": { - "file_append": { - "path": "./scan-results.json" - } - } -} -``` - -### Salida de la consola - -```json console-output-predicate.json -{ - "then_that": { - "file_append": { - "path": "-" // Writes to stdout - } - } -} -``` - -## Optimización del rendimiento - -Acelera tus escaneos siendo específico sobre lo que estás buscando. - -### Utilice ámbitos específicos - -```json optimized-scope.json -{ - "if_this": { - "scope": "contract_call", - "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-dao", - "method": "collateralize-and-mint" - } -} -``` - -Apuntar a contratos y métodos específicos escanea mucho más rápido que ámbitos amplios. - -### Limitar ocurrencias - -```json limited-occurrences.json -{ - "networks": { - "mainnet": { - "expire_after_occurrence": 100, - "if_this": { - "scope": "nft_event", - "actions": ["mint"] - } - } - } -} -``` - -Detener el escaneo después de encontrar 100 eventos coincidentes. - -## Patrones de escaneo comunes - -Aprende de estos ejemplos prácticos de escaneo de eventos específicos de blockchain. - -### Encuentra el primer despliegue del contrato - -```json find-deployment.json -{ - "chain": "stacks", - "networks": { - "mainnet": { - "expire_after_occurrence": 1, - "if_this": { - "scope": "contract_deployment", - "deployer": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR" - } - } - } -} -``` - -```terminal -$ chainhook predicates scan find-deployment.json --mainnet -``` - -### Recopilar actividad de NFT - -```json nft-activity.json -{ - "if_this": { - "scope": "nft_event", - "asset_identifier": "SP32AEEF6WW5Y0NMJ1S8SBSZDAY8R5J32NBZFPKKZ.free-punks-v0::free-punks", - "actions": ["mint", "transfer", "burn"] - }, - "then_that": { - "file_append": { - "path": "./nft-activity.json" - } - } -} -``` - -### Monitorear transacciones de direcciones - -```json address-monitor.json -{ - "if_this": { - "scope": "stx_event", - "actions": ["transfer"], - "predicate": { - "or": [ - { - "equals": { - "sender": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR" - } - }, - { - "equals": { - "recipient": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR" - } - } - ] - } - } -} -``` - -Rastrea todas las transferencias de STX que involucren una dirección específica como remitente o destinatario. - -## Escaneo de depuración - -Habilitar la salida detallada para entender por qué los eventos coinciden o no coinciden: - -```terminal -$ chainhook predicates scan my-predicate.json --mainnet --verbose -``` - -## Lecturas adicionales - -* [Despliegue en modo de servicio](/tools/chainhook/service-mode) -* [Ejemplos de uso](/tools/chainhook/usage) diff --git a/content/docs/es/tools/chainhook/(chainhook-cli)/service-mode.mdx b/content/docs/es/tools/chainhook/(chainhook-cli)/service-mode.mdx deleted file mode 100644 index 70da52569..000000000 --- a/content/docs/es/tools/chainhook/(chainhook-cli)/service-mode.mdx +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: Ejecutar Chainhook como un servicio -sidebarTitle: Modo de servicio -description: Implementa Chainhook como un servicio para la transmisión de eventos de blockchain en tiempo real con nodos de Bitcoin y Stacks. ---- -Esta guía te muestra cómo ejecutar Chainhook como un servicio para el monitoreo continuo de eventos de blockchain. Aprenderás cómo configurar Chainhook con nodos de Bitcoin y Stacks, gestionar predicados de forma dinámica y optimizar para implementaciones en producción. - -## Configuración básica del servicio - -Inicie Chainhook como un servicio utilizando un archivo de configuración: - -```terminal -$ chainhook service start --config-path=./Chainhook.toml -``` - -El servicio se conecta a tus nodos de blockchain y comienza a monitorear eventos que coincidan con tus predicados registrados. - -### Configuración mínima - -```toml Chainhook.toml -[storage] -working_dir = "/var/chainhook" - -[network] -mode = "mainnet" - -[limits] -max_number_of_bitcoin_predicates = 100 -max_number_of_stacks_predicates = 10 -``` - -## Configuración del nodo Bitcoin - -Configurar Chainhook para trabajar con un nodo de Bitcoin para monitorear eventos de Bitcoin: - -```toml Chainhook.toml -[network] -mode = "mainnet" -bitcoind_rpc_url = "http://localhost:8332" -bitcoind_rpc_username = "devnet" -bitcoind_rpc_password = "devnet" - -# Option 1: Receive events via ZeroMQ (recommended for Bitcoin-only) -bitcoind_zmq_url = "tcp://0.0.0.0:18543" - -# Option 2: Receive events via Stacks node (if running both chains) -# stacks_node_rpc_url = "http://localhost:20443" -``` - -### Configuración de mapeo de Bitcoin - -| bitcoin.conf | Chainhook.toml | -|--------------|----------------| -| rpcuser | bitcoind\_rpc\_username | -| rpcpassword | bitcoind\_rpc\_password | -| rpcport | bitcoind\_rpc\_url | -| zmqpubhashblock | bitcoind\_zmq\_url | - -## Configuración del nodo Stacks - -Para monitorear eventos de Stacks, configure tanto el nodo de Stacks como Chainhook: - -### Configuración del nodo Stacks - -```toml Stacks.toml -[node] -working_dir = "/stacks-blockchain" -rpc_bind = "0.0.0.0:20443" -p2p_bind = "0.0.0.0:20444" - -[burnchain] -chain = "bitcoin" -mode = "mainnet" -peer_host = "localhost" -username = "devnet" # Must match bitcoin.conf rpcuser -password = "devnet" # Must match bitcoin.conf rpcpassword -rpc_port = 8332 # Must match bitcoin.conf rpcport - -[[events_observer]] -endpoint = "localhost:20455" -retry_count = 255 -events_keys = ["*"] -``` - -### Configuración de Chainhook para Stacks - -```toml Chainhook.toml -[network] -mode = "mainnet" -bitcoind_rpc_url = "http://localhost:8332" -bitcoind_rpc_username = "devnet" -bitcoind_rpc_password = "devnet" -stacks_node_rpc_url = "http://localhost:20443" -stacks_events_ingestion_port = 20455 -``` - -## Gestión de predicados - -Registre predicados con el servicio para comenzar a monitorear eventos específicos. - -### Comienza con predicados - -```terminal -$ chainhook service start --predicate-path=my-predicate.json --config-path=Chainhook.toml -``` - -### Registro dinámico - -Habilite la API HTTP para registrar predicados mientras el servicio está en ejecución: - -```toml Chainhook.toml -[http_api] -http_port = 20456 -database_uri = "redis://localhost:6379/" -``` - -Registrar un nuevo predicado a través de la API: - -```terminal -$ curl -X POST http://localhost:20456/v1/chainhooks \ - -H "Content-Type: application/json" \ - -d @predicate.json - -{"result":"f8d43129-dba1-4a6c-b368-21426de0f3cd","status":200} -``` - -## Monitoreo de servicios - -Monitoree la salud y el estado de su servicio Chainhook. - -### Chequeo de salud - -```terminal -$ curl http://localhost:20456/health -``` - -```json -{ - "status": "healthy", - "stacks_node": "connected", - "bitcoin_node": "connected", - "database": "connected", - "predicates_active": 3 -} -``` - -## Lecturas adicionales - -* [Diseño de predicados](/tools/chainhook/usage) -* [Ámbitos de eventos de Bitcoin](/tools/chainhook/reference/bitcoin-scopes) -* [Ámbitos de eventos de Stacks](/tools/chainhook/reference/stacks-scopes) diff --git a/content/docs/es/tools/chainhook/(chainhook-sdk)/edit-update.mdx b/content/docs/es/tools/chainhook/(chainhook-sdk)/edit-update.mdx new file mode 100644 index 000000000..b0706108a --- /dev/null +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/edit-update.mdx @@ -0,0 +1,79 @@ +--- +title: Editar chainhooks +description: Modificar chainhooks existentes usando el SDK +--- +Modifica chainhooks existentes incluyendo filtros, acciones y opciones. La UI de la Plataforma no admite edición - usa el SDK en su lugar. + +:::callout +La interfaz de usuario de la plataforma actualmente no admite la edición de chainhooks. Debes usar el SDK o API para realizar actualizaciones. +::: + +## updateChainhook + +### Campos mutables vs inmutables + +| Mutable | Immutable | +|---------------------|---------------------------| +| `name` | `chain` | +| `filters` | `network` | +| `action` | | +| `options` | | + +### Ejemplo de Actualización Básica + +```typescript +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +await client.updateChainhook('chainhook-uuid', { + name: 'Updated chainhook name', + filters: { + events: [{ type: 'ft_transfer', asset_identifier: 'SP...ABC.token::usdc' }], + }, +}); +``` + +### Agregar filtro de eventos (conservando los eventos existentes) + +Para agregar un nuevo filtro de eventos a un chainhook existente, puedes obtener la definición actual, modificarla y luego actualizarla. + +```typescript +// ✅ Good: Fetch first +const current = await client.getChainhook(uuid); +await client.updateChainhook('chainhook-uuid', { + filters: { + events: [ + ...(current.definition.filters.events ?? []), + { type: 'contract_call', contract_identifier: 'SP...XYZ.counter' }, + ], + }, +}); + +// ❌ Bad: Will overwrite any existing events +await client.updateChainhook(uuid, { + filters: { events: { type: 'contract_call', contract_identifier: 'SP...XYZ.counter' } }, +}); +``` + +### Actualizar Múltiples Campos + +```typescript +await client.updateChainhook('chainhook-uuid', { + name: 'Updated name', + filters: { events: [{ type: 'stx_transfer', sender: 'SP...SENDER' }] }, + action: { type: 'http_post', url: 'https://new-url.com/webhooks' }, + options: { decode_clarity_values: true }, +}); + +const updated = await client.getChainhook('chainhook-uuid'); +console.log('Updated:', updated.definition.name); +``` + +:::next-steps +* [Lista y Obtener](/tools/chainhook/list-fetch): Recuperar información de chainhook antes de actualizar +* [Referencia de Filtros](/tools/chainhook/reference/filters): Explorar todas las opciones de filtro +::: diff --git a/content/docs/es/tools/chainhook/(chainhook-sdk)/evaluate.mdx b/content/docs/es/tools/chainhook/(chainhook-sdk)/evaluate.mdx new file mode 100644 index 000000000..fc4701b23 --- /dev/null +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/evaluate.mdx @@ -0,0 +1,65 @@ +--- +title: Evaluar un bloque específico +description: Ejecuta tus chainhooks contra bloques específicos para pruebas, depuración e indexación histórica. +--- +El endpoint evaluate reproduce un solo bloque contra uno de tus chainhooks registrados para que puedas validar filtros sin esperar tráfico en vivo. + +Úsalo para reproducir entregas perdidas, inspeccionar esquemas de carga útil después de cambios de filtro, o probar la infraestructura de webhooks con bloques conocidos antes de habilitar un hook en producción. + +## evaluateChainhook + +### Métodos de Evaluación + +| Método | Parámetro | Ejemplo | +|--------|-----------|---------| +| **Por altura** | `block_height` | `{ block_height: 100000 }` | +| **Por hash** I don't see any text to translate in your message. You've provided the rules and formatting instructions, but the actual text content appears to be missing after "Text to translate:" - there's only a "|" character. + +Could you please provide the text you'd like me to translate from English to Spanish? `index_block_hash` | `{ index_block_hash: '0xa204...' }` | + +### Ejemplo + + + ```ts !! with-block-height.ts + import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + + const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, + }); + + await client.evaluateChainhook('chainhook-uuid', { + // !mark + block_height: 100000, + }); + ``` + + ```ts !! with-block-hash.ts + import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + + const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, + }); + + await client.evaluateChainhook('chainhook-uuid', { + // !mark + index_block_hash: '0xa204...', + }); + ``` + + +Returns HTTP `204 No Content`. Si los filtros coinciden, el payload del webhook se envía a tu `action.url`. + +## Casos de Uso + +| Caso de Uso | Descripción | Ejemplo | +|----------|-------------|---------| +| **Debug** | Investigar eventos perdidos | Evaluar bloque específico que debería haberse activado | +| **Rellenar** | Indexar datos históricos | Procesar bloques pasados después de crear chainhook | +| **Reprocesar** | Solucionar problemas del manejador de webhook | Re-evaluar después de corregir errores | + +:::next-steps +* [Registrar & Habilitar](/tools/chainhook/register-enable): Crear chainhooks para evaluar +* [Referencia de Filtros](/tools/chainhook/reference/filters): Configurar qué eventos coinciden +::: diff --git a/content/docs/es/tools/chainhook/(chainhook-sdk)/introduction.mdx b/content/docs/es/tools/chainhook/(chainhook-sdk)/introduction.mdx new file mode 100644 index 000000000..c9b5d5552 --- /dev/null +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/introduction.mdx @@ -0,0 +1,166 @@ +--- +title: Introducción +description: SDK de TypeScript/JavaScript para gestionar chainhooks programáticamente +--- +## Descripción general + +El SDK de Chainhook (`@hirosystems/chainhooks-client`) proporciona un cliente TypeScript/JavaScript para gestionar chainhooks de forma programática. + +## Instalación + + + ```terminal !! npm + $ npm install @hirosystems/chainhooks-client + ``` + + ```terminal !! yarn + $ yarn add @hirosystems/chainhooks-client + ``` + + ```terminal !! pnpm + $ pnpm add @hirosystems/chainhooks-client + ``` + + ```terminal !! bun + $ bun add @hirosystems/chainhooks-client + ``` + + +## Ejemplo Rápido + +```typescript +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, // or CHAINHOOKS_BASE_URL.mainnet + apiKey: process.env.HIRO_API_KEY!, +}); + +// Register and enable a chainhook +const chainhook = await client.registerChainhook({ + version: '1', + name: 'my-first-chainhook', + chain: 'stacks', + network: 'testnet', + filters: { + events: [ + { + type: 'contract_call', + contract_identifier: 'SP...XYZ.counter', + function_name: 'increment', + }, + ], + }, + action: { + type: 'http_post', + url: 'https://example.com/webhooks', + }, + options: { + decode_clarity_values: true, + enable_on_registration: true, + }, +}); + +console.log('Chainhook created:', chainhook.uuid); +``` + +## URLs base + +El SDK proporciona constantes específicas de red: + +| Network | Constant | URL | +|---------|----------|-----| +| Red de prueba | `CHAINHOOKS_BASE_URL.testnet` | https://api.testnet.hiro.so | +| Red principal | `CHAINHOOKS_BASE_URL.mainnet` | https://api.mainnet.hiro.so | + +```typescript +import { CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +// Testnet (for development) +const testnetClient = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +// Mainnet (for production) +const mainnetClient = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, + apiKey: process.env.HIRO_API_KEY!, +}); +``` + +## Autenticación + +### Obtén tu Clave de API + +1. Visitar [platform.hiro.so](https://platform.hiro.so) +2. Inicia sesión o crea una cuenta +3. Navega a la sección de Claves API +4. Generar o copiar tu clave API + +### Configurar Cliente + +```typescript +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, // Store securely in environment variables +}); +``` + +:::callout +type: warn + +### Claves API + +Nunca confirmes claves de API al control de versiones. Siempre usa variables de entorno o gestión segura de secretos. +::: + +## Métodos del SDK + +El SDK proporciona los siguientes métodos: + +### Métodos Principales + +| Method | Description | +|--------|-------------| +| `registerChainhook()` | Crear un nuevo chainhook | +| `getChainhooks()` | Lista todos tus chainhooks (con paginación) | +| `getChainhook()` | Get a specific chainhook by UUID | +| `updateChainhook()` | Actualizar un chainhook existente | +| `deleteChainhook()` | Eliminar un chainhook | + +### Métodos de Activación + +| Method | Description | +|--------|-------------| +| `enableChainhook()` | Habilitar o deshabilitar un único chainhook | +| `bulkEnableChainhooks()` | Habilitar o deshabilitar múltiples chainhooks con filtros | + +### Métodos de Utilidad + +| Method | Description | +|--------|-------------| +| `evaluateChainhook()` | Evaluar un chainhook contra bloques pasados específicos | +| `rotateConsumerSecret()` | Rotar el secreto del webhook para verificación de carga útil | + +## Soporte para TypeScript + +### Tipos Disponibles + +El SDK proporciona definiciones completas de tipos TypeScript: + +```typescript +import type { + ChainhookDefinitionSchema, // Chainhook configuration + ChainhookStatusSchema, // Status and activity info + EvaluateChainhookRequest, // Evaluation parameters + BulkEnableChainhooksRequest, // Bulk operation filters +} from '@hirosystems/chainhooks-client'; +``` + +## Próximos Pasos + +:::next-steps +* [Inicio rápido](/tools/chainhook/quickstart): Comienza en 5 minutos +* [Registrar y Habilitar](/tools/chainhook/register-enable): Crear y activar chainhooks +::: diff --git a/content/docs/es/tools/chainhook/(chainhook-sdk)/list-fetch.mdx b/content/docs/es/tools/chainhook/(chainhook-sdk)/list-fetch.mdx new file mode 100644 index 000000000..c1f617b7b --- /dev/null +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/list-fetch.mdx @@ -0,0 +1,53 @@ +--- +title: Obtener chainhooks +description: Recuperar información de chainhook usando el SDK +--- +## getChainhooks + +Recupera una lista paginada de todos tus chainhooks. + +### Ejemplo + +```typescript +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +// Get first page (default: 20 results) +const chainhooks = await client.getChainhooks(); + +console.log('Total chainhooks:', chainhooks.total); +console.log('Results:', chainhooks.results.length); +console.log('Limit:', chainhooks.limit); +console.log('Offset:', chainhooks.offset); +``` + +### Con opciones + +```typescript +// Get specific page with 50 chainhooks starting from position 100 +const chainhooks = await client.getChainhooks({ + limit: 50, + offset: 100, +}); +``` + +*** + +## getChainhook + +Retrieve a specific chainhook by UUID. + +### Ejemplo + +```typescript +const chainhook = await client.getChainhook('be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185'); +``` + +:::next-steps +* [Editar y Actualizar](/tools/chainhook/edit-update): Modificar chainhooks existentes +* [Registrar y Habilitar](/tools/chainhook/register-enable): Crear nuevos chainhooks +::: diff --git a/content/docs/es/tools/chainhook/(chainhook-sdk)/manage-keys.mdx b/content/docs/es/tools/chainhook/(chainhook-sdk)/manage-keys.mdx new file mode 100644 index 000000000..29116d2ed --- /dev/null +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/manage-keys.mdx @@ -0,0 +1,65 @@ +--- +title: Administrar Claves +description: Rotar secretos del consumidor y validar cada entrega de Chainhook +--- +## Qué aprenderás + +:::objectives +* Crear/rotar un secreto de consumidor de Chainhook. +* Validar solicitudes de webhook verificando la `Authorization` encabezado. +::: + +## Prerrequisitos + +:::prerequisites +* Clave API de Hiro +* Node.js (el ejemplo del servidor usa Fastify). +::: + +## Validación de solicitudes de webhook con un secreto de consumidor + +Cuando creas un secreto, nuestro servicio Chainhook adjunta un `Authorization: Bearer ` encabezado a cada intento de webhook, dándote un simple apretón de manos de secreto compartido. Así es como empezar: + +1. Rota el secreto con `rotateConsumerSecret` (o el `/chainhooks/{uuid}/secret` API) cada vez que necesites inicializar o crear un nuevo token. +2. Rechazar entregas de webhook cuyas `Authorization` header no es igual a `Bearer `. + +### Crear/rotar secreto del consumidor + +```ts server.ts +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, // or .testnet / custom URL + apiKey: process.env.HIRO_API_KEY!, +}); + +// Store this value securely and use it to validate webhook requests +const secret = await client.rotateConsumerSecret(chainhookUuid).secret; +``` + +### Servidor Fastify de ejemplo + +```ts -n +import Fastify from 'fastify'; + +const server = Fastify(); + +server.post('/webhook', async (request, reply) => { + if (!secret) { + reply.code(503).send({ error: 'consumer secret unavailable' }); + return; + } + + const authHeader = request.headers.authorization; + if (authHeader !== `Bearer ${secret}`) { + reply.code(401).send({ error: 'invalid consumer secret' }); + return; + } + + const event = request.body; + console.log(`received chainhook ${event.chainhook.uuid}`); + reply.code(204).send(); +}); + +await server.listen({ port: Number(process.env.PORT) || 3000 }); +``` diff --git a/content/docs/es/tools/chainhook/(chainhook-sdk)/register-enable.mdx b/content/docs/es/tools/chainhook/(chainhook-sdk)/register-enable.mdx new file mode 100644 index 000000000..166f8aeb8 --- /dev/null +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/register-enable.mdx @@ -0,0 +1,144 @@ +--- +title: Crear chainhooks +description: Crear y activar chainhooks usando el SDK de Chainhook +--- +# Registrar y Habilitar + +Aprende cómo crear y activar chainhooks usando el SDK de Chainhook. + +## registerChainhook + +Crea una nueva configuración de chainhook. Por defecto, los nuevos chainhooks se crean en estado deshabilitado a menos que `enable_on_registration` está configurado en `true`. + +### Ejemplo + +```ts -nc +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +const chainhook = await client.registerChainhook({ + version: '1', + name: 'my-chainhook', + chain: 'stacks', + network: 'testnet', + filters: { + events: [ + { + type: 'contract_call', + contract_identifier: 'SP...XYZ.counter', + function_name: 'increment', + }, + ], + }, + action: { + type: 'http_post', + url: 'https://example.com/webhooks', + }, + options: { + decode_clarity_values: true, + enable_on_registration: true, + }, +}); + +console.log('Chainhook UUID:', chainhook.uuid); +console.log('Enabled:', chainhook.status.enabled); // true +``` + +Si no estableces `enable_on_registration`, el chainhook será creado pero deshabilitado por defecto. + +*** + +## enableChainhook + +Enable or disable a single chainhook by UUID. This allows you to enable chainhooks after registration or pause without deleting it. + +### Ejemplos + +```typescript +// Enable a chainhook +await client.enableChainhook('chainhook-uuid', true); + +// Disable a chainhook +await client.enableChainhook('chainhook-uuid', false); +``` + +Returns HTTP `204 No Content` en caso de éxito. + +*** + +## bulkEnableChainhooks + +Habilita o deshabilita múltiples chainhooks a la vez usando filtros. Esto es útil para gestionar muchos chainhooks de manera programática. + +### Por UUIDs + +Habilitar chainhooks específicos por sus UUIDs (máximo 200): + +```typescript +await client.bulkEnableChainhooks({ + enabled: true, + filters: { + // !mark(1:5) + uuids: [ + 'uuid-1', + 'uuid-2', + 'uuid-3', + ], + }, +}); +``` + +### Por URL de Webhook + +Enable all chainhooks that POST to a specific URL: + +```typescript +await client.bulkEnableChainhooks({ + enabled: true, + filters: { + // !mark + webhook_url: 'https://example.com/webhooks', + }, +}); +``` + +### Por Estado + +Habilitar todos los chainhooks con un estado específico: + +```typescript + +await client.bulkEnableChainhooks({ + enabled: true, + filters: { + // !mark + statuses: ['inactive'], + }, +}); +``` + +### Filtros Combinados + +Usa múltiples filtros juntos: + +```typescript +await client.bulkEnableChainhooks({ + enabled: false, // Disable matching chainhooks + filters: { + // !mark(1:2) + webhook_url: 'https://old-server.com/webhooks', + statuses: ['active'], + }, +}); +``` + +This will disable all active chainhooks that POST to the old webhook URL. + +:::next-steps +* [Evaluar](/tools/chainhook/evaluate): Probar chainhooks contra bloques pasados +* [Referencia de Filtros](/tools/chainhook/reference/filters): Explora todas las opciones de filtro +::: diff --git a/content/docs/es/tools/chainhook/(event-handling)/custom-indexer.mdx b/content/docs/es/tools/chainhook/(event-handling)/custom-indexer.mdx deleted file mode 100644 index 90078bd88..000000000 --- a/content/docs/es/tools/chainhook/(event-handling)/custom-indexer.mdx +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Construye un indexador personalizado -description: Crea tu propio indexador de blockchain con Chainhook. ---- -Contenido provisional - nueva guía por crear diff --git a/content/docs/es/tools/chainhook/(event-handling)/example-indexers.mdx b/content/docs/es/tools/chainhook/(event-handling)/example-indexers.mdx deleted file mode 100644 index e9d132555..000000000 --- a/content/docs/es/tools/chainhook/(event-handling)/example-indexers.mdx +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Indexadores de ejemplo -description: Implementaciones de referencia de indexadores Chainhook. ---- -Contenido provisional - nueva guía por crear diff --git a/content/docs/es/tools/chainhook/(event-handling)/payload-handling.mdx b/content/docs/es/tools/chainhook/(event-handling)/payload-handling.mdx deleted file mode 100644 index 34674958d..000000000 --- a/content/docs/es/tools/chainhook/(event-handling)/payload-handling.mdx +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Manejo de carga útil -description: Procesar y manejar las cargas útiles del webhook de Chainhook. ---- -Contenido provisional - nueva guía por crear diff --git a/content/docs/es/tools/chainhook/(event-handling)/webhook-setup.mdx b/content/docs/es/tools/chainhook/(event-handling)/webhook-setup.mdx deleted file mode 100644 index b376507d9..000000000 --- a/content/docs/es/tools/chainhook/(event-handling)/webhook-setup.mdx +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Configuración de webhook -description: Configura puntos finales de webhook para recibir eventos de blockchain. ---- -Contenido provisional - nueva guía por crear diff --git a/content/docs/es/tools/chainhook/(integrations)/register-chainhooks-on-devnet.mdx b/content/docs/es/tools/chainhook/(integrations)/register-chainhooks-on-devnet.mdx deleted file mode 100644 index d3380f3a6..000000000 --- a/content/docs/es/tools/chainhook/(integrations)/register-chainhooks-on-devnet.mdx +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: Registrar Chainhooks en devnet -description: En esta guía, aprenderás cómo registrar automáticamente Chainhooks en devnet usando Clarinet. ---- -## Lo que aprenderás - -:::objectives -* Crear archivos de predicado Chainhook en tu proyecto Clarinet -* Registrar predicados automáticamente al iniciar devnet -* Monitorear la ejecución de Chainhook durante el desarrollo local -::: - -## Requisitos previos - -:::prerequisites -* Clarinet versión 2.1.0 o superior instalada -* Un proyecto de Clarinet con contratos inteligentes -* Comprensión básica de los predicados de Chainhook -::: - -## Registrar Chainhooks en devnet - - - - ### Crear archivos de predicado - - Cree sus archivos de predicado Chainhook en el directorio de su proyecto Clarinet. Puede colocarlos en la raíz del proyecto u organizarlos en una carpeta dedicada. - - ```json chainhooks/increment.json - { - "chain": "stacks", - "uuid": "1", - "name": "Increment Counter", - "version": 1, - "networks": { - "devnet": { - "if_this": { - "scope": "contract_call", - "contract_identifier": "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.counter", - "method": "increment" - }, - "then_that": { - "http_post": { - "url": "http://localhost:3000/api/increment", - "authorization_header": "Bearer devnet-token" - } - } - } - } - } - ``` - - El predicado se activará cada vez que el `increment` el método es llamado en tu contrato de contador. - - - - ### Organizar la estructura del proyecto - - Estructure su proyecto con archivos Chainhook en una ubicación lógica. Aquí hay una disposición de proyecto recomendada: - - ``` - my-project/ - ├── contracts/ - │ └── counter.clar - ├── chainhooks/ - │ ├── increment.json - │ └── decrement.json - ├── tests/ - │ └── counter.test.ts - └── Clarinet.toml - ``` - - Clarinet descubrirá automáticamente los archivos de predicado en el directorio de su proyecto al iniciar devnet. - - - - ### Iniciar devnet con registro de Chainhook - - Inicie devnet desde la raíz de su proyecto. Clarinet inicia automáticamente un servicio Chainhook y registra todos los archivos de predicado que encuentra. - - ```terminal - $ clarinet devnet start - ``` - - Busque la confirmación de registro en la salida de su terminal: - - ```console - Computing deployment plan - ✔ Deployment plan completed - - INFO Feb 5 15:20:07.233382 2 chainhooks registered - ``` - - Este mensaje confirma que sus predicados están activos y monitoreando la cadena de bloques local. - - - - ### Verificar la ejecución de Chainhook - - Interactúe con sus contratos para activar los Chainhooks registrados. Supervise la terminal de devnet para obtener confirmaciones de ejecución. - - ```terminal - $ clarinet console - >> (contract-call? .counter increment) - ``` - - Observa la terminal de devnet para ver los hooks activados: - - ```console - INFO Feb 5 15:21:07.233382 1 hooks triggered - ``` - - Su punto final de webhook o salida de archivo recibirá la carga útil del evento según su `then_that` configuración. - - - -## Próximos pasos - -:::next-steps -* [Alcances de Bitcoin](/tools/chainhook/reference/bitcoin-scopes): Explora los tipos de eventos disponibles para Bitcoin -* [Ámbitos de pilas](/tools/chainhook/reference/stacks-scopes): Explora los tipos de eventos disponibles para Stacks -::: diff --git a/content/docs/es/tools/chainhook/(overview)/faq.mdx b/content/docs/es/tools/chainhook/(overview)/faq.mdx new file mode 100644 index 000000000..c1b9ea4bd --- /dev/null +++ b/content/docs/es/tools/chainhook/(overview)/faq.mdx @@ -0,0 +1,386 @@ +--- +title: Preguntas Frecuentes +description: Preguntas frecuentes sobre Chainhook 2.0 Beta +--- +## Chainhook 2.0 Beta + + + + ¿Cuál es el objetivo/propósito de la Beta de Chainhook 2.0? + + + Nuestro objetivo durante la Beta es aprender tanto como sea posible sobre la confiabilidad, rendimiento y experiencia del desarrollador de Chainhooks 2.0 en anticipación del lanzamiento completo. Si encuentras algún problema, tienes alguna pregunta, o te gustaría compartir comentarios durante la Beta, por favor ponte en contacto con [beta@hiro.so](mailto\:beta@hiro.so). + + + + + ¿Es gratuita la versión beta de Chainhook 2.0? + + + ¡Sí! La Beta de Chainhooks 2.0 es gratuita para todos los participantes. + + + + + ¿Habrá configuración o límites de velocidad impuestos durante la Beta? + + + Todos los usuarios Beta estarán inicialmente limitados a un máximo de 10 configuraciones de Chainhook. + + + + + ¿Cómo se fijarán los precios de los Chainhooks después de la Beta? + + + Los Chainhooks se cobrarán utilizando un modelo de crédito por cada entrega, permitiendo a los usuarios seleccionar diferentes límites de crédito dependiendo de su uso. Para los usuarios de Chainhooks 2.0 Beta, sus límites post-beta se determinarán inicialmente por su nivel de suscripción existente de Hiro. + + + + +## Gestión de Versiones + + + + ¿Qué pasará con los Chainhooks heredados existentes que ejecutan en v1.0? + + + Los usuarios con Chainhooks ejecutándose en v1.0 aún podrán verlos y recibir entregas, pero una vez que se lance la beta, todos los nuevos Chainhooks creados en la Plataforma se ejecutarán en v2.0. + + La API tampoco admitirá Chainhooks ejecutándose en v1.0. + + :::callout + Aprende cómo migrar tus chainhooks de v1 a v2 en el [Guía de Migración](/tools/chainhook/migration). + ::: + + + + + Si v2.0 y v1.0 estarán activos al mismo tiempo, ¿cómo gestionamos ambos? + + + En la Plataforma, los chainhooks v1 son de solo lectura. Puedes verlos y continuarán entregando eventos, pero no puedes modificarlos a través de la interfaz de usuario de la Plataforma. + + Para modificar los chainhooks v1 programáticamente, use el [API de Plataforma](/apis/platform-api). Sin embargo, recomendamos migrar a chainhooks v2 en su lugar. Consulta el [Guía de Migración](/tools/chainhook/migration) para un tutorial completo. + + + + + ¿Cómo migro mis chainhooks de v1 a v2? + + + Para migrar tus chainhooks v1 a v2, sigue los pasos descritos en el [Guía de Migración](/tools/chainhook/migration). + + + + +## Plataforma vs SDK + + + + ¿Puedo editar mis v2 Chainhooks? + + + La Plataforma actualmente no admite la edición de chainhooks existentes. Debes usar el [SDK de Chainhook](/tools/chainhook/chainhook-sdk) o [API de Chainhook](/apis/chainhook-api) para editar tus chainhooks. + + + + + ¿Cuándo debo usar la Plataforma vs el SDK? + + +
+

Utiliza la Plataforma cuando:

+ +
    +
  • Creando chainhooks simples con una interfaz de usuario
  • +
  • Visualización del estado y actividad de chainhook
  • +
  • Gestión de claves API
  • +
  • Comenzando rápidamente
  • +
+ +

Usa el SDK/API cuando:

+ +
    +
  • Necesitas editar chainhooks existentes
  • +
  • Gestión programática de chainhook
  • +
  • Automatizando operaciones de chainhook
  • +
  • Integrando en pipelines de CI/CD
  • +
  • Gestión de muchos chainhooks a escala
  • +
+
+
+
+
+ +## Comenzando + + + + ¿Cómo obtengo una clave API de Hiro? + + +
+
    +
  1. Visitar platform.hiro.so
  2. +
  3. Inicia sesión o crea una cuenta
  4. +
  5. Navega a la sección de Claves API
  6. +
  7. Genera o copia tu clave API
  8. +
+ +

Almacena tu clave API de forma segura en variables de entorno y nunca la confirmes en el control de versiones.

+
+
+
+ + + ¿Cuál es la diferencia entre mainnet y testnet? + + +
+
    +
  • Red principal - La blockchain de producción Stacks con valor económico real
  • +
  • Testnet - Una red de prueba para desarrollo y pruebas
  • +
+ +

Siempre prueba tus chainhooks en testnet antes de desplegar a mainnet. Usa estas URLs base:

+ +
+          
+            {`import { CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client';
+
+              // Testnet (for development)
+              CHAINHOOKS_BASE_URL.testnet  // https://api.testnet.hiro.so
+
+              // Mainnet (for production)
+              CHAINHOOKS_BASE_URL.mainnet  // https://api.mainnet.hiro.so`}
+          
+        
+
+
+
+
+ +## Solución de problemas + + + + Mi webhook no está recibiendo eventos. ¿Qué debería verificar? + + +
+

1. Verificar que chainhook esté habilitado

+ +
+          
+            {`const chainhook = await client.getChainhook('uuid');
+              console.log('Enabled:', chainhook.status.enabled);`}
+          
+        
+ +

2. Verifica que tu URL de webhook sea accesible

+ +
    +
  • URL must be publicly accessible over HTTPS
  • +
  • Prueba con herramientas como webhook.site o ngrok para desarrollo local
  • +
+ +

3. Verifica que tus filtros coincidan con los eventos de blockchain

+ +
    +
  • Usar evaluateChainhook() contra un bloque que sabes que contiene eventos coincidentes
  • +
  • Revisa la Referencia de Filtros para una sintaxis correcta
  • +
+ +

4. Verificar los logs del endpoint del webhook

+ +
    +
  • Ensure your endpoint returns HTTP 200
  • +
  • Busca mensajes de error en los logs de tu aplicación
  • +
+
+
+
+ + + Estoy obteniendo errores 401 No autorizado + + +
+

Tu clave API puede ser incorrecta o estar faltando:

+ +
+          
+            {`// Verify API key is set
+              if (!process.env.HIRO_API_KEY) {
+              throw new Error('HIRO_API_KEY environment variable is required');
+              }
+
+              // Test the key
+              try {
+              await client.getChainhooks();
+              console.log('✅ API key is valid');
+              } catch (error) {
+              console.error('❌ Invalid API key');
+              }`}
+          
+        
+
+
+
+ + + ¿Cómo pruebo mi chainhook antes de habilitarlo? + + +
+

Usa el evaluateChainhook() método para probar contra bloques conocidos:

+ +
+          
+            {`// Create chainhook (disabled by default)
+              const chainhook = await client.registerChainhook({
+              // ... your configuration
+              // Do NOT set enable_on_registration
+              });
+
+              // Test against a specific block
+              await client.evaluateChainhook(chainhook.uuid, {
+              block_height: 150000,
+              });
+
+              // Check your webhook endpoint for the payload
+              // If it works, enable the chainhook
+              await client.enableChainhook(chainhook.uuid, true);`}
+          
+        
+
+
+
+ + + ¿Por qué estoy recibiendo entregas de webhook duplicadas? + + +
+

Chainhook may retry webhook deliveries if your endpoint doesn't return HTTP 200. Make your webhook processing idempotent:

+ +
+          
+            {`app.post('/webhooks', async (req, res) => {
+              const blockHeight = req.body.event.apply[0].block_identifier.index;
+
+              // Check if already processed
+              const exists = await db.isBlockProcessed(blockHeight);
+              if (exists) {
+                console.log(\`Block \${blockHeight} already processed, skipping\`);
+                return res.sendStatus(200);
+              }
+
+              // Process the webhook
+              await processWebhook(req.body);
+
+              // Return 200 immediately
+              res.sendStatus(200);
+              });`}
+          
+        
+
+
+
+
+ +## Preguntas Frecuentes + + + + ¿Puedo usar chainhooks para notificaciones en tiempo real? + + + ¡Sí! Los Chainhooks están diseñados para la entrega en tiempo real de eventos de blockchain. Las cargas útiles de webhook se entregan en segundos después de que ocurran eventos en la cadena. + + + + + ¿Cómo manejan los chainhooks las reorganizaciones de blockchain? + + +
+

Chainhook detecta automáticamente las reorganizaciones y envía ambos rollback y aplicar eventos. Siempre procesa los eventos de reversión primero para mantener la consistencia de los datos.

+

Aprende más en el Guía de uso.

+
+
+
+ + + ¿Puedo filtrar por múltiples tipos de eventos? + + +
+

¡Sí! Puedes incluir múltiples filtros en el eventos matriz. El chainhook se activará si cualquier coincidencias de filtro:

+ +
+          
+            {`{
+              filters: {
+                events: [
+                  { type: 'ft_transfer', asset_identifier: 'SP...TOKEN::usdc' },
+                  { type: 'nft_mint', asset_identifier: 'SP...NFT::collectible' },
+                  { type: 'contract_call', contract_identifier: 'SP...DEX.swap' },
+                ],
+              }
+              }`}
+          
+        
+
+
+
+ + + ¿Qué sucede si mi endpoint de webhook está inactivo? + + + Chainhook reintentará las entregas de webhook con retroceso exponencial. Sin embargo, el tiempo de inactividad prolongado puede resultar en eventos perdidos. Implemente un manejo adecuado de errores y monitoreo para aplicaciones de producción. + + + + + ¿Puedo probar chainhooks localmente? + + +
+

¡Sí! Usa herramientas como ngrok o webhook.site para exponer tu servidor de desarrollo local:

+ +
+          
+            {`# Expose local port 3000
+              ngrok http 3000
+
+              # Use the ngrok URL in your chainhook
+              # https://abc123.ngrok-free.app/webhooks`}
+          
+        
+
+
+
+
+ +*** + +## Dónde Obtener Ayuda + +* **Discord**: Únete a la **#chainhook** canal en [Discord](https://stacks.chat/) bajo las Herramientas para Desarrolladores de Hiro +* **Horario de Oficina Semanal**: Todos los jueves a las 11am ET ([añadir al calendario](https://www.addevent.com/event/oL21905919)) +* **Soporte por Correo Electrónico**I notice that the text you provided appears to be just a single colon (":") with no other content to translate. Since there's no actual text content to translate from English to Spanish, I'll return the same colon: + + : [beta@hiro.so](mailto\:beta@hiro.so) +* **Problemas de GitHub**: [hirosystems/chainhook](https://github.com/hirosystems/chainhook/issues) + +*** + +## Próximos Pasos + +:::next-steps +* [Guía de Migración](/tools/chainhook/migration): Migrar de v1 a v2 +* [Inicio Rápido del SDK](/tools/chainhook/quickstart): Comienza con el SDK +::: diff --git a/content/docs/es/tools/chainhook/(overview)/migration.mdx b/content/docs/es/tools/chainhook/(overview)/migration.mdx new file mode 100644 index 000000000..9fa6e681b --- /dev/null +++ b/content/docs/es/tools/chainhook/(overview)/migration.mdx @@ -0,0 +1,231 @@ +--- +title: Guía de migración +description: Guía para migrar chainhooks heredados a v2 +--- +:::callout +### Gestión de chainhooks v1 + +Los chainhooks v1 heredados permanecen de solo lectura en la UI de la Plataforma; los gestionas a través del [API de Plataforma](/apis/platform-api/reference/chainhooks/list). +::: + +## Lo que aprenderás + +:::objectives +* Capturar y analizar todos los chainhooks v1 existentes. +* Convierte filtros basados en predicados en definiciones explícitas de eventos v2. +* Registra chainhooks v2, prueba la entrega y retira los originales de forma segura. +::: + +## Requisitos previos + +:::prerequisites +* Clave API de Hiro para acceso tanto a la Plataforma como a la API de Chainhook. +::: + + + + ### Obtener una lista de chainhooks v1 + + Usa la API de la Plataforma para obtener los chainhooks que quieres migrar. + + ```ts index.ts + const response = await fetch(`https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks`, { + headers: { + 'content-type': 'application/json' + } + }); + + const chainhooks = await response.json(); + ``` + + + + ### Comenzar el mapeo al nuevo formato v2 + + Traduce las estructuras v1 a formatos v2 antes de crear nuevos hooks. La siguiente tabla muestra el mapeo entre las estructuras v1 y v2: + + | v1 | v2 | Notas | + |------------|-----------|-------| + | `if_this.scope` I notice that you've provided the translation rules but the actual text to translate appears to be just a single vertical bar "|". + + | `filters.events[].type` | Reemplazar `scope/action` combos con un solo tipo de evento. | + | `if_this.actions` I don't see any text to translate in your message. You've provided the translation rules and formatting, but the actual text to translate appears to be missing after "Text to translate:" - there's only a vertical bar "|". + + Could you please provide the text you'd like me to translate from English to Spanish? `type` | `transfer` se asigna a `*_transfer`. | + | `then_that.http_post.url` | `action.url` | v2 maneja secretos automáticamente. | + | `networks.mainnet` | `network: "mainnet"` | Crea un hook v2 por red. | + | `authorization_header` | Gestión de secretos de webhook | Usar `rotateConsumerSecret()` después del registro. | + + #### Ejemplo + + + ```json !! Legacy + { + "name": "stx-transfers", + "networks": { + "mainnet": { + "if_this": { "scope": "stx_event", "actions": ["transfer"] }, + "then_that": { "http_post": { "url": "https://example.com/webhooks" } } + } + } + } + ``` + + ```json !! v2 + { + "version": "1", + "name": "stx-transfers", + "chain": "stacks", + "network": "mainnet", + "filters": { + "events": [{ "type": "stx_transfer" }] + }, + "action": { + "type": "http_post", + "url": "https://example.com/webhooks" + }, + "options": { + "decode_clarity_values": true, + "enable_on_registration": true + } + } + ``` + + + Para más detalles sobre los cambios de formato, consulta el [Filtros](/tools/chainhook/reference/filters) guía de referencia. + + + + ### Crear chainhooks v2 + + Register each chainhook with the SDK or REST API, mirroring the mapped filters. + + + ```ts !! api.ts + const v2Chainhook = JSON.stringify({ + "version": "1", + "name": "stx-transfers", + "chain": "stacks", + "network": "mainnet", + "filters": { + "events": [{ "type": "stx_transfer" }] + }, + "action": { + "type": "http_post", + "url": "https://example.com/webhooks" + }, + "options": { + "decode_clarity_values": true, + "enable_on_registration": true + } + }); + + const response = await fetch('https://api.hiro.so/chainhooks/v1/me/', { + method: 'POST', + headers: { + 'x-api-key': process.env.HIRO_API_KEY!, + 'content-type': 'application/json', + }, + body: JSON.stringify(v2Chainhook), + }); + ``` + + ```ts !! sdk.ts + const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, + apiKey: process.env.HIRO_API_KEY!, + }); + const chainhook = await client.registerChainhook( + { + "version": "1", + "name": "stx-transfers", + "chain": "stacks", + "network": "mainnet", + "filters": { + "events": [{ "type": "stx_transfer" }] + }, + "action": { + "type": "http_post", + "url": "https://example.com/webhooks" + }, + "options": { + "decode_clarity_values": true, + "enable_on_registration": true + } + } + ); + ``` + + + + + ### Validar y limpiar chainhooks heredados + + Transmite eventos a través de ambas versiones, confirma la entrega, luego limpia las definiciones heredadas. + + :::callout + type: info + + ### Mejores prácticas + + Mantén ambos hooks v1 y v2 activos hasta que verifiques la paridad de entrega. + ::: + + #### Verificaciones de habilitación en v2 + + + ```ts !! api.ts + const response = await fetch(`https://api.hiro.so/chainhooks/v1/me/${uuid}`, { + method: 'GET', + headers: { + 'x-api-key': process.env.HIRO_API_KEY!, + 'content-type': 'application/json', + }, + }); + + const chainhook = await response.json(); + console.log(chainhook.status.enabled); + ``` + + ```ts !! sdk.ts + const chainhook = await client.getChainhook(uuid); + console.log(chainhook.status.enabled); + ``` + + + #### Eliminar chainhooks legacy + + ```ts + const response = await fetch( + `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks/${uuid}`,{ + method: 'DELETE', + headers: { + 'content-type': 'application/json' + } + } + ); + + await response.json(); + ``` + + + +## Referencia de filtros + +### Alcances comunes + +| v1 | Acciones Típicas | v2 | Extras | +|----------|-----------------|-----------|--------| +| `stx_event` | `transfer` I notice that you've provided the critical rules for translation, but the actual text to translate appears to be just a single vertical bar "|". + +| `stx_transfer` | Incluir `sender` o `recipient` filtros según sea necesario. | +| `contract_call` | n/a | `contract_call` | Agregar `contract_identifier` y `function_name`. | +| `ft_event` | `transfer` | `ft_transfer` | Especificar `asset_identifier`. | +| `nft_event` | `transfer`, `mint` | `nft_transfer` · `nft_mint` | Proporcionar `asset_identifier`. | + +Para más detalles, consulta el [Filtros](/tools/chainhook/reference/filters) página de referencia. + +:::next-steps +* [Documentación del SDK](/tools/chainhook/chainhook-sdk): Documentación del SDK +* [Referencia de Filtros](/tools/chainhook/reference/filters): Referencia de Filtros +::: diff --git a/content/docs/es/tools/chainhook/(platform-usage)/create-enable-chainhooks.mdx b/content/docs/es/tools/chainhook/(platform-usage)/create-enable-chainhooks.mdx new file mode 100644 index 000000000..00b893f33 --- /dev/null +++ b/content/docs/es/tools/chainhook/(platform-usage)/create-enable-chainhooks.mdx @@ -0,0 +1,4 @@ +--- +title: Crear y Habilitar Chainhooks +description: Crear y habilitar chainhooks usando la interfaz de usuario de la plataforma +--- diff --git a/content/docs/es/tools/chainhook/(platform-usage)/manage-api-keys.mdx b/content/docs/es/tools/chainhook/(platform-usage)/manage-api-keys.mdx new file mode 100644 index 000000000..db4eabd12 --- /dev/null +++ b/content/docs/es/tools/chainhook/(platform-usage)/manage-api-keys.mdx @@ -0,0 +1,4 @@ +--- +title: Gestionar claves API +description: Generar y gestionar claves API de Hiro en la interfaz de usuario de la plataforma +--- diff --git a/content/docs/es/tools/chainhook/(platform-usage)/platform-quickstart.mdx b/content/docs/es/tools/chainhook/(platform-usage)/platform-quickstart.mdx new file mode 100644 index 000000000..b48a7f066 --- /dev/null +++ b/content/docs/es/tools/chainhook/(platform-usage)/platform-quickstart.mdx @@ -0,0 +1,4 @@ +--- +title: Inicio Rápido de la Plataforma +description: Crea tu primer chainhook en la Plataforma Hiro en 5 minutos +--- diff --git a/content/docs/es/tools/chainhook/(platform-usage)/platform-usage.mdx b/content/docs/es/tools/chainhook/(platform-usage)/platform-usage.mdx new file mode 100644 index 000000000..9ba466720 --- /dev/null +++ b/content/docs/es/tools/chainhook/(platform-usage)/platform-usage.mdx @@ -0,0 +1,60 @@ +--- +title: Uso de la Plataforma +description: Gestione los chainhooks visualmente con la interfaz web de Hiro Platform +--- +## Resumen + +La Plataforma Hiro proporciona una interfaz basada en web para gestionar chainhooks sin escribir código. + +| Característica | IU de Plataforma | SDK de Chainhook | +|---------|------------|---------------| +| Crear chainhooks | ✓ Constructor visual | ✓ Programático | +| Ver estado y actividad | ✓ Vista de panel | ✓ Llamadas API | +| Gestionar claves API | ✓ Gestión de IU | - | +| Editar chainhooks | - | ✓ Control completo | +| Automatización | - | ✓ Integración CI/CD | +| Comenzar rápidamente | ✓ No se necesita código | Requiere configuración | + +## Cuándo Usar la Interfaz de Usuario de la Plataforma + +**Elige la Platform UI cuando:** + +* Quieres crear chainhooks visualmente sin código +* Necesita ver el estado y actividad de chainhook en un panel de control +* Están gestionando claves de API +* Quiere empezar rápidamente + +**Elige el [SDK de Chainhook](/tools/chainhook/chainhook-sdk) cuando tú:** + +* Necesito editar chainhooks existentes (no disponible en la interfaz de la plataforma) +* Quiere automatizar las operaciones de chainhook +* Estás integrando la gestión de chainhook en tu aplicación +* Gestionar chainhooks programáticamente a escala + +## Accediendo a la Plataforma + +### Iniciar Sesión + +1. Visitar [platform.hiro.so](https://platform.hiro.so) +2. Iniciar sesión con tu cuenta +3. Navegar a la sección Chainhooks + +:::callout +Si no tienes una cuenta, regístrate en [platform.hiro.so](https://platform.hiro.so) para comenzar. +::: + +### Características de la Plataforma + +La interfaz de usuario de la plataforma proporciona: + +* **Constructor visual de chainhook** - Crear chainhooks con entradas de formulario y menús desplegables +* **Panel de actividad** - Monitorear el estado del chainhook y conteos de activadores +* **Gestión de claves de API** - Generar y rotar claves API +* **Pruebas de webhook** - Prueba tus endpoints de webhook + +## Próximos Pasos + +:::next-steps +* [Inicio Rápido de la Plataforma](/tools/chainhook/platform-quickstart): Crea tu primer chainhook en minutos +* [Crear y Habilitar Chainhooks](/tools/chainhook/create-enable-chainhooks): Aprende cómo configurar chainhooks en la UI +::: diff --git a/content/docs/es/tools/chainhook/(platform-usage)/view-chainhooks.mdx b/content/docs/es/tools/chainhook/(platform-usage)/view-chainhooks.mdx new file mode 100644 index 000000000..dcbd97ebc --- /dev/null +++ b/content/docs/es/tools/chainhook/(platform-usage)/view-chainhooks.mdx @@ -0,0 +1,4 @@ +--- +title: Ver Chainhooks +description: Ver y monitorear el estado y actividad de chainhook en la Interfaz de Usuario de la Plataforma +--- diff --git a/content/docs/es/tools/chainhook/index.mdx b/content/docs/es/tools/chainhook/index.mdx index c79464f23..76297bb56 100644 --- a/content/docs/es/tools/chainhook/index.mdx +++ b/content/docs/es/tools/chainhook/index.mdx @@ -1,56 +1,42 @@ --- title: Chainhook -sidebarTitle: Visión general -description: Chainhook es un indexador consciente de las reorganizaciones que proporciona datos confiables de blockchain para Bitcoin y Stacks. +sidebarTitle: Descripción general +description: Chainhook es un servicio de webhook para la blockchain de Stacks que te permite registrar flujos de eventos y definir filtros precisos para capturar datos en cadena mientras suceden. llm: false --- -## Visión general - -Chainhook es un indexador consciente de las reorganizaciones que te permite construir flujos de eventos personalizados de las cadenas de bloques de Bitcoin y Stacks. A diferencia de los indexadores tradicionales, Chainhook maneja automáticamente las reorganizaciones de la cadena de bloques, asegurando que tus datos se mantengan precisos sin necesidad de reindexación manual. - -Para explorar las características de Chainhook con IA, copie y pegue [llms.txt](/tools/chainhook/llms.txt) en tu LLM de elección. - -![Descripción general de Chainhook](/images/tools/chainhook/overview.svg) +:::callout +type: warn -## Características principales +### Chainhook 2.0 Beta -* **Indexación consciente de la reorganización** - Maneja automáticamente las bifurcaciones y reorganizaciones de la cadena de bloques -* **Predicados si-esto-entonces-aquello** - Definir lógica personalizada para activar acciones en eventos específicos de la blockchain -* **Soporte para múltiples cadenas** - Funciona con las cadenas de bloques de Bitcoin y Stacks -* **Desarrollo local** - Probar predicados contra datos históricos de la cadena de bloques +Chainhook 2.0 está actualmente en beta. Si encuentras problemas o tienes comentarios, por favor contacta a [beta@hiro.so](mailto\:beta@hiro.so). +::: -## Instalación +## Descripción general - - ```terminal !! macOS - $ brew install chainhook - ``` +Chainhook facilita la suscripción a la actividad blockchain en Stacks mediante el registro de flujos de eventos, filtrando exactamente los datos que te interesan y enviándolos directamente a tu aplicación en tiempo real. - ```terminal !! Linux - $ sudo snap install chainhook - ``` +Con Chainhook 2.0, puedes gestionar chainhooks a través de: - ```terminal !! Windows - $ winget install HiroSystems.Chainhook - ``` +* **[SDK de Chainhook](/tools/chainhook/chainhook-sdk)** - Cliente TypeScript/JavaScript para gestión programática +* **[Plataforma Hiro](/tools/chainhook/platform-usage)** - UI basada en web para la creación visual de chainhooks +* **[API de Chainhook](/apis/chainhook-api)** - Direct REST API access - ```terminal !! Cargo - $ git clone https://github.com/hirosystems/chainhook.git - $ cd chainhook && cargo chainhook-install - ``` - +## Características Clave -## Próximos pasos +* **Indexación consciente de reorganización** - Maneja automáticamente las bifurcaciones y reorganizaciones de la blockchain +* **Filtrado de eventos** - Define lógica personalizada para activar acciones en eventos específicos de blockchain +* **Evaluación histórica** - Prueba chainhooks contra bloques pasados para indexación o depuración :::next-steps -* [Inicio rápido](/tools/chainhook/quickstart): Comienza con Chainhook. -* [Registrar chainhooks localmente](/tools/chainhook/register-chainhooks-on-devnet): Registrar chainhooks en tu blockchain local. +- [Introducción del SDK](/tools/chainhook/introduction): Comienza con el SDK de Chainhook +- [Guía de migración](/tools/chainhook/migration): Guía de migración para actualizar a Chainhook 2.0 ::: :::callout type: help -### ¿Necesitas ayuda para construir con Chainhook? +### ¿Necesitas ayuda con Chainhook? -Contáctenos en el **#chainhook** canal encendido [Discord](https://stacks.chat/) bajo la sección de Herramientas para Desarrolladores de Hiro. También hay una [horario de oficina semanal](https://www.addevent.com/event/oL21905919) llamada todos los jueves a las 11 am ET. +Comunícate con nosotros en el #chainhook canal activado [Discord](https://stacks.chat/) bajo la sección de Herramientas para Desarrolladores de Hiro. ::: diff --git a/content/docs/es/tools/chainhook/meta.json b/content/docs/es/tools/chainhook/meta.json index 4ce4bb6a9..7dbb00316 100644 --- a/content/docs/es/tools/chainhook/meta.json +++ b/content/docs/es/tools/chainhook/meta.json @@ -2,7 +2,7 @@ "title": "Chainhook", "root": true, "pages": [ - "---Chainhook---", + "---Chainhook|beta---", "index", "...(overview)", "---Chainhook CLI---", diff --git a/content/docs/es/tools/chainhook/reference/filters.mdx b/content/docs/es/tools/chainhook/reference/filters.mdx new file mode 100644 index 000000000..c5c19bf0e --- /dev/null +++ b/content/docs/es/tools/chainhook/reference/filters.mdx @@ -0,0 +1,389 @@ +--- +title: Filtros +description: Referencia completa para todos los tipos de filtros de Chainhook +--- +Los filtros definen qué eventos de blockchain activarán tu chainhook. + +Aquí está una referencia completa para todos los tipos de filtros de Chainhook: + +| Filtro | Cuándo usar | +|--------|-------------| +| `ft_event` | Captura *cada* SIP-010 transferir, acuñar o quemar a través de activos. | +| `ft_transfer` | Follow a single asset such as USDC; optionally add `sender`/`receiver` para disparadores a nivel de cartera. | +| `ft_mint` | Rastrea expansiones de suministro o flujos de entrada de puentes para un activo (configurar `asset_identifier`). | +| `ft_burn` | Rastrea redenciones o contracciones de suministro para un activo (establecer `asset_identifier`). | +| `nft_event` | Monitorea cada transferencia, acuñación o quema para todas las colecciones. | +| `nft_transfer` | Sigue una colección SIP-009; añade `sender`, `receiver`, o `value` para orientación de propietario/token. | +| `nft_mint` | Observa cada nuevo mint para una colección (set `asset_identifier`). | +| `nft_burn` | Captura quemaduras o canjes (establecer `asset_identifier`). | +| `stx_event` | Capturar todas las transferencias nativas, acuñaciones o quemas. | +| `stx_transfer` | Rastrea cada transferencia de STX; añade `sender` o `receiver` para destacar directores específicos. | +| `contract_deploy` | Reacciona a nuevos contratos que entren en la red. | +| `contract_call` | Observa cada invocación; reduce con `contract_identifier` y `function_name`. | +| `contract_log` | Capturar `print`/salida de log de un contrato (establecer `contract_identifier`). | +| `coinbase` | Observa las recompensas de los mineros llegando a la cadena. | +| `tenure_change` | Seguimiento de actualizaciones de tenencia de Prueba de Transferencia; agregar `cause` I don't see any text to translate in your message. You've provided the rules and instructions, but the actual text content appears to be missing after "Text to translate:". Could you please provide the text you'd like me to translate from English to Spanish?`"block_found"` o `"extended"`) para especificidad. | + +## Eventos de Tokens Fungibles (FT) + +### Cualquier Evento FT + +Coincidir con cualquier evento de token fungible (transferencia, quema o acuñación): + +```json +{ + "type": "ft_event" +} +``` + +### Transferencia FT + +Coincidir transferencias FT para un activo específico: + +```json +{ + "type": "ft_transfer", + // !mark + "asset_identifier": "SP...ABC.ft::usdc" +} +``` + +Filtrar por remitente: + +```json +{ + "type": "ft_transfer", + "asset_identifier": "SP...ABC.ft::usdc", + // !mark + "sender": "SP...FROM" +} +``` + +Filtrar por receptor: + +```json +{ + "type": "ft_transfer", + "asset_identifier": "SP...ABC.ft::usdc", + // !mark + "receiver": "SP...TO" +} +``` + +### Acuñación de FT + +Coincidir eventos de acuñación FT: + +```json +{ + "type": "ft_mint", + "asset_identifier": "SP...ABC.ft::usdc" +} +``` + +### FT Burn + +Coincidir eventos de quema de FT: + +```json +{ + "type": "ft_burn", + "asset_identifier": "SP...ABC.ft::usdc" +} +``` + +*** + +## Eventos de Token No Fungible (NFT) + +### Cualquier Evento NFT + +Coincide con cualquier evento de NFT (transferencia, quema o acuñación): + +```json +{ + "type": "nft_event" +} +``` + +### Transferencia de NFT + +Coincidir transferencias de NFT para una colección específica: + +```json +{ + "type": "nft_transfer", + // !mark + "asset_identifier": "SP...COLL.nft::collectible" +} +``` + +Filtrar por remitente: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + // !mark + "sender": "SP...FROM" +} +``` + +Filtrar por receptor: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + // !mark + "receiver": "SP...TO" +} +``` + +Filtrar por ID de token específico: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + // !mark + "value": "u123" +} +``` + +### Acuñación de NFT + +Hacer coincidir eventos de acuñación de NFT: + +```json +{ + "type": "nft_mint", + "asset_identifier": "SP...COLL.nft::collectible" +} +``` + +### Quema de NFT + +Coincidir eventos de quema de NFT: + +```json +{ + "type": "nft_burn", + "asset_identifier": "SP...COLL.nft::collectible" +} +``` + +*** + +## Eventos STX + +### Cualquier Evento STX + +Coincidir con cualquier evento STX (transferencia, quema o acuñación): + +```json +{ + "type": "stx_event" +} +``` + +### Transferencia STX + +Coincide con cualquier transferencia de STX: + +```json +{ + "type": "stx_transfer" +} +``` + +Filtrar por remitente: + +```json +{ + "type": "stx_transfer", + // !mark + "sender": "SP...SENDER" +} +``` + +Filtrar por receptor: + +```json +{ + "type": "stx_transfer", + // !mark + "receiver": "SP...RECEIVER" +} +``` + +*** + +## Eventos de Contrato + +### Implementación de Contrato + +Coincidir con cualquier despliegue de contrato: + +```json +{ + "type": "contract_deploy" +} +``` + +Filtrar por implementador: + +```json +{ + "type": "contract_deploy", + // !mark + "sender": "SP...DEPLOYER" +} +``` + +### Llamada de Contrato + +Coincidir con cualquier llamada de contrato: + +```json +{ + "type": "contract_call" +} +``` + +Coincidir llamadas a un contrato específico: + +```json +{ + "type": "contract_call", + // !mark + "contract_identifier": "SP...XYZ.counter" +} +``` + +Coincidir llamadas a una función específica: + +```json +{ + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter", + // !mark + "function_name": "increment" +} +``` + +Filtrar por llamador: + +```json +{ + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter", + "function_name": "increment", + // !mark + "sender": "SP...CALLER" +} +``` + +### Registro de Contrato + +Coincide con cualquier evento de impresión: + +```json +{ + "type": "contract_log" +} +``` + +Coincidir eventos de impresión de contrato: + +```json +{ + "type": "contract_log", + // !mark + "contract_identifier": "SP...XYZ.counter" +} +``` + +Filtrar por remitente de transacción: + +```json +{ + "type": "contract_log", + "contract_identifier": "SP...XYZ.counter", + // !mark + "sender": "SP...SENDER" +} +``` + +*** + +## Eventos del Sistema + +### Coinbase + +Coincidir eventos de coinbase (recompensas de bloque): + +```json +{ + "type": "coinbase" +} +``` + +### Cambio de Tenencia + +Coincidir con cualquier cambio de tenure: + +```json +{ + "type": "tenure_change" +} +``` + +Coincidir cambios de tenencia por causa (bloque encontrado): + +```json +{ + "type": "tenure_change", + // !mark[/"block_found"/] + "cause": "block_found" +} +``` + +Cambios de permanencia coincidentes por causa (extendido): + +```json +{ + "type": "tenure_change", + // !mark[/"extended"/] + "cause": "extended" +} +``` + +*** + +## Combinando Filtros + +Puedes combinar múltiples filtros en el `filters.events` array. Un chainhook se activará si **cualquier** de los filtros coinciden: + +```json +{ + "filters": { + "events": [ + { + "type": "ft_transfer", + "asset_identifier": "SP...ABC.token::diko" + }, + { + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter", + "function_name": "increment" + } + ] + } +} +``` + +:::next-steps +* [Referencia de Opciones](/tools/chainhook/reference/options): Configurar el enriquecimiento de carga útil y las ventanas de evaluación +* [Registrar y Habilitar](/tools/chainhook/register-enable): Crea tu primer chainhook +::: diff --git a/content/docs/es/tools/chainhook/reference/options.mdx b/content/docs/es/tools/chainhook/reference/options.mdx new file mode 100644 index 000000000..3b31ba846 --- /dev/null +++ b/content/docs/es/tools/chainhook/reference/options.mdx @@ -0,0 +1,221 @@ +--- +title: Opciones +description: Referencia completa para todas las opciones de configuración de Chainhook +--- +Las opciones controlan el enriquecimiento de carga útil y las ventanas de evaluación para tu chainhook. La `options` el campo es opcional y puede omitirse o establecerse a `null`. + +:::callout +Todas las opciones booleanas tienen como valor predeterminado `false` si se omite. Las opciones de enteros son opcionales. +::: + +*** + +## Opciones de carga útil + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `decode_clarity_values` | boolean | `false` | Incluir valores de Clarity legibles para humanos | +| `include_contract_abi` | boolean | `false` | Incluir ABI del contrato en los despliegues | +| `include_contract_source_code` | boolean | `false` | Incluir código fuente en despliegues | +| `include_post_conditions` | boolean | `false` | Incluir post-condiciones en metadatos | +| `include_raw_transactions` | boolean | `false` | Incluir hex de transacción sin procesar | +| `include_block_metadata` | boolean | `false` | Incluir metadatos de bloque (Stacks & Bitcoin) | +| `include_block_signatures` | boolean | `false` | Incluir firmas de bloque y firmantes | +| `enable_on_registration` | boolean | `false` | Habilitar chainhook inmediatamente en la creación | +| `expire_after_evaluations` | integer | ninguno | Expira después de evaluar N bloques | +| `expire_after_occurrences` | integer | ninguno | Expira después de N coincidencias | + +### decode\_clarity\_values + +Incluir legible para humanos `repr` (y tipos inferidos) junto con `hex` para argumentos, registros y resultados. + +* **Tipo**I notice that the text you provided for translation is just a single colon (:). Since this appears to be formatting punctuation rather than translatable content, and following your rules about preserving formatting exactly, the translation would be: + + : `boolean` +* **Predeterminado**: `false` + +```json +{ + "options": { + "decode_clarity_values": true + } +} +``` + +*** + +### include\_contract\_abi + +Incluir la ABI del contrato en las operaciones de despliegue. Esto mejora el tipado de argumentos al decodificar. + +* **Tipo**: `boolean` +* **Predeterminado**: `false` + +```json +{ + "options": { + "include_contract_abi": true + } +} +``` + +*** + +### include\_contract\_source\_code + +Incluir el código fuente del contrato en las operaciones de despliegue. + +* **Tipo**: `boolean` +* **Predeterminado**: `false` + +```json +{ + "options": { + "include_contract_source_code": true + } +} +``` + +*** + +### include\_post\_conditions + +Incluir post-condiciones decodificadas en los metadatos de transacción. + +* **Tipo**: `boolean` +* **Predeterminado**: `false` + +```json +{ + "options": { + "include_post_conditions": true + } +} +``` + +*** + +### include\_raw\_transactions + +Incluir hex de transacción sin procesar en los metadatos de transacción. + +* **Tipo**: `boolean` +* **Predeterminado**: `false` + +```json +{ + "options": { + "include_raw_transactions": true + } +} +``` + +*** + +### include\_block\_metadata + +Incluir metadatos de bloque tanto para bloques de Stacks como de Bitcoin, con costos de ejecución, recuento de transacciones, etc. + +* **Tipo**: `boolean` +* **Predeterminado**: `false` + +```json +{ + "options": { + "include_block_metadata": true + } +} +``` + +*** + +### include\_block\_signatures + +Incluir información del firmante y firmas en los metadatos del bloque. + +* **Tipo**I notice that you've provided the translation rules and instructions, but the actual text to translate appears to be just a single colon ":". + + Since there's no translatable content (just a colon), I'll return it as-is: + + : `boolean` +* **Default**: `false` + +```json +{ + "options": { + "include_block_signatures": true + } +} +``` + +*** + +## Opciones de Activación + +### enable\_on\_registration + +Habilitar el chainhook inmediatamente después del registro/creación. Por defecto, un nuevo chainhook está deshabilitado al momento de la creación. + +* **Tipo**I notice that you've provided the translation rules but the actual text to translate appears to be just a single colon ":". + + : `boolean` +* **Predeterminado**I notice that the text you provided appears to be just a single colon ":". Since this is already just a punctuation mark and not actual text content to translate, the translation remains: + + : `false` + +```json +{ + "options": { + "enable_on_registration": true + } +} +``` + +Cuando se establece en `true`, la respuesta incluirá `status.enabled = true` y el chainhook comenzará a procesar eventos inmediatamente. + +*** + +## Opciones de Expiración + +### expire\_after\_evaluations + +Expirar automáticamente el chainhook después de evaluar esta cantidad de bloques. + +* **Tipo**: `integer` +* **Predeterminado**: Ninguno (sin vencimiento) + +```json +{ + "options": { + "expire_after_evaluations": 10000 + } +} +``` + +Esta chainhook expirará después de evaluar 10,000 bloques. + +*** + +### expire\_after\_occurrences + +Hacer expirar automáticamente el chainhook después de esta cantidad de ocurrencias coincidentes. + +* **Tipo**: `integer` +* **Predeterminado**: Ninguno (sin expiración) + +```json +{ + "options": { + "expire_after_occurrences": 250 + } +} +``` + +Este chainhook expirará después de activarse 250 veces. + +*** + +:::next-steps +* [Referencia de Filtros](/tools/chainhook/reference/filters): Aprende sobre todos los filtros de eventos disponibles +* [Anatomía del Payload](/tools/chainhook/reference/payload-anatomy): Comprender la estructura de las cargas útiles de chainhook +::: diff --git a/content/docs/es/tools/chainhook/reference/payload-anatomy.mdx b/content/docs/es/tools/chainhook/reference/payload-anatomy.mdx new file mode 100644 index 000000000..56bc1fa50 --- /dev/null +++ b/content/docs/es/tools/chainhook/reference/payload-anatomy.mdx @@ -0,0 +1,251 @@ +--- +title: Anatomía del payload +description: Comprendiendo la estructura de las cargas útiles de webhook de Chainhook +--- +## Resumen de Payload + +Una carga útil de Chainhook consta de dos secciones principales: **evento** y **chainhook**I notice that you've only provided a colon ":" as the text to translate. Since there's no actual content to translate, I'll return the same: + +: + +* `event` contiene los datos de la blockchain (bloques, transacciones, operaciones) +* `chainhook` contiene metadatos sobre el chainhook que se activó + +*** + +## Estructura Básica + +```json +{ + "event": { + "apply": [...], // Blocks being applied to the chain + "rollback": [...], // Blocks being rolled back (during reorgs) + "chain": "stacks", + "network": "mainnet" + }, + "chainhook": { + "name": "my-chainhook", + "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185" + } +} +``` + +*** + +## Sección de Eventos + +### Aplicar Array + +El `apply` array contiene bloques que se están agregando a la cadena canónica. Cada bloque incluye: + +```json +{ + "timestamp": 1757977309, + "block_identifier": { + "hash": "0xa204da7...", + "index": 3549902 + }, + "parent_block_identifier": { + "hash": "0xad0acff...", + "index": 3549901 + }, + "transactions": [...] +} +``` + +### Rollback Array + +El `rollback` array contiene bloques que están siendo eliminados durante una reorganización de cadena. Misma estructura que `apply`. + +:::callout +Chainhook maneja automáticamente las reorganizaciones enviando eventos de rollback. Tu aplicación debe revertir cualquier cambio de estado de los bloques que fueron revertidos. +::: + +*** + +## Estructura de Transacciones + +Cada transacción en la `transactions` array contiene: + +### Metadatos + +Información a nivel de transacción: + +```json +{ + "metadata": { + "type": "contract_call", + "nonce": 6689, + "result": { + "hex": "0x0703", + "repr": "(ok true)" + }, + "status": "success", + "fee_rate": "3000", + "position": { + "index": 0, + "microblock_identifier": null + }, + "execution_cost": { + "runtime": "7807", + "read_count": "5", + "read_length": "2441", + "write_count": "2", + "write_length": "1" + }, + "sender_address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9", + "sponsor_address": null, + "canonical": true, + "sponsored": false, + "microblock_canonical": true + } +} +``` + +### Identificador de Transacción + +Identificador único para la transacción: + +```json +{ + "transaction_identifier": { + "hash": "0x09defc9a6cd9318b5c458389d4dd57597203ec539818aec0de3cfcfd7af0c2ab" + } +} +``` + +*** + +## Matriz de Operaciones + +Cada transacción incluye un `operations` matriz que describe los cambios de estado. Las operaciones están indexadas secuencialmente. + +### Operación de Tarifa + +Tarifa de transacción pagada: + +```json +{ + "type": "fee", + "amount": { + "value": "-3000", + "currency": { + "symbol": "STX", + "decimals": 6 + } + }, + "status": "success", + "account": { + "address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9" + }, + "metadata": { + "sponsored": false + }, + "operation_identifier": { + "index": 0 + } +} +``` + +### Operación de Llamada de Contrato + +Invocación de función de contrato: + +```json +{ + "type": "contract_call", + "status": "success", + "account": { + "address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9" + }, + "metadata": { + "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token", + "function_name": "transfer", + "args": [ + { + "hex": "0x01000000000000000000000002690ed9fe", + "name": "amount", + "repr": "u10352515582", + "type": "uint" + }, + { + "hex": "0x0516362f36a4c7ca0318a59fe6448fd3a0c32bda724d", + "name": "sender", + "repr": "'SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9", + "type": "principal" + }, + { + "hex": "0x05161740f4690c79466e3389a13476586e0cb3e1dfbf", + "name": "recipient", + "repr": "'SPBM1X391HWMCVHKH6GK8XJRDR6B7REZQYQ8KBCK", + "type": "principal" + }, + { + "hex": "0x09", + "name": "memo", + "repr": "none", + "type": "(optional (buff 34))" + } + ] + }, + "operation_identifier": { + "index": 1 + } +} +``` + +:::callout +El `args` array incluye `repr` y `type` campos cuando `decode_clarity_values` está habilitado en opciones. +::: + +### Operación de Transferencia de Token + +Transferencias FT/NFT/STX: + +```json +{ + "type": "token_transfer", + "amount": { + "value": "-10352515582", + "currency": { + "symbol": "", + "decimals": 0, + "metadata": { + "token_type": "ft", + "asset_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token::diko" + } + } + }, + "status": "success", + "account": { + "address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9" + }, + "operation_identifier": { + "index": 2 + } +} +``` + +### Operación de Registro de Contrato + +Declaraciones de impresión de contratos: + +```json +{ + "type": "contract_log", + "status": "success", + "metadata": { + "topic": "print", + "value": "0x09", + "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token" + }, + "operation_identifier": { + "index": 4 + } +} +``` + +:::next-steps +* [Referencia de Filtros](/tools/chainhook/reference/filters): Configurar qué eventos activan tu chainhook +* [Referencia de Opciones](/tools/chainhook/reference/options): Personalizar el contenido de la carga útil +::: diff --git a/content/docs/es/tools/contract-monitoring/index.mdx b/content/docs/es/tools/contract-monitoring/index.mdx index 388a0883f..8c8d30a36 100644 --- a/content/docs/es/tools/contract-monitoring/index.mdx +++ b/content/docs/es/tools/contract-monitoring/index.mdx @@ -1,29 +1,27 @@ --- title: Monitoreo de contratos -sidebarTitle: Visión general -description: El monitoreo de contratos le permite rastrear eventos de contratos inteligentes en tiempo real y recibir notificaciones por webhook cuando ocurren actividades específicas. +sidebarTitle: Descripción general +description: El monitoreo de contratos te permite rastrear eventos de contratos inteligentes en tiempo real y recibir notificaciones webhook cuando ocurren actividades específicas. llm: false --- -## Visión general - -Para explorar las funciones de monitoreo de contratos con IA, copie y pegue [llms.txt](/tools/contract-monitoring/llms.txt) en tu LLM de elección. +## Descripción general ## Características principales -* **Notificaciones en tiempo real** - Apunta a eventos específicos y recibe webhooks en cuestión de segundos +* **Notificaciones en tiempo real** - Dirige eventos específicos y recibe webhooks en segundos * **Configuración sin código** - Configurar monitores a través de la interfaz de usuario de la plataforma sin escribir código ## Próximos pasos :::next-steps -* [Crear una alerta](/tools/contract-monitoring/create-alert): Configure su primera alerta para llamadas a funciones. -* [Chainhooks](/tools/chainhook): Aprende más sobre Chainhooks y cómo se diferencian del monitoreo de contratos. +* [Crear una alerta](/tools/contract-monitoring/create-alert): Configura tu primera alerta para llamadas de función. +* [Chainhooks](/tools/chainhook): Aprende más sobre Chainhooks y cómo difieren del monitoreo de contratos. ::: :::callout type: help -### ¿Necesita ayuda con el monitoreo de contratos? +### ¿Necesitas ayuda con el monitoreo de contratos? -Contáctenos en el #api canal encendido [Discord](https://stacks.chat/) bajo la sección de Herramientas para Desarrolladores de Hiro. +Contáctanos en el #api canal encendido [Discord](https://stacks.chat/) bajo la sección Hiro Developer Tools. ::: diff --git a/hooks/use-localized-navigation.tsx b/hooks/use-localized-navigation.tsx index ea3bf434c..48d53b86d 100644 --- a/hooks/use-localized-navigation.tsx +++ b/hooks/use-localized-navigation.tsx @@ -1,6 +1,7 @@ 'use client'; import { useMemo } from 'react'; +import { baseOptions } from '@/app/layout.config'; import type { BaseLayoutProps } from '@/components/layouts/shared'; import { useTranslations } from './use-translations'; @@ -11,107 +12,65 @@ import { useTranslations } from './use-translations'; export function useLocalizedNavigation(): BaseLayoutProps['links'] { const t = useTranslations(); - return useMemo( - () => [ - // Tools menu - { - type: 'menu' as const, - text: t.navigation.menus.tools, - items: [ - { - text: t.tools.chainhook.title, - description: t.tools.chainhook.description, - url: '/tools/chainhook', - }, - { - text: t.tools.contractMonitoring.title, - description: t.tools.contractMonitoring.description, - url: '/tools/contract-monitoring', - }, - { - text: t.tools.bitcoinIndexer.title, - description: t.tools.bitcoinIndexer.description, - url: '/tools/bitcoin-indexer', - isNew: true, - }, - ], - }, - // APIs menu - { - type: 'menu' as const, - text: t.navigation.menus.apis, - items: [ - { - text: t.apis.apiKeys.title, - description: t.apis.apiKeys.description, - url: '/resources/guides/api-keys', - }, - { - text: t.apis.rateLimits.title, - description: t.apis.rateLimits.description, - url: '/resources/guides/rate-limits', - }, - { - text: t.apis.stacksApi.title, - description: t.apis.stacksApi.description, - url: '/apis/stacks-blockchain-api', - }, - { - text: t.apis.stacksNodeRpcApi.title, - description: t.apis.stacksNodeRpcApi.description, - url: '/apis/stacks-node-rpc-api', - }, - { - text: t.apis.tokenMetadata.title, - description: t.apis.tokenMetadata.description, - url: '/apis/token-metadata-api', - }, - { - text: t.apis.platform.title, - description: t.apis.platform.description, - url: '/apis/platform-api', - }, - { - text: t.apis.ordinals.title, - description: t.apis.ordinals.description, - url: '/apis/ordinals-api', - }, - { - text: t.apis.runes.title, - description: t.apis.runes.description, - url: '/apis/runes-api', - }, - { - text: t.apis.signerMetrics.title, - description: t.apis.signerMetrics.description, - url: '/apis/signer-metrics-api', - }, - ], - }, - // Libraries & SDKs menu - // Resources menu + return useMemo(() => { + if (!baseOptions.links) return []; + + const menuTitleMap = new Map([ + ['Tools', t.navigation.menus.tools], + ['APIs', t.navigation.menus.apis], + ['Libraries & SDKs', t.navigation.menus.libraries], + ['Resources', t.navigation.menus.resources], + ]); + + const itemTranslations = new Map< + string, { - type: 'menu' as const, - text: t.navigation.menus.resources, - items: [ - { - text: t.resources.guides.title, - description: t.resources.guides.description, - url: '/resources/guides', - }, - { - text: t.resources.snippets.title, - description: t.resources.snippets.description, - url: '/resources/snippets', - }, - { - text: t.resources.archive.title, - description: t.resources.archive.description, - url: '/resources/archive', - }, - ], - }, - ], - [t], - ); + title: string; + description: string; + } + >([ + ['/tools/chainhook', t.tools.chainhook], + ['/tools/contract-monitoring', t.tools.contractMonitoring], + ['/tools/bitcoin-indexer', t.tools.bitcoinIndexer], + ['/resources/guides/api-keys', t.apis.apiKeys], + ['/resources/guides/rate-limits', t.apis.rateLimits], + ['/apis/stacks-blockchain-api', t.apis.stacksApi], + ['/apis/stacks-node-rpc-api', t.apis.stacksNodeRpcApi], + ['/apis/token-metadata-api', t.apis.tokenMetadata], + ['/apis/chainhook-api', t.apis.chainhook], + ['/apis/platform-api', t.apis.platform], + ['/apis/ordinals-api', t.apis.ordinals], + ['/apis/runes-api', t.apis.runes], + ['/apis/signer-metrics-api', t.apis.signerMetrics], + ['/resources/guides', t.resources.guides], + ['/resources/snippets', t.resources.snippets], + ['/resources/archive', t.resources.archive], + ]); + + return baseOptions.links.map((link) => { + if (link.type !== 'menu') return link; + + const localizedText = + typeof link.text === 'string' ? menuTitleMap.get(link.text) ?? link.text : link.text; + + const localizedItems = link.items?.map((item) => { + if (!('url' in item) || !item.url) return item; + + const localized = itemTranslations.get(item.url); + if (!localized) return item; + + return { + ...item, + text: localized.title, + description: localized.description, + }; + }); + + return { + ...link, + text: localizedText, + items: localizedItems, + }; + }); + }, [t]); } diff --git a/idioma.lock b/idioma.lock index c5280a2a3..532adef3f 100644 --- a/idioma.lock +++ b/idioma.lock @@ -5,17 +5,13 @@ files: translations: es: true content/docs/en/tools/contract-monitoring/index.mdx: - content: 0246b6000548051a90e626ee9b290d8c + content: c2f9ae2b76ffa1869fa47be00c0df949 translations: es: true content/docs/en/tools/contract-monitoring/create-alert.mdx: content: dbcf487653ba34bb5eaf3acdd3898591 translations: es: true - content/docs/en/tools/chainhook/index.mdx: - content: ece782d79e832d7d80502b0e0ea7df3e - translations: - es: true content/docs/en/tools/bitcoin-indexer/index.mdx: content: e0e6fae95b0cde6724695ea676a50993 translations: @@ -53,7 +49,7 @@ files: translations: es: true content/docs/en/resources/guides/index.mdx: - content: cc9153a7eca95fdf92d844b5d6123a5d + content: 05c06d205560a26e056daa3da10bdf12 translations: es: true content/docs/en/resources/guides/build-an-nft-marketplace.mdx: @@ -304,54 +300,6 @@ files: content: 11bacf870dcb86c65eb37cd0f7df05bc translations: es: true - content/docs/en/tools/chainhook/reference/stacks-scopes.mdx: - content: eddcd9f8e69860dc30c3909858c332fa - translations: - es: true - content/docs/en/tools/chainhook/reference/bitcoin-scopes.mdx: - content: f0a4f2e9378ce1b9b62bb36a5fcb379a - translations: - es: true - content/docs/en/tools/chainhook/(chainhook-cli)/service-mode.mdx: - content: b03cca5682cf363ac68e68d8e0695696 - translations: - es: true - content/docs/en/tools/chainhook/(chainhook-cli)/event-scanning.mdx: - content: f7cab8dd10c07d9ff78fe2317022cfec - translations: - es: true - content/docs/en/tools/chainhook/(chainhook-cli)/cli-reference.mdx: - content: e957e8658bf1aa88f5b4530fc7d078bd - translations: - es: true - content/docs/en/tools/chainhook/(overview)/usage.mdx: - content: 2509818906e70fda0eab035e2e11a043 - translations: - es: true - content/docs/en/tools/chainhook/(overview)/quickstart.mdx: - content: dde5949e260e47c2aca6188688727354 - translations: - es: true - content/docs/en/tools/chainhook/(integrations)/register-chainhooks-on-devnet.mdx: - content: 4adab0c4cb5a5a24a63171937ea0a999 - translations: - es: true - content/docs/en/tools/chainhook/(event-handling)/webhook-setup.mdx: - content: 256241d98346a7b19b2f518f7b2a55dc - translations: - es: true - content/docs/en/tools/chainhook/(event-handling)/payload-handling.mdx: - content: 9b02be5964017a7314421ca2b451e39e - translations: - es: true - content/docs/en/tools/chainhook/(event-handling)/example-indexers.mdx: - content: 0f97f6c4f94ce17f941c7cc4019a5ef2 - translations: - es: true - content/docs/en/tools/chainhook/(event-handling)/custom-indexer.mdx: - content: a39c099d151c014bcfb76834870c98a5 - translations: - es: true content/docs/en/tools/bitcoin-indexer/(indexer)/node-installation.mdx: content: 94fae442ea20991dfeac5649dc4e2ce1 translations: @@ -361,7 +309,7 @@ files: translations: es: true content/docs/en/tools/bitcoin-indexer/(indexer)/full-sync.mdx: - content: f783285d58eeca56467ace17753c2a69 + content: 1b2d61c0e243295c2312a87328389cac translations: es: true content/docs/en/tools/bitcoin-indexer/(indexer)/configuration.mdx: @@ -1141,7 +1089,7 @@ files: translations: es: true content/docs/en/resources/guides/using-pyth-price-feeds.mdx: - content: b6fedf15336c0fa9bafa73b73ef944c1 + content: a39ffa2cccb0a8b515796a0d3bc3a25e translations: es: true content/docs/en/tools/clarinet/(overview)/quickstart.mdx: @@ -1325,6 +1273,138 @@ files: translations: es: true content/docs/en/resources/archive/download-guide.mdx: - content: 25c13ed29bef0e98811ac88d7935689b + content: 830654f26dc77cad2d5a56a7ba9aa849 + translations: + es: true + content/docs/en/tools/chainhook/index.mdx: + content: a3b5d553d27a4b0b5165493834d651dd + translations: + es: true + content/docs/en/apis/chainhook-api/usage.mdx: + content: 874f2946f0eca159b8b51d5ad4462e82 + translations: + es: true + content/docs/en/apis/chainhook-api/index.mdx: + content: 79b4de59ff6ecd996b0188e9a0349bcb + translations: + es: true + content/docs/en/tools/chainhook/reference/payload-anatomy.mdx: + content: 7e9a1ee0962e14246ab73cdcecd04bf3 + translations: + es: true + content/docs/en/tools/chainhook/reference/options.mdx: + content: 829b730a8218f88c859d3806cb70e415 + translations: + es: true + content/docs/en/tools/chainhook/reference/filters.mdx: + content: 463e7e09482d76d8d8a167356dd596d1 + translations: + es: true + content/docs/en/tools/chainhook/(platform-usage)/view-chainhooks.mdx: + content: 881a49bed2b09c9e31d1158a89c9d074 + translations: + es: true + content/docs/en/tools/chainhook/(platform-usage)/platform-usage.mdx: + content: d3aa2bbc6d52e9befdf6f330a86fac8b + translations: + es: true + content/docs/en/tools/chainhook/(platform-usage)/platform-quickstart.mdx: + content: d1b82394672a71836d0c87c2c43236eb + translations: + es: true + content/docs/en/tools/chainhook/(platform-usage)/manage-api-keys.mdx: + content: f1bc3ebaacdc75c667e096cf86ea6975 + translations: + es: true + content/docs/en/tools/chainhook/(platform-usage)/create-enable-chainhooks.mdx: + content: 2e3573b59e73a3d96d8f4f38257d89c6 + translations: + es: true + content/docs/en/tools/chainhook/(overview)/migration.mdx: + content: 8cfd38ef16dea630e041140d7c6c6b4f + translations: + es: true + content/docs/en/tools/chainhook/(overview)/faq.mdx: + content: 93f6fb71e4887d95524ed029da144726 + translations: + es: true + content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx: + content: b861f167b666720118ac005efe9b6e33 + translations: + es: true + content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx: + content: b960ab63639590bdb4540047c2797314 + translations: + es: true + content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx: + content: ff642f5bbe3cc936dec794be10cadbd3 + translations: + es: true + content/docs/en/tools/chainhook/(chainhook-sdk)/introduction.mdx: + content: d4ffc20faa1b46a90c207b8426d23d7e + translations: + es: true + content/docs/en/tools/chainhook/(chainhook-sdk)/evaluate.mdx: + content: 4d521a416e9d3781e90148842da2d5c8 + translations: + es: true + content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx: + content: 8c334f53e455abe0c5f9e565c01ba8b6 + translations: + es: true + content/docs/en/apis/chainhook-api/reference/info/status.mdx: + content: 0a5769e1d1ab9ebfc85b0b9e394a2d5f + translations: + es: true + content/docs/en/apis/chainhook-api/reference/info/index.mdx: + content: 9bd2cbf38a0954ef2050725a2702be71 + translations: + es: true + content/docs/en/apis/chainhook-api/reference/secrets/rotate-consumer-secret.mdx: + content: 3318dbeaac1826727ba7e558de2ca6e3 + translations: + es: true + content/docs/en/apis/chainhook-api/reference/secrets/index.mdx: + content: bf6bfdf7cd696535836ea9a6455f7123 + translations: + es: true + content/docs/en/apis/chainhook-api/reference/secrets/delete-consumer-secret.mdx: + content: dd30629de045d14c23634ecb1babd7a3 + translations: + es: true + content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook.mdx: + content: acbf559fb562ac4621e60174fca84f21 + translations: + es: true + content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook-enabled.mdx: + content: ed34022de212168d18e1b754be0df7de + translations: + es: true + content/docs/en/apis/chainhook-api/reference/chainhooks/register-chainhook.mdx: + content: d24f38b45ed23c68257059464da97e74 + translations: + es: true + content/docs/en/apis/chainhook-api/reference/chainhooks/index.mdx: + content: feedad04052a1c25c0a80ba7f2a29445 + translations: + es: true + content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhooks.mdx: + content: b9a51ee7aacfe302e175242a3d2250a2 + translations: + es: true + content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhook.mdx: + content: 705e66075357bceecdfda43610ff24fc + translations: + es: true + content/docs/en/apis/chainhook-api/reference/chainhooks/evaluate-chainhook.mdx: + content: 49e2de023b9933082fec7968c7afbd8e + translations: + es: true + content/docs/en/apis/chainhook-api/reference/chainhooks/delete-chainhook.mdx: + content: cc5f9a34fb8ba69f6558f28518dc2119 + translations: + es: true + content/docs/en/apis/chainhook-api/reference/chainhooks/bulk-enable-chainhooks.mdx: + content: 170efd0c995ccff40d41c36a9d237b93 translations: es: true diff --git a/lib/api-config.ts b/lib/api-config.ts index db7fb6d6a..5d53c2814 100644 --- a/lib/api-config.ts +++ b/lib/api-config.ts @@ -10,6 +10,19 @@ export const apiConfig = { }, }, + // Chainhook API endpoints (use spec-provided servers) + chainhook: { + clarityConversion: false, + enablePlayground: true, + credentialId: 'chainhook', + credentialPublicOperations: [ + { method: 'GET', path: '/' }, + ], + playgroundOptions: { + proxyUrl: '/api/proxy', + }, + }, + // Platform API endpoints platform: { baseUrl: 'https://platform.hiro.so', @@ -43,21 +56,37 @@ export const apiConfig = { // Helper to get config based on document path export function getAPIConfig(documentPath: string) { - // RPC node endpoints - if (documentPath.includes('stacks-node-rpc-api.json')) { - return apiConfig.rpcNode; + const normalizedPath = documentPath.toLowerCase(); + + const cloneConfig = (key: T) => { + const config = apiConfig[key]; + return { + ...config, + playgroundOptions: config.playgroundOptions + ? { ...config.playgroundOptions } + : undefined, + }; + }; + + if (normalizedPath.includes('stacks-node-rpc-api.json')) { + return cloneConfig('rpcNode'); + } + + if (normalizedPath.includes('platform-api.json') || normalizedPath.includes('/apis/platform/')) { + return cloneConfig('platform'); + } + + if (normalizedPath.includes('token-metadata')) { + return cloneConfig('tokenMetadata'); } - // Platform APIs - if (documentPath.includes('/apis/platform/')) { - return apiConfig.platform; + if (normalizedPath.includes('stacks-blockchain-api.json')) { + return cloneConfig('stacksBlockchain'); } - // Token metadata - if (documentPath.includes('token-metadata')) { - return apiConfig.tokenMetadata; + if (normalizedPath.includes('chainhook-api.json')) { + return cloneConfig('chainhook'); } - // Default to Stacks Blockchain API - return apiConfig.stacksBlockchain; + return undefined; } diff --git a/lib/markdown-generator.ts b/lib/markdown-generator.ts index a28eb6b4c..8a80f3da4 100644 --- a/lib/markdown-generator.ts +++ b/lib/markdown-generator.ts @@ -80,7 +80,7 @@ export class ReferenceMarkdownGenerator { const sections: string[] = []; // Using Accordion JSX syntax - sections.push(``); + sections.push(``); sections.push(` `); sections.push(` ${method.name}`); sections.push(` `); @@ -271,7 +271,7 @@ bun add ${packageName} }); if (namespaceFunctions.length > 0) { - sections.push(``); + sections.push(``); namespaceFunctions.forEach((func) => { // Skip the namespace itself diff --git a/lib/translations/en.ts b/lib/translations/en.ts index ce6225a1b..ab155c08a 100644 --- a/lib/translations/en.ts +++ b/lib/translations/en.ts @@ -95,6 +95,10 @@ export const en: Translations = { title: 'Token Metadata API', description: 'API for retrieving NFT and fungible token metadata.', }, + chainhook: { + title: 'Chainhook API', + description: 'RESTful API for accessing Chainhook.', + }, platform: { title: 'Platform API', description: 'API for accessing Hiro Platform data and functionality.', diff --git a/lib/translations/es.ts b/lib/translations/es.ts index 12f85ff80..4d2ba20c0 100644 --- a/lib/translations/es.ts +++ b/lib/translations/es.ts @@ -95,6 +95,10 @@ export const es: Translations = { title: 'API de Metadatos de Tokens', description: 'API para obtener metadatos de tokens NFT y fungibles.', }, + chainhook: { + title: 'API de Chainhook', + description: 'API RESTful para acceder a Chainhook.', + }, platform: { title: 'API de Plataforma', description: 'API para acceder a datos y funcionalidad de la Plataforma Hiro.', diff --git a/lib/translations/types.ts b/lib/translations/types.ts index 1a5aaed30..42c61c279 100644 --- a/lib/translations/types.ts +++ b/lib/translations/types.ts @@ -88,6 +88,10 @@ export interface Translations { title: string; description: string; }; + chainhook: { + title: string; + description: string; + }; tokenMetadata: { title: string; description: string; diff --git a/providers/api-credentials-provider.tsx b/providers/api-credentials-provider.tsx new file mode 100644 index 000000000..1f305042d --- /dev/null +++ b/providers/api-credentials-provider.tsx @@ -0,0 +1,53 @@ +'use client'; + +import type { ReactNode } from 'react'; +import { createContext, useContext, useMemo, useState } from 'react'; + +type CredentialMap = Record; + +type ApiCredentialsContextValue = { + getCredential: (id: string) => string; + setCredential: (id: string, value: string) => void; + clearCredential: (id: string) => void; +}; + +const ApiCredentialsContext = createContext(undefined); + +export function ApiCredentialsProvider({ children }: { children: ReactNode }) { + const [credentials, setCredentials] = useState({}); + + const value = useMemo( + () => ({ + getCredential: (id) => credentials[id] ?? '', + setCredential: (id, value) => { + setCredentials((prev) => { + if (!value.trim()) { + const updated = { ...prev }; + delete updated[id]; + return updated; + } + return { ...prev, [id]: value.trim() }; + }); + }, + clearCredential: (id) => { + setCredentials((prev) => { + if (!(id in prev)) return prev; + const updated = { ...prev }; + delete updated[id]; + return updated; + }); + }, + }), + [credentials], + ); + + return {children}; +} + +export function useApiCredentials() { + const context = useContext(ApiCredentialsContext); + if (!context) { + throw new Error('useApiCredentials must be used within an ApiCredentialsProvider'); + } + return context; +} diff --git a/scripts/fetch-openapi-specs.mts b/scripts/fetch-openapi-specs.mts index da4b46862..18de18227 100644 --- a/scripts/fetch-openapi-specs.mts +++ b/scripts/fetch-openapi-specs.mts @@ -43,6 +43,10 @@ const API_SPECS: ApiSpec[] = [ name: 'runes', url: 'https://runes-api.vercel.app/openapi.json', }, + { + name: 'chainhook', + url: 'https://chainhooks-api.vercel.app/openapi.json', + }, ]; const GITHUB_API_SPECS: GitHubApiSpec[] = [