From ba442602bbb2c3c526127c37411546f58efb9a18 Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Wed, 5 Nov 2025 15:23:05 -0600 Subject: [PATCH 01/16] initial v2 migration --- content/docs/en/apis/chainhook-api/index.mdx | 99 +++ content/docs/en/apis/chainhook-api/meta.json | 5 + .../reference/bulk-enable-chainhooks.mdx | 12 + .../reference/delete-chainhook.mdx | 12 + .../reference/delete-consumer-secret.mdx | 12 + .../reference/evaluate-chainhook.mdx | 12 + .../chainhook-api/reference/get-chainhook.mdx | 12 + .../reference/get-chainhooks.mdx | 12 + .../en/apis/chainhook-api/reference/meta.json | 16 + .../reference/register-chainhook.mdx | 12 + .../reference/rotate-consumer-secret.mdx | 12 + .../apis/chainhook-api/reference/status.mdx | 12 + .../reference/update-chainhook-enabled.mdx | 12 + .../reference/update-chainhook.mdx | 12 + content/docs/en/apis/chainhook-api/usage.mdx | 287 ++++++++ .../(chainhook-cli)/cli-reference.mdx | 209 ------ .../(chainhook-cli)/event-scanning.mdx | 202 ------ .../tools/chainhook/(chainhook-cli)/meta.json | 4 - .../(chainhook-cli)/service-mode.mdx | 152 ---- .../chainhook/(chainhook-sdk)/edit-update.mdx | 502 +++++++++++++ .../chainhook/(chainhook-sdk)/evaluate.mdx | 435 ++++++++++++ .../tools/chainhook/(chainhook-sdk)/index.mdx | 159 +++++ .../chainhook/(chainhook-sdk)/list-fetch.mdx | 346 +++++++++ .../chainhook/(chainhook-sdk)/manage-keys.mdx | 489 +++++++++++++ .../tools/chainhook/(chainhook-sdk)/meta.json | 11 + .../chainhook/(chainhook-sdk)/quickstart.mdx | 275 +++++++ .../(chainhook-sdk)/register-enable.mdx | 341 +++++++++ .../(event-handling)/custom-indexer.mdx | 6 - .../(event-handling)/example-indexers.mdx | 6 - .../chainhook/(event-handling)/meta.json | 4 - .../(event-handling)/payload-handling.mdx | 6 - .../(event-handling)/webhook-setup.mdx | 6 - .../tools/chainhook/(integrations)/meta.json | 4 - .../register-chainhooks-on-devnet.mdx | 123 ---- .../en/tools/chainhook/(overview)/faq.mdx | 252 +++++++ .../en/tools/chainhook/(overview)/meta.json | 7 +- .../tools/chainhook/(overview)/migration.mdx | 668 ++++++++++++++++++ .../tools/chainhook/(overview)/quickstart.mdx | 100 --- .../en/tools/chainhook/(overview)/usage.mdx | 443 +++++++----- .../chainhook/(platform-usage)/index.mdx | 5 + .../chainhook/(platform-usage)/list-fetch.mdx | 5 + .../(platform-usage)/manage-keys.mdx | 5 + .../chainhook/(platform-usage)/meta.json | 9 + .../chainhook/(platform-usage)/quickstart.mdx | 5 + .../(platform-usage)/register-enable.mdx | 5 + content/docs/en/tools/chainhook/index.mdx | 55 +- content/docs/en/tools/chainhook/meta.json | 8 +- .../en/tools/chainhook/reference/filters.mdx | 353 +++++++++ .../en/tools/chainhook/reference/meta.json | 7 + .../en/tools/chainhook/reference/options.mdx | 280 ++++++++ .../chainhook/reference/payload-anatomy.mdx | 488 +++++++++++++ .../(chainhook-cli)/cli-reference.mdx | 208 ------ .../(chainhook-cli)/event-scanning.mdx | 201 ------ .../(chainhook-cli)/service-mode.mdx | 151 ---- .../(event-handling)/custom-indexer.mdx | 5 - .../(event-handling)/example-indexers.mdx | 5 - .../(event-handling)/payload-handling.mdx | 5 - .../(event-handling)/webhook-setup.mdx | 5 - .../register-chainhooks-on-devnet.mdx | 122 ---- scripts/fetch-openapi-specs.mts | 4 + 60 files changed, 5472 insertions(+), 1748 deletions(-) create mode 100644 content/docs/en/apis/chainhook-api/index.mdx create mode 100644 content/docs/en/apis/chainhook-api/meta.json create mode 100644 content/docs/en/apis/chainhook-api/reference/bulk-enable-chainhooks.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/delete-chainhook.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/delete-consumer-secret.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/evaluate-chainhook.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/get-chainhook.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/get-chainhooks.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/meta.json create mode 100644 content/docs/en/apis/chainhook-api/reference/register-chainhook.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/rotate-consumer-secret.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/status.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/update-chainhook-enabled.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/update-chainhook.mdx create mode 100644 content/docs/en/apis/chainhook-api/usage.mdx delete mode 100644 content/docs/en/tools/chainhook/(chainhook-cli)/cli-reference.mdx delete mode 100644 content/docs/en/tools/chainhook/(chainhook-cli)/event-scanning.mdx delete mode 100644 content/docs/en/tools/chainhook/(chainhook-cli)/meta.json delete mode 100644 content/docs/en/tools/chainhook/(chainhook-cli)/service-mode.mdx create mode 100644 content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx create mode 100644 content/docs/en/tools/chainhook/(chainhook-sdk)/evaluate.mdx create mode 100644 content/docs/en/tools/chainhook/(chainhook-sdk)/index.mdx create mode 100644 content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx create mode 100644 content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx create mode 100644 content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json create mode 100644 content/docs/en/tools/chainhook/(chainhook-sdk)/quickstart.mdx create mode 100644 content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx delete mode 100644 content/docs/en/tools/chainhook/(event-handling)/custom-indexer.mdx delete mode 100644 content/docs/en/tools/chainhook/(event-handling)/example-indexers.mdx delete mode 100644 content/docs/en/tools/chainhook/(event-handling)/meta.json delete mode 100644 content/docs/en/tools/chainhook/(event-handling)/payload-handling.mdx delete mode 100644 content/docs/en/tools/chainhook/(event-handling)/webhook-setup.mdx delete mode 100644 content/docs/en/tools/chainhook/(integrations)/meta.json delete mode 100644 content/docs/en/tools/chainhook/(integrations)/register-chainhooks-on-devnet.mdx create mode 100644 content/docs/en/tools/chainhook/(overview)/faq.mdx create mode 100644 content/docs/en/tools/chainhook/(overview)/migration.mdx delete mode 100644 content/docs/en/tools/chainhook/(overview)/quickstart.mdx create mode 100644 content/docs/en/tools/chainhook/(platform-usage)/index.mdx create mode 100644 content/docs/en/tools/chainhook/(platform-usage)/list-fetch.mdx create mode 100644 content/docs/en/tools/chainhook/(platform-usage)/manage-keys.mdx create mode 100644 content/docs/en/tools/chainhook/(platform-usage)/meta.json create mode 100644 content/docs/en/tools/chainhook/(platform-usage)/quickstart.mdx create mode 100644 content/docs/en/tools/chainhook/(platform-usage)/register-enable.mdx create mode 100644 content/docs/en/tools/chainhook/reference/filters.mdx create mode 100644 content/docs/en/tools/chainhook/reference/meta.json create mode 100644 content/docs/en/tools/chainhook/reference/options.mdx create mode 100644 content/docs/en/tools/chainhook/reference/payload-anatomy.mdx delete mode 100644 content/docs/es/tools/chainhook/(chainhook-cli)/cli-reference.mdx delete mode 100644 content/docs/es/tools/chainhook/(chainhook-cli)/event-scanning.mdx delete mode 100644 content/docs/es/tools/chainhook/(chainhook-cli)/service-mode.mdx delete mode 100644 content/docs/es/tools/chainhook/(event-handling)/custom-indexer.mdx delete mode 100644 content/docs/es/tools/chainhook/(event-handling)/example-indexers.mdx delete mode 100644 content/docs/es/tools/chainhook/(event-handling)/payload-handling.mdx delete mode 100644 content/docs/es/tools/chainhook/(event-handling)/webhook-setup.mdx delete mode 100644 content/docs/es/tools/chainhook/(integrations)/register-chainhooks-on-devnet.mdx 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..0e0714e34 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/index.mdx @@ -0,0 +1,99 @@ +--- +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..34dd082bd --- /dev/null +++ b/content/docs/en/apis/chainhook-api/meta.json @@ -0,0 +1,5 @@ +{ + "title": "Chainhook API", + "root": true, + "pages": ["index", "usage", "...reference"] +} diff --git a/content/docs/en/apis/chainhook-api/reference/bulk-enable-chainhooks.mdx b/content/docs/en/apis/chainhook-api/reference/bulk-enable-chainhooks.mdx new file mode 100644 index 000000000..543e9d95b --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/bulk-enable-chainhooks.mdx @@ -0,0 +1,12 @@ +--- +title: Bulk enable or disable chainhooks +sidebarTitle: Patch +description: Updates the enabled status of chainhooks that match the provided filters +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/delete-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/delete-chainhook.mdx new file mode 100644 index 000000000..556565c69 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/delete-chainhook.mdx @@ -0,0 +1,12 @@ +--- +title: Delete a chainhook +sidebarTitle: Delete +description: Deletes a chainhook by its UUID +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/delete-consumer-secret.mdx b/content/docs/en/apis/chainhook-api/reference/delete-consumer-secret.mdx new file mode 100644 index 000000000..1e06c01f0 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/delete-consumer-secret.mdx @@ -0,0 +1,12 @@ +--- +title: Delete consumer secret +sidebarTitle: Delete +description: Deletes the Chainhooks event payload consumer secret +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/evaluate-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/evaluate-chainhook.mdx new file mode 100644 index 000000000..c4de59e62 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/evaluate-chainhook.mdx @@ -0,0 +1,12 @@ +--- +title: Evaluate a chainhook +sidebarTitle: Post +description: Queues an on-demand evaluation job for a specific block +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/get-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/get-chainhook.mdx new file mode 100644 index 000000000..a81b3beee --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/get-chainhook.mdx @@ -0,0 +1,12 @@ +--- +title: Get a specific chainhook +sidebarTitle: Get +description: Returns a chainhook by its UUID +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/get-chainhooks.mdx b/content/docs/en/apis/chainhook-api/reference/get-chainhooks.mdx new file mode 100644 index 000000000..b4536faab --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/get-chainhooks.mdx @@ -0,0 +1,12 @@ +--- +title: Get all chainhooks +sidebarTitle: Get +description: Returns all chainhooks registered by the current user +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/meta.json b/content/docs/en/apis/chainhook-api/reference/meta.json new file mode 100644 index 000000000..7f60b985b --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/meta.json @@ -0,0 +1,16 @@ +{ + "title": "API Reference", + "pages": [ + "status", + "register-chainhook", + "get-chainhooks", + "get-chainhook", + "update-chainhook", + "delete-chainhook", + "evaluate-chainhook", + "update-chainhook-enabled", + "bulk-enable-chainhooks", + "rotate-consumer-secret", + "delete-consumer-secret" + ] +} diff --git a/content/docs/en/apis/chainhook-api/reference/register-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/register-chainhook.mdx new file mode 100644 index 000000000..c579cc7fe --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/register-chainhook.mdx @@ -0,0 +1,12 @@ +--- +title: Register a chainhook +sidebarTitle: Post +description: Allows users to register a new Chainhook +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/rotate-consumer-secret.mdx b/content/docs/en/apis/chainhook-api/reference/rotate-consumer-secret.mdx new file mode 100644 index 000000000..5b3b8b025 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/rotate-consumer-secret.mdx @@ -0,0 +1,12 @@ +--- +title: Rotate consumer secret +sidebarTitle: Post +description: Generates and returns a new Chainhooks event payload consumer secret +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/status.mdx b/content/docs/en/apis/chainhook-api/reference/status.mdx new file mode 100644 index 000000000..690b71997 --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/status.mdx @@ -0,0 +1,12 @@ +--- +title: API Status +sidebarTitle: Get +description: Displays the status of the API and its current workload +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/update-chainhook-enabled.mdx b/content/docs/en/apis/chainhook-api/reference/update-chainhook-enabled.mdx new file mode 100644 index 000000000..9d97096bd --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/update-chainhook-enabled.mdx @@ -0,0 +1,12 @@ +--- +title: Enable or disable a chainhook +sidebarTitle: Patch +description: Changes the enabled status of a chainhook by its UUID +full: true +--- + + diff --git a/content/docs/en/apis/chainhook-api/reference/update-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/update-chainhook.mdx new file mode 100644 index 000000000..c579aad9b --- /dev/null +++ b/content/docs/en/apis/chainhook-api/reference/update-chainhook.mdx @@ -0,0 +1,12 @@ +--- +title: Update a chainhook +sidebarTitle: Patch +description: Updates a chainhook by its UUID +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/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..ffe918503 --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx @@ -0,0 +1,502 @@ +--- +title: Edit & Update +description: Modify existing chainhooks using the SDK +--- + +# Edit & Update + +Learn how to update existing chainhooks using the Chainhook SDK. Updates allow you to modify filters, actions, and options without recreating the chainhook. + +:::callout{type="info"} +**Platform Limitation**: The Platform UI does not currently support editing chainhooks. You must use the SDK or API to make updates. +::: + +## updateChainhook + +Updates an existing chainhook by UUID. You can modify any mutable fields, but `chain` and `network` cannot be changed. + +### Basic Update + +```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', + }, + ], + }, +}); +``` + +### Immutable vs Mutable Fields + +| Mutable (Can Update) | Immutable (Cannot Update) | +|---------------------|---------------------------| +| `name` | `chain` | +| `filters` | `network` | +| `action` | | +| `options` | | + +--- + +## Additive Updates + +For additive changes (like adding a new event filter), fetch the current definition first: + +### Adding Event Filters + +```typescript +// Step 1: Fetch current chainhook +const current = await client.getChainhook('chainhook-uuid'); + +// Step 2: Spread existing events and add new one +await client.updateChainhook('chainhook-uuid', { + filters: { + events: [ + ...(current.definition.filters.events ?? []), + { + type: 'contract_call', + contract_identifier: 'SP...XYZ.counter', + function_name: 'decrement', + }, + ], + }, +}); +``` + +This preserves existing filters while adding new ones. + +### Adding Options + +```typescript +// Fetch current chainhook +const current = await client.getChainhook('chainhook-uuid'); + +// Spread existing options and add/override +await client.updateChainhook('chainhook-uuid', { + options: { + ...current.definition.options, + decode_clarity_values: true, + include_contract_abi: true, + }, +}); +``` + +--- + +## Common Update Patterns + +### Update Webhook URL + +Change where chainhook events are sent: + +```typescript +await client.updateChainhook('chainhook-uuid', { + action: { + type: 'http_post', + url: 'https://new-server.com/webhooks', + }, +}); +``` + +### Update Chainhook Name + +```typescript +await client.updateChainhook('chainhook-uuid', { + name: 'production-ft-tracker', +}); +``` + +### Replace All Filters + +Completely replace the filter configuration: + +```typescript +await client.updateChainhook('chainhook-uuid', { + filters: { + events: [ + { + type: 'nft_transfer', + asset_identifier: 'SP...COLL.nft::collectible', + }, + ], + }, +}); +``` + +### 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, + include_block_metadata: true, + }, +}); +``` + +--- + +## cURL Examples + +### Update Name + +```bash +curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ + "name": "Updated chainhook name" + }' +``` + +### Update Filters + +```bash +curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ + "filters": { + "events": [ + { + "type": "ft_transfer", + "asset_identifier": "SP...ABC.token::usdc" + } + ] + } + }' +``` + +### Update Action URL + +```bash +curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ + "action": { + "type": "http_post", + "url": "https://new-server.com/webhooks" + } + }' +``` + +### Update Options + +```bash +curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ + "options": { + "decode_clarity_values": true, + "include_contract_abi": true + } + }' +``` + +--- + +## Response + +Successful updates return HTTP `204 No Content`. + +To verify the update, fetch the chainhook: + +```typescript +await client.updateChainhook('chainhook-uuid', { name: 'New name' }); + +// Verify +const updated = await client.getChainhook('chainhook-uuid'); +console.log('Updated name:', updated.definition.name); +``` + +--- + +## deleteChainhook + +Remove a chainhook permanently. This cannot be undone. + +### TypeScript + +```typescript +await client.deleteChainhook('chainhook-uuid'); + +console.log('Chainhook deleted'); +``` + +### cURL + +```bash +curl -sS -X DELETE "https://api.testnet.hiro.so/chainhooks/me/" \ + -H "x-api-key: $HIRO_API_KEY" +``` + +### Response + +Returns HTTP `204 No Content` on success. + +--- + +## Best Practices + +### 1. Fetch Before Additive Updates + +Always fetch the current definition for additive updates: + +```typescript +// ✅ Good: Fetch first for additive updates +const current = await client.getChainhook(uuid); +await client.updateChainhook(uuid, { + filters: { + events: [...current.definition.filters.events, newEvent], + }, +}); + +// ❌ Bad: Might overwrite existing filters +await client.updateChainhook(uuid, { + filters: { + events: [newEvent], + }, +}); +``` + +### 2. Validate Updates + +Verify updates were applied: + +```typescript +await client.updateChainhook(uuid, { name: 'New name' }); + +const updated = await client.getChainhook(uuid); +if (updated.definition.name !== 'New name') { + throw new Error('Update failed'); +} +``` + +### 3. Disable Before Major Updates + +Disable the chainhook before making significant changes: + +```typescript +// Disable while updating +await client.enableChainhook(uuid, false); + +// Make updates +await client.updateChainhook(uuid, { + filters: { /* new filters */ }, + action: { /* new action */ }, +}); + +// Re-enable +await client.enableChainhook(uuid, true); +``` + +### 4. Use Descriptive Names + +Update names to reflect the chainhook's purpose: + +```typescript +await client.updateChainhook(uuid, { + name: `${environment}-${feature}-${version}`, +}); +// Example: "production-ft-tracker-v2" +``` + +### 5. Document Changes + +Store update history in your database: + +```typescript +await client.updateChainhook(uuid, { /* updates */ }); + +// Log the change +await db.chainhookUpdates.create({ + chainhookUuid: uuid, + timestamp: new Date(), + changes: { /* what changed */ }, + updatedBy: userId, +}); +``` + +--- + +## Error Handling + +### Chainhook Not Found + +```typescript +try { + await client.updateChainhook('invalid-uuid', { name: 'New name' }); +} catch (error) { + if (error.status === 404) { + console.error('Chainhook not found'); + } +} +``` + +### Invalid Update + +```typescript +try { + await client.updateChainhook(uuid, { + chain: 'bitcoin', // Immutable field + }); +} catch (error) { + if (error.status === 400) { + console.error('Invalid update:', error.message); + } +} +``` + +--- + +## Migration Example + +Migrate all chainhooks from one webhook URL to another: + +```typescript +async function migrateWebhookUrl(oldUrl: string, newUrl: string) { + // Get all chainhooks + const allChainhooks = await client.getChainhooks({ limit: 100 }); + + // Filter by old URL + const toMigrate = allChainhooks.results.filter( + ch => ch.definition.action.url === oldUrl + ); + + console.log(`Migrating ${toMigrate.length} chainhooks...`); + + // Update each one + for (const ch of toMigrate) { + await client.updateChainhook(ch.uuid, { + action: { + type: 'http_post', + url: newUrl, + }, + }); + console.log(`✅ Migrated: ${ch.definition.name}`); + } + + console.log('Migration complete!'); +} + +await migrateWebhookUrl( + 'https://old-server.com/webhooks', + 'https://new-server.com/webhooks' +); +``` + +### Safer Migration with Bulk Operations + +For safer migrations, use `bulkEnableChainhooks` to disable chainhooks during updates, then re-enable them: + +```typescript +async function safeWebhookMigration(oldUrl: string, newUrl: string) { + // Step 1: Disable all chainhooks for the old URL + console.log('Disabling chainhooks...'); + await client.bulkEnableChainhooks({ + enabled: false, + filters: { + webhook_url: oldUrl, + }, + }); + + // Step 2: Update webhook URLs + console.log('Updating webhook URLs...'); + const allChainhooks = await client.getChainhooks({ limit: 100 }); + const toMigrate = allChainhooks.results.filter( + ch => ch.definition.action.url === oldUrl + ); + + for (const ch of toMigrate) { + await client.updateChainhook(ch.uuid, { + action: { + type: 'http_post', + url: newUrl, + }, + }); + console.log(`✅ Updated: ${ch.definition.name}`); + } + + // Step 3: Re-enable all chainhooks with new URL + console.log('Re-enabling chainhooks...'); + await client.bulkEnableChainhooks({ + enabled: true, + filters: { + webhook_url: newUrl, + }, + }); + + console.log('Migration complete!'); +} + +await safeWebhookMigration( + 'https://old-server.com/webhooks', + 'https://new-server.com/webhooks' +); +``` + +This approach prevents chainhooks from firing during the migration window, avoiding potential errors or duplicate events. + +### Bulk Operations for Maintenance + +Use bulk enable/disable for maintenance windows: + +```typescript +// Disable all chainhooks during server maintenance +await client.bulkEnableChainhooks({ + enabled: false, + filters: { + webhook_url: 'https://api.myapp.com/webhooks', + }, +}); + +// Perform maintenance... +console.log('Server maintenance in progress...'); + +// Re-enable after maintenance +await client.bulkEnableChainhooks({ + enabled: true, + filters: { + webhook_url: 'https://api.myapp.com/webhooks', + }, +}); +``` + +:::callout{type="info"} +Learn more about bulk operations in the [Register & Enable guide](/tools/chainhook/register-enable#bulkenablechainhooks). +::: + +--- + +## Next Steps + +:::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..0836fdb3c --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/evaluate.mdx @@ -0,0 +1,435 @@ +--- +title: Evaluate Chainhook +description: Test chainhooks against specific past blocks +--- + +# Evaluate Chainhook + +The `evaluateChainhook` method allows you to test your chainhook configuration against specific past blocks. This is useful for testing, debugging, and historical indexing. + +## evaluateChainhook + +Evaluate a chainhook against a specific block by height or hash. If the chainhook's filters match any events in the specified block, a webhook payload will be sent to your configured endpoint. + +### By Block Height + +```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.evaluateChainhook('chainhook-uuid', { + block_height: 100000, +}); +``` + +### By Block Hash + +```typescript +await client.evaluateChainhook('chainhook-uuid', { + index_block_hash: '0xa204da7ae59b86a569d66c237721937e1708b72913c6a67abf58360b8f5935b7', +}); +``` + +### Response + +Returns HTTP `204 No Content` on success. If the chainhook filter matches any events in the specified block, a webhook payload will be sent to your configured `action.url`. + +--- + +## cURL Examples + +### Evaluate by Height + +```bash +curl -sS -X POST "https://api.testnet.hiro.so/chainhooks/me//evaluate" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ + "block_height": 100000 + }' +``` + +### Evaluate by Hash + +```bash +curl -sS -X POST "https://api.testnet.hiro.so/chainhooks/me//evaluate" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ + "index_block_hash": "0xa204da7ae59b86a569d66c237721937e1708b72913c6a67abf58360b8f5935b7" + }' +``` + +--- + +## Use Cases + +### 1. Test Chainhook Configuration + +Verify your chainhook works against a known block before enabling it: + +```typescript +// Create chainhook (disabled) +const chainhook = await client.registerChainhook({ + version: '1', + name: 'test-chainhook', + chain: 'stacks', + network: 'testnet', + filters: { + events: [ + { + type: 'ft_transfer', + asset_identifier: 'SP...ABC.token::usdc', + }, + ], + }, + action: { + type: 'http_post', + url: 'https://example.com/webhooks', + }, + // No enable_on_registration +}); + +// Test against a block with known FT transfers +await client.evaluateChainhook(chainhook.uuid, { + block_height: 150000, +}); + +// Check your webhook endpoint for the payload +// If it looks good, enable the chainhook +await client.enableChainhook(chainhook.uuid, true); +``` + +### 2. Historical Indexing + +Process past blocks to build up historical state: + +```typescript +async function indexHistoricalBlocks( + chainhookUuid: string, + startHeight: number, + endHeight: number +) { + console.log(`Indexing blocks ${startHeight} to ${endHeight}...`); + + for (let height = startHeight; height <= endHeight; height++) { + await client.evaluateChainhook(chainhookUuid, { + block_height: height, + }); + + // Log progress every 100 blocks + if (height % 100 === 0) { + console.log(`Processed ${height - startHeight + 1} blocks...`); + } + + // Add delay to avoid rate limits + await new Promise(resolve => setTimeout(resolve, 100)); + } + + console.log('Indexing complete!'); +} + +// Index 1000 blocks +await indexHistoricalBlocks('chainhook-uuid', 100000, 101000); +``` + +### 3. Debug Missed Events + +If you think your chainhook missed an event, evaluate against that specific block: + +```typescript +// User reports missing event at block 155000 +await client.evaluateChainhook('chainhook-uuid', { + block_height: 155000, +}); + +// Check your webhook logs to see if it triggers now +``` + +### 4. Re-process Specific Block + +Re-evaluate a block after fixing your webhook handler: + +```typescript +// Your webhook had a bug, fix it, then re-process +await client.evaluateChainhook('chainhook-uuid', { + block_height: 145000, +}); +``` + +### 5. Backfill Data + +Backfill data after creating a new chainhook: + +```typescript +// Create new chainhook for NFT transfers +const chainhook = await client.registerChainhook({ + version: '1', + name: 'nft-tracker', + chain: 'stacks', + network: 'mainnet', + filters: { + events: [ + { + type: 'nft_transfer', + asset_identifier: 'SP...COLL.nft::collectible', + }, + ], + }, + action: { + type: 'http_post', + url: 'https://example.com/webhooks', + }, + options: { + enable_on_registration: true, + }, +}); + +// Backfill last 30 days (~4320 blocks) +const currentHeight = 200000; // Get from API +const startHeight = currentHeight - 4320; + +for (let height = startHeight; height < currentHeight; height++) { + await client.evaluateChainhook(chainhook.uuid, { + block_height: height, + }); + await new Promise(resolve => setTimeout(resolve, 100)); // Rate limit +} +``` + +--- + +## Advanced Patterns + +### Batch Evaluation with Concurrency + +Process multiple blocks concurrently: + +```typescript +async function evaluateBatch( + chainhookUuid: string, + heights: number[], + concurrency = 5 +) { + // Split into batches + const batches = []; + for (let i = 0; i < heights.length; i += concurrency) { + batches.push(heights.slice(i, i + concurrency)); + } + + // Process each batch + for (const batch of batches) { + await Promise.all( + batch.map(height => + client.evaluateChainhook(chainhookUuid, { block_height: height }) + ) + ); + console.log(`Processed ${batch.length} blocks`); + } +} + +// Evaluate blocks 100000-100100 with concurrency of 5 +const heights = Array.from({ length: 101 }, (_, i) => 100000 + i); +await evaluateBatch('chainhook-uuid', heights, 5); +``` + +### Evaluate with Error Handling + +Handle errors gracefully during bulk evaluation: + +```typescript +async function safeEvaluate( + chainhookUuid: string, + height: number +): Promise<{ height: number; success: boolean; error?: string }> { + try { + await client.evaluateChainhook(chainhookUuid, { block_height: height }); + return { height, success: true }; + } catch (error) { + return { + height, + success: false, + error: error.message, + }; + } +} + +// Evaluate range with error tracking +const results = await Promise.all( + [100000, 100001, 100002].map(height => + safeEvaluate('chainhook-uuid', height) + ) +); + +const failed = results.filter(r => !r.success); +if (failed.length > 0) { + console.error('Failed blocks:', failed); +} +``` + +### Resume Interrupted Indexing + +Track progress and resume if interrupted: + +```typescript +async function indexWithCheckpoint( + chainhookUuid: string, + startHeight: number, + endHeight: number, + checkpointKey: string +) { + // Load last checkpoint + let currentHeight = loadCheckpoint(checkpointKey) || startHeight; + + try { + while (currentHeight <= endHeight) { + await client.evaluateChainhook(chainhookUuid, { + block_height: currentHeight, + }); + + // Save checkpoint every 10 blocks + if (currentHeight % 10 === 0) { + saveCheckpoint(checkpointKey, currentHeight); + } + + currentHeight++; + } + + // Clear checkpoint when complete + clearCheckpoint(checkpointKey); + } catch (error) { + console.error(`Failed at height ${currentHeight}:`, error); + console.log(`Resume with: indexWithCheckpoint(..., ${currentHeight}, ...)`); + throw error; + } +} +``` + +--- + +## Best Practices + +### 1. Rate Limiting + +Add delays between requests to avoid rate limits: + +```typescript +for (let height = start; height <= end; height++) { + await client.evaluateChainhook(uuid, { block_height: height }); + await new Promise(resolve => setTimeout(resolve, 100)); // 100ms delay +} +``` + +### 2. Test Before Production + +Always test against a few blocks before bulk evaluation: + +```typescript +// Test on 3 sample blocks first +for (const height of [100000, 100500, 101000]) { + await client.evaluateChainhook(uuid, { block_height: height }); +} + +// Verify webhooks work correctly, then do full range +``` + +### 3. Use Block Height for Ranges + +Block heights are easier for ranges than hashes: + +```typescript +// ✅ Good: Easy to iterate +for (let height = 100000; height <= 101000; height++) { + await client.evaluateChainhook(uuid, { block_height: height }); +} + +// ❌ Bad: Hard to iterate with hashes +``` + +### 4. Monitor Webhook Success + +Track webhook deliveries when evaluating: + +```typescript +// In your webhook handler +app.post('/webhooks', (req, res) => { + const { event, chainhook } = req.body; + const blockHeight = event.apply[0].block_identifier.index; + + console.log(`Received event for block ${blockHeight}`); + + // Process event... + + res.sendStatus(200); +}); +``` + +### 5. Consider Expiration Options + +Use expiration options to avoid processing too many blocks: + +```typescript +const chainhook = await client.registerChainhook({ + // ... config + options: { + expire_after_evaluations: 5000, // Stop after 5000 blocks + }, +}); +``` + +--- + +## Troubleshooting + +### No Webhook Received + +If you don't receive a webhook after evaluation: + +1. **Check filter matches**: The block may not contain matching events +2. **Verify webhook URL**: Ensure your endpoint is accessible +3. **Check chainhook status**: Verify the chainhook exists and is configured correctly + +```typescript +const chainhook = await client.getChainhook(uuid); +console.log('Webhook URL:', chainhook.definition.action.url); +console.log('Filters:', chainhook.definition.filters); +``` + +### Rate Limit Errors + +If you hit rate limits: + +```typescript +// Increase delay between requests +await new Promise(resolve => setTimeout(resolve, 200)); // 200ms instead of 100ms + +// Or reduce concurrency +await evaluateBatch(uuid, heights, 3); // 3 concurrent instead of 5 +``` + +### Block Not Found + +If you get a 404 for a block: + +```typescript +try { + await client.evaluateChainhook(uuid, { block_height: 999999999 }); +} catch (error) { + if (error.status === 404) { + console.error('Block not found - may not exist yet'); + } +} +``` + +--- + +## Next Steps + +:::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)/index.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/index.mdx new file mode 100644 index 000000000..a23798cee --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/index.mdx @@ -0,0 +1,159 @@ +--- +title: Chainhook SDK +description: TypeScript/JavaScript SDK for managing chainhooks programmatically +--- + +# Chainhook SDK + +The Chainhook SDK (`@hirosystems/chainhooks-client`) provides a TypeScript/JavaScript client for programmatically managing chainhooks. Use the SDK when you need to create, update, or manage chainhooks from your application code. + +## Why Use the SDK? + +**Use the SDK when you need to:** +- Edit existing chainhooks (not available in Platform UI) +- Automate chainhook operations +- Integrate chainhook management into your application +- Manage chainhooks in CI/CD pipelines +- Handle multiple chainhooks programmatically +- Evaluate chainhooks against past blocks +- Rotate webhook secrets for security + +**Use the [Platform UI](/tools/chainhook/platform-usage) when you need to:** +- Create simple chainhooks visually +- View chainhook status and activity +- Get started quickly without code + +## 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 constants for both networks: + +```typescript +import { CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +// Testnet +CHAINHOOKS_BASE_URL.testnet // https://api.testnet.hiro.so + +// Mainnet +CHAINHOOKS_BASE_URL.mainnet // https://api.mainnet.hiro.so +``` + +## Authentication + +All SDK methods require a Hiro API key. Get your API key from the [Hiro Platform](https://platform.hiro.so). + +```typescript +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, // Store securely in environment variables +}); +``` + +:::callout{type="warning"} +**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 + +The SDK is written in TypeScript and provides full type definitions: + +```typescript +import type { + ChainhookDefinitionSchema, + ChainhookStatusSchema, + EvaluateChainhookRequest, + BulkEnableChainhooksRequest, +} 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..62712ccc1 --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx @@ -0,0 +1,346 @@ +--- +title: List & Fetch +description: Retrieve chainhook information using the SDK +--- + +# List & Fetch + +Learn how to list and fetch chainhooks using the Chainhook SDK. + +## getChainhooks + +Retrieve a paginated list of all your chainhooks. + +### TypeScript + +```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 Pagination Options + +```typescript +// Get specific page with custom limit +const chainhooks = await client.getChainhooks({ + limit: 50, + offset: 100, +}); + +// This fetches 50 chainhooks starting from position 100 +``` + +### cURL + +```bash +curl -sS "https://api.testnet.hiro.so/chainhooks/me?limit=20&offset=0" \ + -H "x-api-key: $HIRO_API_KEY" +``` + +### Response + +```json +{ + "limit": 20, + "offset": 0, + "total": 45, + "results": [ + { + "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", + "definition": { + "version": "1", + "name": "my-chainhook", + "chain": "stacks", + "network": "testnet", + "filters": { ... }, + "action": { ... } + }, + "status": { + "enabled": true, + "type": "active", + "info": { + "last_evaluated_block_height": 150000, + "number_of_times_triggered": 42, + "number_of_blocks_evaluated": 1000 + } + } + }, + // ... more chainhooks + ] +} +``` + +### Pagination Example + +Fetch all chainhooks using pagination: + +```typescript +async function getAllChainhooks() { + const allChainhooks = []; + let offset = 0; + const limit = 100; + + while (true) { + const response = await client.getChainhooks({ limit, offset }); + allChainhooks.push(...response.results); + + // Check if we've fetched all chainhooks + if (offset + limit >= response.total) { + break; + } + + offset += limit; + } + + return allChainhooks; +} + +const allMyChainhooks = await getAllChainhooks(); +console.log(`Found ${allMyChainhooks.length} chainhooks`); +``` + +--- + +## getChainhook + +Retrieve a specific chainhook by UUID. + +### TypeScript + +```typescript +const chainhook = await client.getChainhook('be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185'); + +console.log('Name:', chainhook.definition.name); +console.log('Enabled:', chainhook.status.enabled); +console.log('Network:', chainhook.definition.network); +console.log('Times triggered:', chainhook.status.info.number_of_times_triggered); +``` + +### cURL + +```bash +curl -sS "https://api.testnet.hiro.so/chainhooks/me/" \ + -H "x-api-key: $HIRO_API_KEY" +``` + +### Response + +```json +{ + "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", + "definition": { + "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 + } + }, + "status": { + "enabled": true, + "type": "active", + "info": { + "last_evaluated_block_height": 150000, + "last_occurrence": 149850, + "number_of_times_triggered": 42, + "number_of_blocks_evaluated": 1000 + } + } +} +``` + +--- + +## Understanding Status + +The `status` object provides information about the chainhook's state: + +### Status Fields + +```typescript +{ + "status": { + "enabled": true, // Whether the chainhook is active + "type": "active", // Status type (active, inactive, expired, etc.) + "info": { + "last_evaluated_block_height": 150000, // Most recent block checked + "last_occurrence": 149850, // Most recent block where filter matched + "number_of_times_triggered": 42, // Total matches + "number_of_blocks_evaluated": 1000, // Total blocks processed + "expired_at_block_height": null // When chainhook expired (if applicable) + } + } +} +``` + +### Status Types + +| Type | Description | +|------|-------------| +| `active` | Chainhook is enabled and processing blocks | +| `inactive` | Chainhook is disabled (paused) | +| `expired` | Chainhook expired based on options | +| `scanning` | Chainhook is catching up with chain tip | + +### Check if Enabled + +```typescript +const chainhook = await client.getChainhook(uuid); + +if (chainhook.status.enabled) { + console.log('Chainhook is active'); +} else { + console.log('Chainhook is disabled'); +} +``` + +### Check Activity + +```typescript +const chainhook = await client.getChainhook(uuid); +const { info } = chainhook.status; + +console.log(`Triggered ${info.number_of_times_triggered} times`); +console.log(`Evaluated ${info.number_of_blocks_evaluated} blocks`); +console.log(`Last trigger at block ${info.last_occurrence}`); +``` + +--- + +## Common Use Cases + +### Find Chainhooks by Name + +```typescript +const allChainhooks = await client.getChainhooks({ limit: 100 }); + +const myChain hook = allChainhooks.results.find( + ch => ch.definition.name === 'ft-transfer-tracker' +); + +if (myChainhook) { + console.log('Found:', myChainhook.uuid); +} +``` + +### List All Enabled Chainhooks + +```typescript +const allChainhooks = await client.getChainhooks({ limit: 100 }); + +const enabledChainhooks = allChainhooks.results.filter( + ch => ch.status.enabled +); + +console.log(`${enabledChainhooks.length} enabled chainhooks`); +``` + +### Monitor Chainhook Activity + +```typescript +async function monitorChainhook(uuid: string) { + const chainhook = await client.getChainhook(uuid); + const { info } = chainhook.status; + + console.log(` + Chainhook: ${chainhook.definition.name} + Status: ${chainhook.status.enabled ? 'Active' : 'Inactive'} + Last evaluated: Block ${info.last_evaluated_block_height} + Total triggers: ${info.number_of_times_triggered} + Blocks processed: ${info.number_of_blocks_evaluated} + `); +} + +// Call periodically to monitor +setInterval(() => monitorChainhook('uuid-here'), 60000); // Every minute +``` + +### List Chainhooks by Webhook URL + +```typescript +const allChainhooks = await client.getChainhooks({ limit: 100 }); + +const webhookUrl = 'https://example.com/webhooks'; +const matchingChainhooks = allChainhooks.results.filter( + ch => ch.definition.action.url === webhookUrl +); + +console.log(`${matchingChainhooks.length} chainhooks POST to ${webhookUrl}`); +``` + +### Check for Expired Chainhooks + +```typescript +const allChainhooks = await client.getChainhooks({ limit: 100 }); + +const expiredChainhooks = allChainhooks.results.filter( + ch => ch.status.type === 'expired' +); + +for (const ch of expiredChainhooks) { + console.log(`Expired: ${ch.definition.name} at block ${ch.status.info.expired_at_block_height}`); +} +``` + +--- + +## Error Handling + +### Chainhook Not Found + +```typescript +try { + const chainhook = await client.getChainhook('invalid-uuid'); +} catch (error) { + if (error.status === 404) { + console.error('Chainhook not found'); + } else { + throw error; + } +} +``` + +### Handle Pagination Errors + +```typescript +try { + const chainhooks = await client.getChainhooks({ limit: 1000, offset: 0 }); +} catch (error) { + console.error('Failed to fetch chainhooks:', error); +} +``` + +--- + +## Next Steps + +:::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..2ef4e237d --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx @@ -0,0 +1,489 @@ +--- +title: Manage Keys +description: API key setup and webhook secret management +--- + +# Manage Keys + +Learn how to manage your Hiro API keys and webhook secrets for secure chainhook operations. + +## Hiro API Keys + +API keys are required to authenticate requests to the Chainhook API. Get your API key from the [Hiro Platform](https://platform.hiro.so). + +### Getting 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 + +### Using API Keys with the SDK + +Store your API key securely in environment variables: + +#### Environment Variables + +Create a `.env` file in your project root: + +```bash +HIRO_API_KEY=your_api_key_here +``` + +:::callout{type="warning"} +**Never commit `.env` files to version control.** Add `.env` to your `.gitignore` file. +::: + +#### Load Environment Variables + +Use a package like `dotenv` to load environment variables: + + + ```terminal !! npm + npm install dotenv + ``` + + ```terminal !! yarn + yarn add dotenv + ``` + + ```terminal !! pnpm + pnpm add dotenv + ``` + + ```terminal !! bun + bun add dotenv + ``` + + +Then load in your code: + +```typescript +import 'dotenv/config'; +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, // Loaded from .env +}); +``` + +### Authentication Header + +The SDK automatically adds the `x-api-key` header to all requests: + +```typescript +// SDK handles this automatically +await client.getChainhooks(); + +// Equivalent cURL: +// curl -H "x-api-key: YOUR_API_KEY" https://api.testnet.hiro.so/chainhooks/me +``` + +### Using API Keys Directly (cURL) + +For direct API calls, include the `x-api-key` header: + +```bash +curl -sS "https://api.testnet.hiro.so/chainhooks/me" \ + -H "x-api-key: $HIRO_API_KEY" +``` + +--- + +## Webhook Secrets + +Webhook secrets (consumer secrets) are used to verify that webhook payloads are sent by Hiro and not a third party. + +### How Webhook Secrets Work + +1. When you create a chainhook, Hiro generates a unique consumer secret +2. This secret is included in the headers of every webhook payload sent to your endpoint +3. Your server validates the secret to ensure the request is authentic + +### Webhook Secret Header + +Every webhook request includes: + +```http +POST /webhooks HTTP/1.1 +Host: example.com +Content-Type: application/json +X-Chainhook-Consumer-Secret: your-secret-here + +{ + "event": { ... }, + "chainhook": { ... } +} +``` + +### Verifying Webhook Requests + +Validate incoming webhooks by checking the secret: + +```typescript +import express from 'express'; + +const app = express(); +app.use(express.json()); + +// Store your chainhook secrets (from database or config) +const CHAINHOOK_SECRETS = new Map([ + ['chainhook-uuid-1', 'secret-1'], + ['chainhook-uuid-2', 'secret-2'], +]); + +app.post('/webhooks', (req, res) => { + const providedSecret = req.headers['x-chainhook-consumer-secret']; + const chainhookUuid = req.body.chainhook.uuid; + const expectedSecret = CHAINHOOK_SECRETS.get(chainhookUuid); + + // Verify secret + if (providedSecret !== expectedSecret) { + console.error('Invalid webhook secret'); + return res.sendStatus(401); + } + + // Process webhook + const { event, chainhook } = req.body; + console.log('Valid webhook received from:', chainhook.name); + + // Your processing logic here... + + res.sendStatus(200); +}); + +app.listen(3000, () => console.log('Webhook server running')); +``` + +--- + +## rotateConsumerSecret + +Rotate the webhook secret for a chainhook. This generates a new secret that will be used for all future webhook deliveries. + +### TypeScript + +```typescript +const newSecret = await client.rotateConsumerSecret('chainhook-uuid'); + +console.log('New secret:', newSecret.consumer_secret); + +// Store the new secret securely +await db.chainhooks.update({ + uuid: 'chainhook-uuid', + secret: newSecret.consumer_secret, +}); +``` + +### cURL + +```bash +curl -sS -X POST "https://api.testnet.hiro.so/chainhooks/me//rotate-secret" \ + -H "x-api-key: $HIRO_API_KEY" +``` + +### Response + +```json +{ + "consumer_secret": "new-secret-value-here" +} +``` + +### When to Rotate Secrets + +Rotate webhook secrets when: + +1. **Security breach**: A secret has been exposed or compromised +2. **Regular rotation**: Part of your security policy (e.g., every 90 days) +3. **Team changes**: When team members with access leave +4. **Testing**: When you need to test secret rotation + +### Secret Rotation Example + +```typescript +async function rotateSecret(chainhookUuid: string) { + try { + // Rotate the secret + const { consumer_secret } = await client.rotateConsumerSecret(chainhookUuid); + + // Update your database + await db.chainhooks.update({ + uuid: chainhookUuid, + secret: consumer_secret, + lastRotated: new Date(), + }); + + console.log(`✅ Secret rotated for ${chainhookUuid}`); + return consumer_secret; + } catch (error) { + console.error('Failed to rotate secret:', error); + throw error; + } +} +``` + +### Rotating Secrets for All Chainhooks + +```typescript +async function rotateAllSecrets() { + const chainhooks = await client.getChainhooks({ limit: 100 }); + + for (const ch of chainhooks.results) { + const { consumer_secret } = await client.rotateConsumerSecret(ch.uuid); + + // Update storage + await db.chainhooks.update({ + uuid: ch.uuid, + secret: consumer_secret, + }); + + console.log(`✅ Rotated: ${ch.definition.name}`); + + // Rate limit + await new Promise(resolve => setTimeout(resolve, 100)); + } + + console.log('All secrets rotated'); +} +``` + +--- + +## Best Practices + +### 1. Store Secrets Securely + +Never hardcode secrets in your application: + +```typescript +// ❌ Bad: Hardcoded secret +const SECRET = 'my-secret-here'; + +// ✅ Good: From environment or secure storage +const SECRET = process.env.WEBHOOK_SECRET; + +// ✅ Better: From encrypted database +const SECRET = await getSecretFromDatabase(chainhookUuid); +``` + +### 2. Use Environment Variables + +Keep sensitive data in environment variables: + +```bash +# .env file +HIRO_API_KEY=your_api_key +WEBHOOK_SECRET_1=secret_for_chainhook_1 +WEBHOOK_SECRET_2=secret_for_chainhook_2 +``` + +Load with: + +```typescript +import 'dotenv/config'; + +const apiKey = process.env.HIRO_API_KEY; +``` + +### 3. Rotate Secrets Regularly + +Implement a rotation schedule: + +```typescript +// Rotate secrets every 90 days +async function scheduleSecretRotation() { + const chainhooks = await client.getChainhooks({ limit: 100 }); + + for (const ch of chainhooks.results) { + const lastRotated = await db.getLastRotationDate(ch.uuid); + const daysSinceRotation = (Date.now() - lastRotated) / (1000 * 60 * 60 * 24); + + if (daysSinceRotation >= 90) { + await rotateSecret(ch.uuid); + } + } +} + +// Run daily +setInterval(scheduleSecretRotation, 24 * 60 * 60 * 1000); +``` + +### 4. Validate All Incoming Webhooks + +Always verify webhook secrets before processing: + +```typescript +function validateWebhook(req: Request): boolean { + const providedSecret = req.headers['x-chainhook-consumer-secret']; + const chainhookUuid = req.body.chainhook?.uuid; + + if (!providedSecret || !chainhookUuid) { + return false; + } + + const expectedSecret = getStoredSecret(chainhookUuid); + return providedSecret === expectedSecret; +} + +app.post('/webhooks', (req, res) => { + if (!validateWebhook(req)) { + return res.sendStatus(401); + } + + // Process webhook... +}); +``` + +### 5. Handle Secret Rotation Gracefully + +During rotation, briefly accept both old and new secrets: + +```typescript +// Store both current and previous secret +interface ChainhookSecrets { + current: string; + previous?: string; + rotatedAt?: Date; +} + +function validateWebhook(req: Request): boolean { + const providedSecret = req.headers['x-chainhook-consumer-secret']; + const secrets = getStoredSecrets(req.body.chainhook.uuid); + + // Accept current or previous secret (for 5 minutes after rotation) + if (providedSecret === secrets.current) { + return true; + } + + if (secrets.previous && secrets.rotatedAt) { + const minutesSinceRotation = (Date.now() - secrets.rotatedAt.getTime()) / (1000 * 60); + if (minutesSinceRotation < 5) { + return providedSecret === secrets.previous; + } + } + + return false; +} +``` + +### 6. Monitor Failed Validations + +Track and alert on failed secret validations: + +```typescript +app.post('/webhooks', (req, res) => { + if (!validateWebhook(req)) { + // Log the failure + logger.warn('Webhook validation failed', { + chainhookUuid: req.body.chainhook?.uuid, + ip: req.ip, + timestamp: new Date(), + }); + + // Alert if too many failures + await checkForSecurityThreats(req.body.chainhook?.uuid); + + return res.sendStatus(401); + } + + // Process webhook... +}); +``` + +--- + +## Security Considerations + +### API Key Security + +- **Never commit API keys** to version control +- **Use environment variables** for local development +- **Use secret managers** for production (AWS Secrets Manager, HashiCorp Vault, etc.) +- **Rotate keys periodically** as part of security policy +- **Use different keys** for different environments (dev, staging, production) + +### Webhook Secret Security + +- **Always validate** webhook secrets on your server +- **Rotate secrets** if you suspect compromise +- **Log validation failures** for security monitoring +- **Use HTTPS** for webhook endpoints +- **Implement rate limiting** to prevent abuse + +### Production Setup + +For production, use a secret manager: + +```typescript +import { SecretsManager } from 'aws-sdk'; + +const secretsManager = new SecretsManager(); + +async function getApiKey(): Promise { + const secret = await secretsManager.getSecretValue({ + SecretId: 'hiro-api-key-production', + }).promise(); + + return JSON.parse(secret.SecretString!).apiKey; +} + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, + apiKey: await getApiKey(), +}); +``` + +--- + +## Troubleshooting + +### 401 Unauthorized + +If you get 401 errors: + +```typescript +// Check API key is set +console.log('API Key:', process.env.HIRO_API_KEY ? 'Set' : 'Missing'); + +// Verify it's correct +try { + await client.getChainhooks(); + console.log('✅ API key valid'); +} catch (error) { + if (error.status === 401) { + console.error('❌ Invalid API key'); + } +} +``` + +### Webhook Validation Failing + +If webhook validation fails: + +1. Check the secret matches: + ```typescript + console.log('Provided:', req.headers['x-chainhook-consumer-secret']); + console.log('Expected:', storedSecret); + ``` + +2. Verify the chainhook UUID: + ```typescript + console.log('Chainhook UUID:', req.body.chainhook.uuid); + ``` + +3. Check if secret was recently rotated: + ```typescript + const lastRotated = await db.getLastRotationDate(uuid); + console.log('Last rotated:', lastRotated); + ``` + +--- + +## Next Steps + +:::next-steps +* [Register & Enable](/tools/chainhook/register-enable): Create chainhooks with your API key +* [Quickstart](/tools/chainhook/quickstart): Complete end-to-end example +::: 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..c14c21b64 --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json @@ -0,0 +1,11 @@ +{ + "pages": [ + "index", + "quickstart", + "register-enable", + "list-fetch", + "edit-update", + "evaluate", + "manage-keys" + ] +} diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/quickstart.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/quickstart.mdx new file mode 100644 index 000000000..caa9beeaf --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/quickstart.mdx @@ -0,0 +1,275 @@ +--- +title: SDK Quickstart +description: Get started with the Chainhook SDK in 5 minutes +--- + +# SDK Quickstart + +This guide will help you create and enable your first chainhook using the Chainhook SDK in just a few minutes. + +## Prerequisites + +- Node.js 16+ installed +- A Hiro API key (get one from [Hiro Platform](https://platform.hiro.so)) +- A webhook endpoint to receive events (you can use [ngrok](https://ngrok.com/) or [webhook.site](https://webhook.site) for testing) + +## Step 1: Install the SDK + + + ```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 + ``` + + +## Step 2: Set Up Environment Variables + +Create a `.env` file in your project root: + +```bash +HIRO_API_KEY=your_api_key_here +WEBHOOK_URL=https://your-webhook-url.com/chainhook +``` + +:::callout{type="warning"} +Never commit your `.env` file to version control. Add it to `.gitignore`. +::: + +## Step 3: Initialize the Client + +Create a file called `chainhook-example.ts` (or `.js`): + +```typescript +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, // Use testnet for development + apiKey: process.env.HIRO_API_KEY!, +}); +``` + +## Step 4: Create Your First Chainhook + +Let's create a chainhook that triggers on FT transfers: + +```typescript +async function createChainhook() { + const chainhook = await client.registerChainhook({ + version: '1', + name: 'my-first-chainhook', + chain: 'stacks', + network: 'testnet', + filters: { + events: [ + { + type: 'ft_transfer', + }, + ], + }, + action: { + type: 'http_post', + url: process.env.WEBHOOK_URL!, + }, + options: { + decode_clarity_values: true, + enable_on_registration: true, // Enable immediately + }, + }); + + console.log('✅ Chainhook created and enabled!'); + console.log('UUID:', chainhook.uuid); + console.log('Status:', chainhook.status); + + return chainhook; +} + +createChainhook().catch(console.error); +``` + +## Step 5: Run Your Code + +Execute your script: + + + ```terminal !! Node.js + node chainhook-example.js + ``` + + ```terminal !! TypeScript + ts-node chainhook-example.ts + ``` + + ```terminal !! Bun + bun run chainhook-example.ts + ``` + + +You should see output like: + +``` +✅ Chainhook created and enabled! +UUID: be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185 +Status: { enabled: true, type: 'active', ... } +``` + +## Step 6: Verify Your Chainhook + +Check that your chainhook is active: + +```typescript +async function verifyChainhook(uuid: string) { + const chainhook = await client.getChainhook(uuid); + + console.log('Name:', chainhook.definition.name); + console.log('Enabled:', chainhook.status.enabled); + console.log('Network:', chainhook.definition.network); +} +``` + +## Step 7: Receive Webhook Events + +When FT transfers occur on Stacks testnet, your webhook endpoint will receive payloads like: + +```json +{ + "event": { + "apply": [ + { + "transactions": [ + { + "operations": [ + { + "type": "token_transfer", + "amount": { "value": "1000000" }, + "account": { "address": "SP..." } + } + ] + } + ] + } + ] + }, + "chainhook": { + "name": "my-first-chainhook", + "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185" + } +} +``` + +## Complete Example + +Here's a complete working example: + +```typescript +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; +import 'dotenv/config'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +async function main() { + try { + // Create and enable chainhook + const chainhook = await client.registerChainhook({ + version: '1', + name: 'ft-transfer-tracker', + chain: 'stacks', + network: 'testnet', + filters: { + events: [ + { + type: 'ft_transfer', + }, + ], + }, + action: { + type: 'http_post', + url: process.env.WEBHOOK_URL!, + }, + options: { + decode_clarity_values: true, + enable_on_registration: true, + }, + }); + + console.log('✅ Chainhook created!'); + console.log('UUID:', chainhook.uuid); + console.log('Enabled:', chainhook.status.enabled); + + // Verify it was created + const retrieved = await client.getChainhook(chainhook.uuid); + console.log('✅ Verified chainhook:', retrieved.definition.name); + + // List all your chainhooks + const allChainhooks = await client.getChainhooks({ limit: 10 }); + console.log(`📋 You have ${allChainhooks.total} chainhook(s)`); + + } catch (error) { + console.error('❌ Error:', error); + } +} + +main(); +``` + +## Testing Your Webhook + +To test your webhook endpoint locally: + +1. **Use ngrok** to expose your local server: + ```bash + ngrok http 3000 + ``` + +2. **Use webhook.site** to inspect incoming payloads: + - Visit [webhook.site](https://webhook.site) + - Copy the unique URL + - Use it as your `WEBHOOK_URL` + +3. **Trigger a test event** on Stacks testnet (make an FT transfer) + +## Next Steps + +Now that you have a basic chainhook running, explore more advanced features: + +:::next-steps +* [Register & Enable](/tools/chainhook/register-enable): Learn about all registration options +* [Filter Reference](/tools/chainhook/reference/filters): Explore all available event filters +::: + +## Troubleshooting + +### API Key Error + +If you see `401 Unauthorized`: +- Verify your API key is correct +- Ensure it's properly set in environment variables +- Check that you're using the correct base URL (testnet vs mainnet) + +### Webhook Not Receiving Events + +If your webhook isn't receiving payloads: +- Verify the webhook URL is publicly accessible +- Check that `enable_on_registration: true` is set +- Ensure your filter matches actual blockchain events +- Verify the chainhook status with `getChainhook()` + +### TypeScript Errors + +If you encounter TypeScript errors: +```bash +npm install --save-dev @types/node +``` 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..1caae1bcb --- /dev/null +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx @@ -0,0 +1,341 @@ +--- +title: Register & Enable +description: Create and activate chainhooks using the 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`. + +### TypeScript + +```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!, +}); + +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, // Enable immediately + }, +}); + +console.log('Chainhook UUID:', chainhook.uuid); +console.log('Enabled:', chainhook.status.enabled); // true +``` + +### cURL + +```bash +curl -sS -X POST "https://api.testnet.hiro.so/chainhooks/me" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ + "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 + } + }' +``` + +### Response + +```json +{ + "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", + "definition": { + "version": "1", + "name": "my-chainhook", + "chain": "stacks", + "network": "testnet", + "filters": { ... }, + "action": { ... }, + "options": { ... } + }, + "status": { + "enabled": true, + "type": "active" + } +} +``` + +### Enable Later + +If you don't set `enable_on_registration`, the chainhook will be created but disabled: + +```typescript +const chainhook = await client.registerChainhook({ + version: '1', + name: 'my-chainhook', + chain: 'stacks', + network: 'testnet', + filters: { /* ... */ }, + action: { /* ... */ }, + // No options.enable_on_registration +}); + +console.log('Enabled:', chainhook.status.enabled); // false + +// Enable it later +await client.enableChainhook(chainhook.uuid, true); +``` + +--- + +## enableChainhook + +Enable or disable a single chainhook by UUID. This allows you to pause a chainhook without deleting it. + +### TypeScript + +```typescript +// Enable a chainhook +await client.enableChainhook('chainhook-uuid', true); + +// Disable a chainhook +await client.enableChainhook('chainhook-uuid', false); +``` + +### cURL + +Enable: +```bash +curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me//enabled" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ "enabled": true }' +``` + +Disable: +```bash +curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me//enabled" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ "enabled": false }' +``` + +### Response + +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: { + 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: { + webhook_url: 'https://example.com/webhooks', + }, +}); +``` + +### By Status + +Enable all chainhooks with a specific status: + +```typescript +await client.bulkEnableChainhooks({ + enabled: true, + filters: { + statuses: ['inactive'], + }, +}); +``` + +### Combined Filters + +Use multiple filters together: + +```typescript +await client.bulkEnableChainhooks({ + enabled: false, // Disable matching chainhooks + filters: { + webhook_url: 'https://old-server.com/webhooks', + statuses: ['active'], + }, +}); +``` + +This will disable all active chainhooks that POST to the old webhook URL. + +### cURL + +```bash +curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/enabled" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ + "enabled": true, + "filters": { + "uuids": ["uuid-1", "uuid-2"], + "webhook_url": "https://example.com/webhooks" + } + }' +``` + +### Response + +```json +{ + "updated": 5, + "results": [ + { + "uuid": "uuid-1", + "enabled": true + }, + { + "uuid": "uuid-2", + "enabled": true + } + ] +} +``` + +--- + +## Best Practices + +### 1. Enable on Registration for Production + +For production chainhooks, enable immediately to avoid missing events: + +```typescript +{ + options: { + enable_on_registration: true, + } +} +``` + +### 2. Test Before Enabling + +For testing, create disabled and [evaluate against known blocks](/tools/chainhook/evaluate) first: + +```typescript +// Create disabled +const chainhook = await client.registerChainhook({ + // ... config + // No enable_on_registration +}); + +// Test against a known block +await client.evaluateChainhook(chainhook.uuid, { + block_height: 150000, +}); + +// If working as expected, enable it +await client.enableChainhook(chainhook.uuid, true); +``` + +### 3. Use Bulk Operations for Multiple Chainhooks + +When managing many chainhooks, use bulk operations: + +```typescript +// Disable all chainhooks for a specific webhook during maintenance +await client.bulkEnableChainhooks({ + enabled: false, + filters: { + webhook_url: 'https://api.myapp.com/webhooks', + }, +}); + +// Re-enable after maintenance +await client.bulkEnableChainhooks({ + enabled: true, + filters: { + webhook_url: 'https://api.myapp.com/webhooks', + }, +}); +``` + +### 4. Store UUIDs + +Always store the returned UUID for later reference: + +```typescript +const chainhook = await client.registerChainhook({ /* ... */ }); + +// Store this in your database +await db.chainhooks.create({ + uuid: chainhook.uuid, + name: chainhook.definition.name, + enabled: chainhook.status.enabled, +}); +``` + +--- + +## Next Steps + +:::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..2220f321f --- /dev/null +++ b/content/docs/en/tools/chainhook/(overview)/faq.mdx @@ -0,0 +1,252 @@ +--- +title: FAQ +description: Frequently asked questions about Chainhook 2.0 Beta +--- + +# Frequently Asked Questions + +## 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 [support@hiro.so](mailto:support@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 or via the API during and after the Beta period will run on v2.0. + +The API will also not support Chainhooks running on v1.0. + +:::callout{type="info"} +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 access both and what will change/stay the same? + +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. + +--- + +## 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. + +:::callout{type="warning"} +When creating a chainhook in the Platform, make sure all details are correct before creating it. You will not be able to edit it later in the Platform UI - you'll need to delete and recreate it, or use the SDK/API to make updates. +::: + +### 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](https://platform.hiro.so) +2. Sign in or create an account +3. Navigate to the API Keys section +4. Generate or copy your API key + +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: + +```typescript +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** + ```typescript + 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](/tools/chainhook/reference/filters) 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: + +```typescript +// 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: + +```typescript +// 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: + +```typescript +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](/tools/chainhook/usage#handling-blockchain-reorganizations). + +### 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: + +```typescript +{ + 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](https://ngrok.com/) or [webhook.site](https://webhook.site) to expose your local development server: + +```bash +# 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**: [support@hiro.so](mailto:support@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..cf9bb582e 100644 --- a/content/docs/en/tools/chainhook/(overview)/meta.json +++ b/content/docs/en/tools/chainhook/(overview)/meta.json @@ -1,4 +1,7 @@ { - "title": "Overview", - "pages": ["quickstart", "usage"] + "pages": [ + "usage", + "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..69423e7bd --- /dev/null +++ b/content/docs/en/tools/chainhook/(overview)/migration.mdx @@ -0,0 +1,668 @@ +--- +title: Migrating from v1 to v2 +description: Guide for migrating legacy v1 chainhooks to Chainhook 2.0 Beta +--- + +# Migrating from v1 to v2 + +This guide helps you migrate your legacy v1 chainhooks to the new Chainhook 2.0 Beta. Learn how to manage v1 chainhooks programmatically and recreate them using v2. + +## Overview + +### What Changed + +**Chainhook v1** used the Hiro Platform API with predicate-based JSON files and CLI tools. + +**Chainhook v2** introduces: +- New REST API with simpler endpoint structure +- TypeScript/JavaScript SDK for programmatic management +- Updated filter syntax and options +- Improved Platform UI (creation only, no editing yet) + +### Platform API vs Chainhook API + +There are now **two separate APIs** for managing chainhooks: + +| API | Purpose | Base URL | Auth | +|-----|---------|----------|------| +| **Platform API** | Manage legacy v1 chainhooks | `https://api.platform.hiro.so` | API key in path | +| **Chainhook API** | Manage v2 chainhooks | `https://api.testnet.hiro.so` or `https://api.mainnet.hiro.so` | `x-api-key` header | + +:::callout{type="info"} +**v1 chainhooks are read-only in the Platform UI.** You must use the Platform API to manage them programmatically. +::: + +--- + +## Migration Strategy + +### Recommended Approach + +1. **Audit v1 chainhooks** - List all your v1 chainhooks via Platform API +2. **Recreate in v2** - Use the new Chainhook SDK/API to create equivalent v2 chainhooks +3. **Test v2 chainhooks** - Verify v2 chainhooks work as expected +4. **Delete v1 chainhooks** - Remove old v1 chainhooks after successful migration + +:::callout{type="warning"} +**Do not delete v1 chainhooks until you've verified v2 replacements are working correctly.** +::: + +--- + +## Step 1: List v1 Chainhooks + +Use the Platform API to retrieve all your existing v1 chainhooks. + +### cURL + +```bash +curl -sS "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks" \ + -H "content-type: application/json" +``` + +### JavaScript/TypeScript + +```typescript +async function listV1Chainhooks() { + const response = await fetch( + `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks`, + { + headers: { + 'content-type': 'application/json', + }, + } + ); + + if (!response.ok) { + throw new Error(`Failed to list chainhooks: ${response.statusText}`); + } + + const chainhooks = await response.json(); + return chainhooks; +} + +const v1Chainhooks = await listV1Chainhooks(); +console.log(`Found ${v1Chainhooks.length} v1 chainhooks`); +``` + +### Response Example + +```json +[ + { + "uuid": "aa3626dc-2090-49cd-8f1e-8f9994393aed", + "name": "my-v1-chainhook", + "version": 1, + "chain": "stacks", + "networks": { + "mainnet": { + "if_this": { + "scope": "stx_event", + "actions": ["transfer"] + }, + "then_that": { + "http_post": { + "url": "https://example.com/webhooks", + "authorization_header": "Bearer token" + } + } + } + } + } +] +``` + +--- + +## Step 2: Get v1 Chainhook Details + +Retrieve detailed information about a specific v1 chainhook. + +### cURL + +```bash +curl -sS "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks/$CHAINHOOK_UUID" \ + -H "content-type: application/json" +``` + +### JavaScript/TypeScript + +```typescript +async function getV1Chainhook(uuid: string) { + const response = await fetch( + `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks/${uuid}`, + { + headers: { + 'content-type': 'application/json', + }, + } + ); + + if (!response.ok) { + throw new Error(`Failed to get chainhook: ${response.statusText}`); + } + + return await response.json(); +} + +const v1Chainhook = await getV1Chainhook('aa3626dc-2090-49cd-8f1e-8f9994393aed'); +console.log('v1 Chainhook:', v1Chainhook); +``` + +--- + +## Step 3: Create Equivalent v2 Chainhooks + +Recreate your v1 chainhooks using the v2 SDK or API with updated syntax. + +### v1 to v2 Mapping + +| v1 Concept | v2 Equivalent | +|------------|---------------| +| `if_this.scope` | `filters.events[].type` | +| `if_this.actions` | Part of event type (e.g., `ft_transfer`) | +| `then_that.http_post` | `action.type: "http_post"` | +| `networks.mainnet` | `network: "mainnet"` | +| `authorization_header` | Removed (use webhook secrets) | + +### Example Migration + +**v1 Chainhook (Platform API format)**: +```json +{ + "name": "stx-transfers", + "version": 1, + "chain": "stacks", + "networks": { + "mainnet": { + "if_this": { + "scope": "stx_event", + "actions": ["transfer"] + }, + "then_that": { + "http_post": { + "url": "https://example.com/webhooks", + "authorization_header": "Bearer token" + } + }, + "start_block": 100000 + } + } +} +``` + +**v2 Chainhook (SDK)**: +```typescript +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, + apiKey: process.env.HIRO_API_KEY!, +}); + +const v2Chainhook = await client.registerChainhook({ + version: '1', + name: 'stx-transfers', + chain: 'stacks', + network: 'mainnet', + filters: { + events: [ + { + type: 'stx_transfer', // Changed from scope + actions + }, + ], + }, + action: { + type: 'http_post', + url: 'https://example.com/webhooks', + // Note: Authorization header removed, use webhook secrets instead + }, + options: { + decode_clarity_values: true, + enable_on_registration: true, + }, +}); + +console.log('Created v2 chainhook:', v2Chainhook.uuid); +``` + +--- + +## Step 4: Delete v1 Chainhooks + +After verifying v2 chainhooks work correctly, delete the old v1 chainhooks. + +### cURL + +```bash +curl -sS -X DELETE \ + "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks/$CHAINHOOK_UUID" \ + -H "content-type: application/json" +``` + +### JavaScript/TypeScript + +```typescript +async function deleteV1Chainhook(uuid: string) { + 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', + }, + } + ); + + if (!response.ok) { + throw new Error(`Failed to delete chainhook: ${response.statusText}`); + } + + const result = await response.json(); + return result; +} + +await deleteV1Chainhook('aa3626dc-2090-49cd-8f1e-8f9994393aed'); +console.log('v1 chainhook deleted'); +``` + +### Response + +```json +{ + "status": "success", + "chainhookUuid": "aa3626dc-2090-49cd-8f1e-8f9994393aed", + "message": "Chainhook deleted successfully" +} +``` + +--- + +## Complete Migration Script + +Here's a complete script to migrate all v1 chainhooks to v2: + +```typescript +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + +const PLATFORM_API_BASE = 'https://api.platform.hiro.so'; +const API_KEY = process.env.HIRO_API_KEY!; + +// Initialize v2 client +const v2Client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, + apiKey: API_KEY, +}); + +// Helper: List v1 chainhooks +async function listV1Chainhooks() { + const response = await fetch( + `${PLATFORM_API_BASE}/v1/ext/${API_KEY}/chainhooks` + ); + return await response.json(); +} + +// Helper: Delete v1 chainhook +async function deleteV1Chainhook(uuid: string) { + await fetch( + `${PLATFORM_API_BASE}/v1/ext/${API_KEY}/chainhooks/${uuid}`, + { method: 'DELETE' } + ); +} + +// Convert v1 filter to v2 +function convertV1ToV2(v1Chainhook: any) { + const network = v1Chainhook.networks.mainnet || v1Chainhook.networks.testnet; + const scope = network.if_this.scope; + const actions = network.if_this.actions || []; + + // Map v1 scopes to v2 event types + let eventType: string; + if (scope === 'stx_event') { + eventType = actions.includes('transfer') ? 'stx_transfer' : 'stx_event'; + } else if (scope === 'contract_call') { + eventType = 'contract_call'; + } else if (scope === 'ft_event') { + eventType = actions.includes('transfer') ? 'ft_transfer' : 'ft_event'; + } else if (scope === 'nft_event') { + eventType = actions.includes('transfer') ? 'nft_transfer' : 'nft_event'; + } else { + eventType = scope; // Fallback + } + + return { + version: '1', + name: v1Chainhook.name, + chain: v1Chainhook.chain, + network: v1Chainhook.networks.mainnet ? 'mainnet' : 'testnet', + filters: { + events: [ + { + type: eventType, + // Add additional filter properties based on v1 config + ...(network.if_this.contract_identifier && { + contract_identifier: network.if_this.contract_identifier, + }), + }, + ], + }, + action: { + type: 'http_post', + url: network.then_that.http_post.url, + }, + options: { + decode_clarity_values: true, + enable_on_registration: true, + }, + }; +} + +// Main migration function +async function migrateAllChainhooks() { + console.log('🔄 Starting migration from v1 to v2...\n'); + + // Step 1: List v1 chainhooks + const v1Chainhooks = await listV1Chainhooks(); + console.log(`Found ${v1Chainhooks.length} v1 chainhooks\n`); + + const migrated = []; + const failed = []; + + // Step 2: Migrate each v1 chainhook to v2 + for (const v1Chainhook of v1Chainhooks) { + try { + console.log(`Migrating: ${v1Chainhook.name} (${v1Chainhook.uuid})`); + + // Convert and create v2 chainhook + const v2Config = convertV1ToV2(v1Chainhook); + const v2Chainhook = await v2Client.registerChainhook(v2Config); + + console.log(`✅ Created v2 chainhook: ${v2Chainhook.uuid}`); + + // Store migration info + migrated.push({ + v1Uuid: v1Chainhook.uuid, + v2Uuid: v2Chainhook.uuid, + name: v1Chainhook.name, + }); + + // Wait a bit between API calls + await new Promise(resolve => setTimeout(resolve, 500)); + } catch (error) { + console.error(`❌ Failed to migrate ${v1Chainhook.name}:`, error.message); + failed.push({ uuid: v1Chainhook.uuid, name: v1Chainhook.name, error }); + } + } + + console.log(`\n📊 Migration Summary:`); + console.log(`- Migrated: ${migrated.length}`); + console.log(`- Failed: ${failed.length}\n`); + + // Step 3: Prompt to delete v1 chainhooks + if (migrated.length > 0) { + console.log('⚠️ Review v2 chainhooks before deleting v1 versions'); + console.log('Run deleteV1Chainhooks() when ready to clean up\n'); + + return { migrated, failed }; + } +} + +// Optional: Delete v1 chainhooks after verification +async function deleteV1Chainhooks(uuids: string[]) { + console.log(`🗑️ Deleting ${uuids.length} v1 chainhooks...\n`); + + for (const uuid of uuids) { + try { + await deleteV1Chainhook(uuid); + console.log(`✅ Deleted v1 chainhook: ${uuid}`); + await new Promise(resolve => setTimeout(resolve, 500)); + } catch (error) { + console.error(`❌ Failed to delete ${uuid}:`, error.message); + } + } + + console.log('\n✅ v1 cleanup complete!'); +} + +// Run migration +const result = await migrateAllChainhooks(); + +// After verifying v2 chainhooks work: +// await deleteV1Chainhooks(result.migrated.map(m => m.v1Uuid)); +``` + +--- + +## Filter Conversion Guide + +### STX Events + +**v1**: +```json +{ + "scope": "stx_event", + "actions": ["transfer"] +} +``` + +**v2**: +```json +{ + "events": [ + { "type": "stx_transfer" } + ] +} +``` + +### Contract Calls + +**v1**: +```json +{ + "scope": "contract_call", + "contract_identifier": "SP...XYZ.counter", + "method": "increment" +} +``` + +**v2**: +```json +{ + "events": [ + { + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter", + "function_name": "increment" + } + ] +} +``` + +### FT Events + +**v1**: +```json +{ + "scope": "ft_event", + "asset_identifier": "SP...ABC.token::usdc", + "actions": ["transfer"] +} +``` + +**v2**: +```json +{ + "events": [ + { + "type": "ft_transfer", + "asset_identifier": "SP...ABC.token::usdc" + } + ] +} +``` + +### NFT Events + +**v1**: +```json +{ + "scope": "nft_event", + "asset_identifier": "SP...COLL.nft::item", + "actions": ["mint", "transfer"] +} +``` + +**v2**: +```json +{ + "events": [ + { + "type": "nft_mint", + "asset_identifier": "SP...COLL.nft::item" + }, + { + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::item" + } + ] +} +``` + +--- + +## Key Differences + +### 1. Authentication + +**v1 (Platform API)**: +- API key in path: `/v1/ext/{apiKey}/chainhooks` + +**v2 (Chainhook API)**: +- API key in header: `x-api-key: YOUR_KEY` +- Or use SDK which handles this automatically + +### 2. Webhook Authorization + +**v1**: +- Used `authorization_header` in config + +**v2**: +- Uses `x-chainhook-consumer-secret` header automatically +- Rotate with `rotateConsumerSecret()` method + +### 3. Filter Syntax + +**v1**: +- `scope` + `actions` approach +- Example: `scope: "stx_event", actions: ["transfer"]` + +**v2**: +- Direct event type +- Example: `type: "stx_transfer"` + +### 4. Network Configuration + +**v1**: +- Nested under `networks.mainnet` or `networks.testnet` + +**v2**: +- Top-level `network` field +- Separate chainhooks for each network + +--- + +## Troubleshooting + +### v1 Chainhook Not Found + +If you can't find a v1 chainhook: +```typescript +const chainhook = await getV1Chainhook(uuid); +if (!chainhook) { + console.error('Chainhook not found - may have been deleted'); +} +``` + +### Migration Fails + +If migration fails, check: +1. API key is valid for both Platform and Chainhook APIs +2. Webhook URL is accessible +3. Filter syntax is correct for v2 + +### v2 Chainhook Not Triggering + +After migration, if events aren't delivered: +1. Verify chainhook is enabled: `chainhook.status.enabled === true` +2. Check webhook secret validation in your endpoint +3. Use `evaluateChainhook()` to test against a known block + +--- + +## Best Practices + +### 1. Test Migration on Testnet First + +Migrate testnet chainhooks first to validate your process: + +```typescript +// Migrate testnet chainhooks first +const v2Client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: API_KEY, +}); +``` + +### 2. Keep v1 Running During Testing + +Don't delete v1 chainhooks until you've confirmed v2 works: +- Let both run for 24-48 hours +- Compare webhook deliveries +- Verify no data loss + +### 3. Document Your Mappings + +Keep a record of v1 to v2 UUID mappings: + +```typescript +await db.migrations.create({ + v1Uuid: v1Chainhook.uuid, + v2Uuid: v2Chainhook.uuid, + migratedAt: new Date(), + name: v1Chainhook.name, +}); +``` + +### 4. Monitor Both Systems + +Monitor webhook deliveries during transition: + +```typescript +app.post('/webhooks', (req, res) => { + const { chainhook } = req.body; + + console.log(`Webhook from: ${chainhook.uuid}`); + console.log(`Name: ${chainhook.name}`); + + // Process event... + res.sendStatus(200); +}); +``` + +--- + +## Next Steps + +:::next-steps +* [SDK Documentation](/tools/chainhook/chainhook-sdk): Learn the v2 SDK +* [Filter Reference](/tools/chainhook/reference/filters): Explore v2 filters +::: + +:::callout{type="help"} +### Need Migration Help? + +If you need assistance migrating your chainhooks: +- **Discord**: #chainhook channel on [Discord](https://stacks.chat/) +- **Email**: [support@hiro.so](mailto:support@hiro.so) +- **Office Hours**: Every Thursday at 11am ET +::: 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 index b0fb8e759..fa7a07338 100644 --- a/content/docs/en/tools/chainhook/(overview)/usage.mdx +++ b/content/docs/en/tools/chainhook/(overview)/usage.mdx @@ -1,262 +1,325 @@ --- -title: Usage examples -sidebarTitle: Usage -description: Learn Chainhook's core functionality through practical examples - predicates, event scopes, reorg handling, and operation modes. +title: Usage +description: Learn Chainhook core concepts, event filtering, webhook delivery, and best practices --- -## Predicates +# Usage -Predicates are JSON specifications that tell Chainhook what blockchain events to monitor and how to respond. They follow an if-this-then-that pattern. +Learn the core concepts of working with Chainhooks, how they process blockchain events, and best practices for building reliable event-driven applications. -```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 - } - } -} -``` +## How Chainhooks Work + +A chainhook monitors blockchain events and delivers matching data to your application via webhooks. Each chainhook consists of three main components: + +### 1. Filters -This predicate monitors all STX transfer events starting from block 100,000 and sends them to your webhook endpoint. +Filters define which blockchain events trigger your chainhook. You can monitor: -### Predicate components +- **Token events** - FT/NFT transfers, mints, and burns +- **STX events** - STX transfers and operations +- **Contract events** - Deployments, function calls, and logs +- **System events** - Block rewards and tenure changes +Example filter for FT transfers: ```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": { ... } - } + "events": [ + { + "type": "ft_transfer", + "asset_identifier": "SP...ABC.token::usdc" + } + ] } ``` -## Event scopes +Learn more in the [Filter Reference](/tools/chainhook/reference/filters). -Each blockchain supports different event types. Scopes define what specific events your predicate will match. +### 2. Actions -### Stacks events +Actions define where chainhook data is delivered. Currently, chainhooks support HTTP POST to your webhook endpoint: -```json contract-call-predicate.json +```json { - "if_this": { - "scope": "contract_call", - "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-oracle-v2-3", - "method": "update-price" + "action": { + "type": "http_post", + "url": "https://example.com/webhooks" } } ``` -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 +Your endpoint receives a POST request with the event payload whenever a filter matches. -### Bitcoin events +### 3. Options -```json bitcoin-ordinals-predicate.json +Options customize the payload content and evaluation behavior: + +```json { - "if_this": { - "scope": "ordinals_protocol", - "operation": "inscription_feed" + "options": { + "decode_clarity_values": true, + "include_block_metadata": true, + "enable_on_registration": true } } ``` -Track Bitcoin ordinal inscriptions. Available Bitcoin scopes: -- `p2pkh`, `p2sh`, `p2wpkh`, `p2tr` - Address types -- `ordinals_protocol` - Ordinal inscriptions -- `stacks_protocol` - Stacks-specific Bitcoin operations +Learn more in the [Options Reference](/tools/chainhook/reference/options). -## Action handlers +--- -Define how Chainhook responds when events match your predicate criteria. +## Event Processing Flow -### Webhook delivery +1. **Block Evaluation** - Chainhook evaluates each new block against your filters +2. **Event Matching** - If events match your filters, a payload is generated +3. **Webhook Delivery** - The payload is POSTed to your configured endpoint +4. **Reorg Handling** - If a reorg occurs, rollback events are sent -```json webhook-config.json -{ - "then_that": { - "http_post": { - "url": "https://api.myapp.com/events", - "authorization_header": "Bearer ${WEBHOOK_SECRET}" - } - } -} +``` +Blockchain → Chainhook → Filter Match → Webhook Delivery → Your Application ``` -Chainhook POSTs matching events to your endpoint with the authorization header. +--- + +## Webhook Payloads -### File output +When a chainhook triggers, you receive a payload with this structure: -```json file-output-config.json +```json { - "then_that": { - "file_append": { - "path": "/data/blockchain-events.jsonl" - } + "event": { + "apply": [ + { + "block_identifier": { "index": 150000, "hash": "0x..." }, + "transactions": [ + { + "metadata": { /* tx info */ }, + "operations": [ /* state changes */ ] + } + ] + } + ], + "rollback": [], + "chain": "stacks", + "network": "mainnet" + }, + "chainhook": { + "uuid": "chainhook-uuid", + "name": "my-chainhook" } } ``` -Append events to a local file, one JSON object per line. +Learn more in the [Payload Anatomy](/tools/chainhook/reference/payload-anatomy) reference. -## Blockchain reorganizations +--- -Chainhook automatically handles blockchain reorganizations (reorgs) by sending both rollback and apply events. +## Handling Blockchain Reorganizations -```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 - ] - } - ] -} -``` +Chainhook automatically detects blockchain reorganizations (reorgs) and sends both `apply` and `rollback` events to keep your data consistent. -Your application must handle both event types to maintain data consistency during reorgs. +### Understanding Reorgs -### Handling reorg events +A reorg occurs when the blockchain's canonical chain changes: +1. Some blocks become orphaned (removed from the canonical chain) +2. New blocks become part of the canonical chain +3. Transactions may be reordered or invalidated -```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); - } +### Processing Reorg Events + +Always process rollback events before apply events: + +```typescript +app.post('/webhooks', async (req, res) => { + const { apply, rollback } = req.body.event; + + // Step 1: Roll back orphaned blocks + for (const block of rollback) { + await database.revertBlock(block); + console.log(`Rolled back block ${block.block_identifier.index}`); } - - // Then apply new canonical transactions - if (apply) { - for (const block of apply) { - await processTransactions(block.transactions); - } + + // Step 2: Apply new canonical blocks + for (const block of apply) { + await database.processBlock(block); + console.log(`Applied block ${block.block_identifier.index}`); } - - res.status(200).send('OK'); + + res.sendStatus(200); }); ``` -## Operation modes +### Best Practices for Reorgs -Chainhook operates in two primary modes depending on your use case. +1. **Process rollbacks first** - Always revert orphaned blocks before applying new ones +2. **Use transactions** - Wrap database operations in transactions for atomicity +3. **Track block hashes** - Store block hashes to detect reorgs in your data +4. **Test reorg scenarios** - Use the [evaluate method](/tools/chainhook/evaluate) to test reorg handling -### Scanning mode +--- -Analyze historical blockchain data by scanning archived blocks: +## SDK vs Platform -```terminal -$ chainhook predicates scan my-predicate.json --mainnet -``` +Choose the right tool for your use case: -Use scanning mode for: -- Testing predicates before deployment -- Analyzing past blockchain events -- Backfilling historical data +### Use the Chainhook SDK When: -### Service mode +- **Editing required** - Platform UI doesn't support editing chainhooks yet +- **Automation** - Integrate chainhook management into your CI/CD pipeline +- **Programmatic control** - Create/update/delete chainhooks from your application +- **Scale** - Manage many chainhooks with bulk operations +- **Advanced features** - Use evaluate for historical indexing or testing -Monitor live blockchain events in real-time: +### Use the Platform UI When: -```terminal -$ chainhook service start --config-path=config.toml -``` +- **Getting started** - Create your first chainhook quickly without code +- **Visual creation** - Prefer a form-based interface +- **Viewing status** - Monitor chainhook activity and metrics +- **Managing keys** - Generate and manage API keys visually -```toml config.toml -[http_api] -http_port = 20456 -database_uri = "redis://localhost:6379" +:::callout{type="info"} +Both approaches use the same underlying Chainhook API and deliver identical webhook payloads. +::: -[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. +## Chainhook Lifecycle -## Network configuration +### 1. Create +Register a chainhook with filters, action, and options: +- SDK: `client.registerChainhook({ ... })` +- Platform: Fill out the creation form -Configure predicates differently for each network environment: +### 2. Enable +Activate the chainhook to start receiving events: +- SDK: Set `enable_on_registration: true` or call `client.enableChainhook()` +- Platform: Enable toggle in the UI -```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" - } - } - } -} +### 3. Monitor +Track chainhook activity and performance: +- SDK: `client.getChainhook(uuid)` to check status +- Platform: View metrics in the dashboard + +### 4. Update +Modify filters, options, or webhook URL: +- SDK: `client.updateChainhook(uuid, { ... })` +- Platform: Not currently available (use SDK) + +### 5. Disable/Delete +Pause or remove a chainhook: +- SDK: `client.enableChainhook(uuid, false)` or `client.deleteChainhook(uuid)` +- Platform: Disable toggle or delete button + +--- + +## Best Practices + +### Webhook Security + +1. **Validate secrets** - Check the `x-chainhook-consumer-secret` header on all requests +2. **Use HTTPS** - Always use encrypted endpoints for webhooks +3. **Rotate secrets** - Periodically rotate webhook secrets with `rotateConsumerSecret()` +4. **Implement retries** - Handle webhook delivery failures gracefully + +### Performance + +1. **Filter precisely** - Use specific filters to reduce unnecessary webhooks +2. **Process asynchronously** - Return 200 quickly, process events in the background +3. **Batch operations** - Use bulk enable/disable for managing multiple chainhooks +4. **Monitor payload size** - Only enable options you need to reduce payload size + +### Reliability + +1. **Handle reorgs** - Always process rollback events correctly +2. **Idempotent processing** - Handle duplicate webhook deliveries +3. **Test before enabling** - Use `evaluateChainhook()` against known blocks +4. **Set expiration** - Use `expire_after_occurrences` for time-limited chainhooks + +### Development + +1. **Start on testnet** - Test your chainhooks on testnet before mainnet +2. **Use decode options** - Enable `decode_clarity_values` for easier debugging +3. **Log webhook deliveries** - Track all incoming webhooks for debugging +4. **Version your chainhooks** - Include version numbers in chainhook names + +--- + +## Common Patterns + +### Track User Activity + +Monitor all activity for a specific address: + +```typescript +await client.registerChainhook({ + name: 'user-activity-tracker', + chain: 'stacks', + network: 'mainnet', + filters: { + events: [ + { type: 'stx_transfer', sender: 'SP...USER' }, + { type: 'stx_transfer', receiver: 'SP...USER' }, + { type: 'ft_transfer', sender: 'SP...USER' }, + { type: 'nft_transfer', sender: 'SP...USER' }, + ], + }, + action: { type: 'http_post', url: 'https://api.myapp.com/user-events' }, + options: { decode_clarity_values: true, enable_on_registration: true }, +}); ``` -### Configuration options +### Monitor Contract Calls + +Track specific contract function calls: + +```typescript +await client.registerChainhook({ + name: 'dex-swap-monitor', + chain: 'stacks', + network: 'mainnet', + filters: { + events: [ + { + type: 'contract_call', + contract_identifier: 'SP...DEX.swap-contract', + function_name: 'swap', + }, + ], + }, + action: { type: 'http_post', url: 'https://api.myapp.com/swaps' }, + options: { decode_clarity_values: true, enable_on_registration: true }, +}); +``` -```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 -} +### Time-Limited Monitoring + +Create a chainhook that expires after a certain number of events: + +```typescript +await client.registerChainhook({ + name: 'limited-nft-mints', + chain: 'stacks', + network: 'mainnet', + filters: { + events: [ + { + type: 'nft_mint', + asset_identifier: 'SP...COLL.nft::collectible', + }, + ], + }, + action: { type: 'http_post', url: 'https://api.myapp.com/mints' }, + options: { + enable_on_registration: true, + expire_after_occurrences: 1000, // Auto-expire after 1000 mints + }, +}); ``` -## Further reading +--- + +## Next Steps -- [Design predicates](/tools/chainhook/usage) -- [Run Chainhook as a service](/tools/chainhook/service-mode) \ No newline at end of file +:::next-steps +* [SDK Quickstart](/tools/chainhook/quickstart): Start building with the SDK +* [Filter Reference](/tools/chainhook/reference/filters): Explore all event filters +::: diff --git a/content/docs/en/tools/chainhook/(platform-usage)/index.mdx b/content/docs/en/tools/chainhook/(platform-usage)/index.mdx new file mode 100644 index 000000000..86b3b66ab --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/index.mdx @@ -0,0 +1,5 @@ +--- +title: Platform Usage +description: Manage chainhooks visually with the Hiro Platform web interface +--- + diff --git a/content/docs/en/tools/chainhook/(platform-usage)/list-fetch.mdx b/content/docs/en/tools/chainhook/(platform-usage)/list-fetch.mdx new file mode 100644 index 000000000..468a021a0 --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/list-fetch.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/(platform-usage)/manage-keys.mdx b/content/docs/en/tools/chainhook/(platform-usage)/manage-keys.mdx new file mode 100644 index 000000000..258830eb8 --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/manage-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..622a2a746 --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/meta.json @@ -0,0 +1,9 @@ +{ + "pages": [ + "index", + "quickstart", + "register-enable", + "list-fetch", + "manage-keys" + ] +} diff --git a/content/docs/en/tools/chainhook/(platform-usage)/quickstart.mdx b/content/docs/en/tools/chainhook/(platform-usage)/quickstart.mdx new file mode 100644 index 000000000..d59e81e49 --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/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)/register-enable.mdx b/content/docs/en/tools/chainhook/(platform-usage)/register-enable.mdx new file mode 100644 index 000000000..cd4a8252c --- /dev/null +++ b/content/docs/en/tools/chainhook/(platform-usage)/register-enable.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/index.mdx b/content/docs/en/tools/chainhook/index.mdx index 186eb4185..756d4e06c 100644 --- a/content/docs/en/tools/chainhook/index.mdx +++ b/content/docs/en/tools/chainhook/index.mdx @@ -5,53 +5,56 @@ description: Chainhook is a reorg-aware indexer that serves reliable blockchain llm: false --- +:::callout{type="info"} +**Chainhook 2.0 Beta**: Chainhook 2.0 is currently in beta. During this period, we're focused on learning about reliability, performance, and developer experience. If you encounter issues or have feedback, please reach out to [support@hiro.so](mailto:support@hiro.so). +::: + ## Overview 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. +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 + To explore Chainhook features with AI, copy and paste [llms.txt](/tools/chainhook/llms.txt) into your LLM of choice. ![Chainhook overview](/images/tools/chainhook/overview.svg) -## 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 +- **Webhook delivery** - Receive blockchain events at your HTTP endpoint +- **Historical evaluation** - Test chainhooks against past blocks for indexing or debugging - +## Getting Started -```terminal !! macOS -$ brew install chainhook -``` +### Choose Your Approach -```terminal !! Linux -$ sudo snap install chainhook -``` +**Use the SDK when you need to:** +- Edit existing chainhooks (not available in Platform UI) +- Automate chainhook operations +- Integrate chainhook management into your application +- Manage chainhooks programmatically at scale -```terminal !! Windows -$ winget install HiroSystems.Chainhook -``` +**Use the Platform UI when you need to:** +- Create chainhooks visually without code +- View chainhook status and activity +- Manage API keys +- Get started quickly -```terminal !! Cargo -$ git clone https://github.com/hirosystems/chainhook.git -$ cd chainhook && cargo chainhook-install -``` - - - -## Next steps +## Next Steps :::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 Quickstart](/tools/chainhook/quickstart): Get started with the Chainhook SDK in 5 minutes +* [Platform Quickstart](/tools/chainhook/platform-quickstart): Create your first chainhook in the Platform UI ::: -:::callout -type: help +:::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. ::: diff --git a/content/docs/en/tools/chainhook/meta.json b/content/docs/en/tools/chainhook/meta.json index 4ce4bb6a9..db453a8f4 100644 --- a/content/docs/en/tools/chainhook/meta.json +++ b/content/docs/en/tools/chainhook/meta.json @@ -2,14 +2,10 @@ "title": "Chainhook", "root": true, "pages": [ - "---Chainhook---", "index", "...(overview)", - "---Chainhook CLI---", - "...(chainhook-cli)", - "---Integrations---", - "...(integrations)", - "---Reference---", + "...(chainhook-sdk)", + "...(platform-usage)", "...reference" ] } 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..88ceb2978 --- /dev/null +++ b/content/docs/en/tools/chainhook/reference/filters.mdx @@ -0,0 +1,353 @@ +--- +title: Filter Reference +description: Complete reference for all Chainhook filter types +--- + +# Filter Reference + +Filters define which blockchain events will trigger your chainhook. Use any of the following filter types in your `filters.events[]` array. + +## 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", + "asset_identifier": "SP...ABC.ft::usdc" +} +``` + +Filter by sender: + +```json +{ + "type": "ft_transfer", + "asset_identifier": "SP...ABC.ft::usdc", + "sender": "SP...FROM" +} +``` + +Filter by receiver: + +```json +{ + "type": "ft_transfer", + "asset_identifier": "SP...ABC.ft::usdc", + "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", + "asset_identifier": "SP...COLL.nft::collectible" +} +``` + +Filter by sender: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + "sender": "SP...FROM" +} +``` + +Filter by receiver: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + "receiver": "SP...TO" +} +``` + +Filter by specific token ID: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + "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", + "sender": "SP...SENDER" +} +``` + +Filter by receiver: + +```json +{ + "type": "stx_transfer", + "receiver": "SP...RECEIVER" +} +``` + +--- + +## Contract Events + +### Contract Deploy + +Match any contract deployment: + +```json +{ + "type": "contract_deploy" +} +``` + +Filter by deployer: + +```json +{ + "type": "contract_deploy", + "sender": "SP...DEPLOYER" +} +``` + +### Contract Call + +Match any contract call: + +```json +{ + "type": "contract_call" +} +``` + +Match calls to a specific contract: + +```json +{ + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter" +} +``` + +Match calls to a specific function: + +```json +{ + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter", + "function_name": "increment" +} +``` + +Filter by caller: + +```json +{ + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter", + "function_name": "increment", + "sender": "SP...CALLER" +} +``` + +### Contract Log + +Match contract print events: + +```json +{ + "type": "contract_log", + "contract_identifier": "SP...XYZ.counter" +} +``` + +Filter by transaction sender: + +```json +{ + "type": "contract_log", + "contract_identifier": "SP...XYZ.counter", + "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", + "cause": "block_found" +} +``` + +Match tenure changes by cause (extended): + +```json +{ + "type": "tenure_change", + "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" + } + ] + } +} +``` + +This chainhook will trigger on either DIKO transfers **or** calls to the `increment` function. + +--- + +## Next Steps + +:::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..3523c644f --- /dev/null +++ b/content/docs/en/tools/chainhook/reference/meta.json @@ -0,0 +1,7 @@ +{ + "pages": [ + "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..c9e8d5481 --- /dev/null +++ b/content/docs/en/tools/chainhook/reference/options.mdx @@ -0,0 +1,280 @@ +--- +title: Options Reference +description: Complete reference for all Chainhook configuration options +--- + +# Options Reference + +Options control payload enrichment and evaluation windows for your chainhook. The `options` field is optional and can be omitted or set to `null`. + +:::callout{type="info"} +**Defaults**: All boolean options default to `false` if omitted. Integer options are optional. +::: + +--- + +## Payload Enrichment Options + +### 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 + } +} +``` + +**Example output when enabled**: +```json +{ + "hex": "0x01000000000000000000000002690ed9fe", + "repr": "u10352515582", + "type": "uint" +} +``` + +--- + +### 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. + +--- + +## Combined Examples + +### Development Setup + +For local development with maximum visibility: + +```json +{ + "options": { + "decode_clarity_values": true, + "include_contract_abi": true, + "include_contract_source_code": true, + "include_post_conditions": true, + "include_block_metadata": true, + "enable_on_registration": true + } +} +``` + +### Production Setup + +For production with essential data only: + +```json +{ + "options": { + "decode_clarity_values": true, + "enable_on_registration": true + } +} +``` + +### Time-Limited Chainhook + +For a chainhook that auto-expires after 1000 blocks or 100 matches: + +```json +{ + "options": { + "decode_clarity_values": true, + "enable_on_registration": true, + "expire_after_evaluations": 1000, + "expire_after_occurrences": 100 + } +} +``` + +The chainhook will expire when **either** condition is met. + +--- + +## Options Summary Table + +| 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 | + +--- + +## Next Steps + +:::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..6153c4623 --- /dev/null +++ b/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx @@ -0,0 +1,488 @@ +--- +title: Payload Anatomy +description: Understanding the structure of Chainhook webhook payloads +--- + +# Payload Anatomy + +This guide explains the structure of webhook payloads delivered by Chainhook when your event filters match. + +## Payload Overview + +A Chainhook payload consists of two main sections: + +1. **event** - Contains the blockchain data (blocks, transactions, operations) +2. **chainhook** - 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{type="info"} +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{type="info"} +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 + } +} +``` + +**Note**: Negative values indicate debits (sender), positive values indicate credits (receiver). + +### 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 + } +} +``` + +--- + +## Complete Example + +Here's a complete payload from a contract call that transfers fungible tokens: + +```json +{ + "event": { + "apply": [ + { + "timestamp": 1757977309, + "transactions": [ + { + "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 + }, + "operations": [ + { + "type": "fee", + "amount": { + "value": "-3000", + "currency": { + "symbol": "STX", + "decimals": 6 + } + }, + "status": "success", + "account": { + "address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9" + }, + "metadata": { + "sponsored": false + }, + "operation_identifier": { + "index": 0 + } + }, + { + "type": "contract_call", + "status": "success", + "account": { + "address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9" + }, + "metadata": { + "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))" + } + ], + "function_name": "transfer", + "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token" + }, + "operation_identifier": { + "index": 1 + } + }, + { + "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 + } + }, + { + "type": "token_transfer", + "amount": { + "value": "10352515582", + "currency": { + "symbol": "", + "decimals": 0, + "metadata": { + "token_type": "ft", + "asset_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token::diko" + } + } + }, + "status": "success", + "account": { + "address": "SPBM1X391HWMCVHKH6GK8XJRDR6B7REZQYQ8KBCK" + }, + "operation_identifier": { + "index": 3 + } + }, + { + "type": "contract_log", + "status": "success", + "metadata": { + "topic": "print", + "value": "0x09", + "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token" + }, + "operation_identifier": { + "index": 4 + } + } + ], + "transaction_identifier": { + "hash": "0x09defc9a6cd9318b5c458389d4dd57597203ec539818aec0de3cfcfd7af0c2ab" + } + } + ], + "block_identifier": { + "hash": "0xa204da7ae59b86a569d66c237721937e1708b72913c6a67abf58360b8f5935b7", + "index": 3549902 + }, + "parent_block_identifier": { + "hash": "0xad0acff4e24c398f00ea07079b36753f5136c1dfc021d19e58cfdcbcee265e5c", + "index": 3549901 + } + } + ], + "chain": "stacks", + "network": "mainnet", + "rollback": [] + }, + "chainhook": { + "name": "blocksurvey", + "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185" + } +} +``` + +--- + +## Operation Types + +Chainhook payloads can include the following operation types: + +| Type | Description | +|------|-------------| +| `fee` | Transaction fee payment | +| `contract_call` | Contract function invocation | +| `contract_deploy` | Contract deployment | +| `token_transfer` | FT/NFT/STX transfer | +| `contract_log` | Contract print statement | +| `coinbase` | Block reward | +| `tenure_change` | Nakamoto tenure change | + +--- + +## Best Practices + +### Handle Reorgs + +Always process the `rollback` array before the `apply` array: + +```typescript +const { apply, rollback } = payload.event; + +// Reverse state changes from rolled-back blocks +for (const block of rollback) { + await revertBlock(block); +} + +// Apply new canonical blocks +for (const block of apply) { + await processBlock(block); +} +``` + +### Index Operations + +Operations are indexed sequentially. Use `operation_identifier.index` to maintain order when processing events. + +### Check Transaction Status + +Always verify `metadata.status === "success"` before processing transaction data. Failed transactions are included in payloads but should typically be ignored. + +--- + +## Next Steps + +:::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/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/(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/scripts/fetch-openapi-specs.mts b/scripts/fetch-openapi-specs.mts index da4b46862..fe62751f3 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: 'chainhooks', + url: 'https://chainhooks-api.vercel.app/openapi.json', + }, ]; const GITHUB_API_SPECS: GitHubApiSpec[] = [ From 3119535b48639e83ae68b9283a27275d211924db Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 08:44:15 -0600 Subject: [PATCH 02/16] add proxy route --- app/api/proxy/route.ts | 63 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 app/api/proxy/route.ts diff --git a/app/api/proxy/route.ts b/app/api/proxy/route.ts new file mode 100644 index 000000000..f34102a67 --- /dev/null +++ b/app/api/proxy/route.ts @@ -0,0 +1,63 @@ +import { NextResponse } from 'next/server'; + +const ALLOWED_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']); + +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 }); + } + + 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') { + 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(url, 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(); + } + + return NextResponse.json({ + status: upstreamResponse.status, + statusText: upstreamResponse.statusText, + headers: Object.fromEntries(upstreamResponse.headers), + data, + }); + } catch (error) { + return NextResponse.json( + { error: error instanceof Error ? error.message : 'Proxy request failed.' }, + { status: 500 }, + ); + } +} From 6d824ec6061509703c487ff47a2517c13b2ff356 Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 08:44:31 -0600 Subject: [PATCH 03/16] add provider for api credentials --- providers/api-credentials-provider.tsx | 53 ++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 providers/api-credentials-provider.tsx 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; +} From 1cb3c8b2b4c0f55302782b3099d3121db4091f7d Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 08:44:57 -0600 Subject: [PATCH 04/16] update utils --- hooks/use-localized-navigation.tsx | 163 +++++++++++------------------ lib/api-config.ts | 51 +++++++-- lib/markdown-generator.ts | 4 +- lib/translations/en.ts | 4 + lib/translations/es.ts | 4 + lib/translations/types.ts | 4 + 6 files changed, 115 insertions(+), 115 deletions(-) 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/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; From 7085ef26694fd6b6b8799b3938cd79aeff3b7a26 Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 08:45:12 -0600 Subject: [PATCH 05/16] update content --- content/docs/en/apis/chainhook-api/index.mdx | 3 +- content/docs/en/apis/chainhook-api/meta.json | 2 +- .../bulk-enable-chainhooks.mdx | 6 +- .../{ => chainhooks}/delete-chainhook.mdx | 4 +- .../{ => chainhooks}/evaluate-chainhook.mdx | 6 +- .../{ => chainhooks}/get-chainhook.mdx | 4 +- .../{ => chainhooks}/get-chainhooks.mdx | 4 +- .../reference/chainhooks/index.mdx | 6 + .../reference/{ => chainhooks}/meta.json | 10 +- .../chainhooks/register-chainhook.mdx | 12 + .../update-chainhook-enabled.mdx | 6 +- .../{ => chainhooks}/update-chainhook.mdx | 6 +- .../chainhook-api/reference/info/index.mdx | 6 + .../chainhook-api/reference/info/meta.json | 5 + .../reference/{ => info}/status.mdx | 4 +- .../reference/register-chainhook.mdx | 12 - .../{ => secrets}/delete-consumer-secret.mdx | 4 +- .../chainhook-api/reference/secrets/index.mdx | 6 + .../chainhook-api/reference/secrets/meta.json | 8 + .../{ => secrets}/rotate-consumer-secret.mdx | 4 +- .../en/resources/archive/download-guide.mdx | 2 +- .../guides/using-pyth-price-feeds.mdx | 2 +- .../bitcoin-indexer/(indexer)/full-sync.mdx | 4 +- .../chainhook/(chainhook-sdk)/edit-update.mdx | 458 +----------- .../chainhook/(chainhook-sdk)/evaluate.mdx | 421 +---------- .../{index.mdx => introduction.mdx} | 82 +- .../chainhook/(chainhook-sdk)/list-fetch.mdx | 226 +----- .../chainhook/(chainhook-sdk)/manage-keys.mdx | 496 +----------- .../tools/chainhook/(chainhook-sdk)/meta.json | 4 +- .../chainhook/(chainhook-sdk)/quickstart.mdx | 275 ------- .../(chainhook-sdk)/register-enable.mdx | 81 +- .../en/tools/chainhook/(overview)/faq.mdx | 348 +++++---- .../en/tools/chainhook/(overview)/meta.json | 7 +- .../tools/chainhook/(overview)/migration.mdx | 706 +++++------------- .../en/tools/chainhook/(overview)/usage.mdx | 325 -------- ...nable.mdx => create-enable-chainhooks.mdx} | 0 .../chainhook/(platform-usage)/index.mdx | 5 - .../{manage-keys.mdx => manage-api-keys.mdx} | 0 .../chainhook/(platform-usage)/meta.json | 12 +- ...quickstart.mdx => platform-quickstart.mdx} | 0 .../(platform-usage)/platform-usage.mdx | 59 ++ .../{list-fetch.mdx => view-chainhooks.mdx} | 0 content/docs/en/tools/chainhook/index.mdx | 38 +- content/docs/en/tools/chainhook/meta.json | 8 +- .../en/tools/chainhook/reference/meta.json | 7 +- .../en/tools/chainhook/reference/options.mdx | 4 +- .../chainhook/reference/payload-anatomy.mdx | 4 +- .../es/resources/archive/download-guide.mdx | 2 +- .../guides/using-pyth-price-feeds.mdx | 2 +- 49 files changed, 702 insertions(+), 2994 deletions(-) rename content/docs/en/apis/chainhook-api/reference/{ => chainhooks}/bulk-enable-chainhooks.mdx (62%) rename content/docs/en/apis/chainhook-api/reference/{ => chainhooks}/delete-chainhook.mdx (72%) rename content/docs/en/apis/chainhook-api/reference/{ => chainhooks}/evaluate-chainhook.mdx (61%) rename content/docs/en/apis/chainhook-api/reference/{ => chainhooks}/get-chainhook.mdx (69%) rename content/docs/en/apis/chainhook-api/reference/{ => chainhooks}/get-chainhooks.mdx (71%) create mode 100644 content/docs/en/apis/chainhook-api/reference/chainhooks/index.mdx rename content/docs/en/apis/chainhook-api/reference/{ => chainhooks}/meta.json (58%) create mode 100644 content/docs/en/apis/chainhook-api/reference/chainhooks/register-chainhook.mdx rename content/docs/en/apis/chainhook-api/reference/{ => chainhooks}/update-chainhook-enabled.mdx (62%) rename content/docs/en/apis/chainhook-api/reference/{ => chainhooks}/update-chainhook.mdx (64%) create mode 100644 content/docs/en/apis/chainhook-api/reference/info/index.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/info/meta.json rename content/docs/en/apis/chainhook-api/reference/{ => info}/status.mdx (73%) delete mode 100644 content/docs/en/apis/chainhook-api/reference/register-chainhook.mdx rename content/docs/en/apis/chainhook-api/reference/{ => secrets}/delete-consumer-secret.mdx (72%) create mode 100644 content/docs/en/apis/chainhook-api/reference/secrets/index.mdx create mode 100644 content/docs/en/apis/chainhook-api/reference/secrets/meta.json rename content/docs/en/apis/chainhook-api/reference/{ => secrets}/rotate-consumer-secret.mdx (73%) rename content/docs/en/tools/chainhook/(chainhook-sdk)/{index.mdx => introduction.mdx} (61%) delete mode 100644 content/docs/en/tools/chainhook/(chainhook-sdk)/quickstart.mdx delete mode 100644 content/docs/en/tools/chainhook/(overview)/usage.mdx rename content/docs/en/tools/chainhook/(platform-usage)/{register-enable.mdx => create-enable-chainhooks.mdx} (100%) delete mode 100644 content/docs/en/tools/chainhook/(platform-usage)/index.mdx rename content/docs/en/tools/chainhook/(platform-usage)/{manage-keys.mdx => manage-api-keys.mdx} (100%) rename content/docs/en/tools/chainhook/(platform-usage)/{quickstart.mdx => platform-quickstart.mdx} (100%) create mode 100644 content/docs/en/tools/chainhook/(platform-usage)/platform-usage.mdx rename content/docs/en/tools/chainhook/(platform-usage)/{list-fetch.mdx => view-chainhooks.mdx} (100%) diff --git a/content/docs/en/apis/chainhook-api/index.mdx b/content/docs/en/apis/chainhook-api/index.mdx index 0e0714e34..e33a56769 100644 --- a/content/docs/en/apis/chainhook-api/index.mdx +++ b/content/docs/en/apis/chainhook-api/index.mdx @@ -93,7 +93,8 @@ For TypeScript/JavaScript projects, we recommend using the [Chainhook SDK](/tool * [API Reference](/apis/chainhook-api/reference): Complete endpoint documentation ::: -:::callout{type="help"} +:::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 index 34dd082bd..ed8d35b52 100644 --- a/content/docs/en/apis/chainhook-api/meta.json +++ b/content/docs/en/apis/chainhook-api/meta.json @@ -1,5 +1,5 @@ { "title": "Chainhook API", "root": true, - "pages": ["index", "usage", "...reference"] + "pages": ["index", "usage", "---Reference---", "...reference"] } diff --git a/content/docs/en/apis/chainhook-api/reference/bulk-enable-chainhooks.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/bulk-enable-chainhooks.mdx similarity index 62% rename from content/docs/en/apis/chainhook-api/reference/bulk-enable-chainhooks.mdx rename to content/docs/en/apis/chainhook-api/reference/chainhooks/bulk-enable-chainhooks.mdx index 543e9d95b..e8c0b267e 100644 --- a/content/docs/en/apis/chainhook-api/reference/bulk-enable-chainhooks.mdx +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/bulk-enable-chainhooks.mdx @@ -1,12 +1,12 @@ --- -title: Bulk enable or disable chainhooks -sidebarTitle: Patch +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/delete-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/delete-chainhook.mdx similarity index 72% rename from content/docs/en/apis/chainhook-api/reference/delete-chainhook.mdx rename to content/docs/en/apis/chainhook-api/reference/chainhooks/delete-chainhook.mdx index 556565c69..eb3974c08 100644 --- a/content/docs/en/apis/chainhook-api/reference/delete-chainhook.mdx +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/delete-chainhook.mdx @@ -1,12 +1,12 @@ --- -title: Delete a chainhook +title: Delete sidebarTitle: Delete description: Deletes a chainhook by its UUID full: true --- diff --git a/content/docs/en/apis/chainhook-api/reference/evaluate-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/evaluate-chainhook.mdx similarity index 61% rename from content/docs/en/apis/chainhook-api/reference/evaluate-chainhook.mdx rename to content/docs/en/apis/chainhook-api/reference/chainhooks/evaluate-chainhook.mdx index c4de59e62..73c77e858 100644 --- a/content/docs/en/apis/chainhook-api/reference/evaluate-chainhook.mdx +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/evaluate-chainhook.mdx @@ -1,12 +1,12 @@ --- -title: Evaluate a chainhook -sidebarTitle: Post +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/get-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhook.mdx similarity index 69% rename from content/docs/en/apis/chainhook-api/reference/get-chainhook.mdx rename to content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhook.mdx index a81b3beee..441b21d07 100644 --- a/content/docs/en/apis/chainhook-api/reference/get-chainhook.mdx +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhook.mdx @@ -1,12 +1,12 @@ --- title: Get a specific chainhook -sidebarTitle: Get +sidebarTitle: Get a specific chainhook description: Returns a chainhook by its UUID full: true --- diff --git a/content/docs/en/apis/chainhook-api/reference/get-chainhooks.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhooks.mdx similarity index 71% rename from content/docs/en/apis/chainhook-api/reference/get-chainhooks.mdx rename to content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhooks.mdx index b4536faab..a312452de 100644 --- a/content/docs/en/apis/chainhook-api/reference/get-chainhooks.mdx +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/get-chainhooks.mdx @@ -1,12 +1,12 @@ --- title: Get all chainhooks -sidebarTitle: Get +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/meta.json b/content/docs/en/apis/chainhook-api/reference/chainhooks/meta.json similarity index 58% rename from content/docs/en/apis/chainhook-api/reference/meta.json rename to content/docs/en/apis/chainhook-api/reference/chainhooks/meta.json index 7f60b985b..dd2beb039 100644 --- a/content/docs/en/apis/chainhook-api/reference/meta.json +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/meta.json @@ -1,7 +1,6 @@ { - "title": "API Reference", + "title": "Chainhooks", "pages": [ - "status", "register-chainhook", "get-chainhooks", "get-chainhook", @@ -9,8 +8,7 @@ "delete-chainhook", "evaluate-chainhook", "update-chainhook-enabled", - "bulk-enable-chainhooks", - "rotate-consumer-secret", - "delete-consumer-secret" - ] + "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/update-chainhook-enabled.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook-enabled.mdx similarity index 62% rename from content/docs/en/apis/chainhook-api/reference/update-chainhook-enabled.mdx rename to content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook-enabled.mdx index 9d97096bd..188f6cf99 100644 --- a/content/docs/en/apis/chainhook-api/reference/update-chainhook-enabled.mdx +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook-enabled.mdx @@ -1,12 +1,12 @@ --- -title: Enable or disable a chainhook -sidebarTitle: Patch +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/update-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook.mdx similarity index 64% rename from content/docs/en/apis/chainhook-api/reference/update-chainhook.mdx rename to content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook.mdx index c579aad9b..2322db17f 100644 --- a/content/docs/en/apis/chainhook-api/reference/update-chainhook.mdx +++ b/content/docs/en/apis/chainhook-api/reference/chainhooks/update-chainhook.mdx @@ -1,12 +1,12 @@ --- -title: Update a chainhook -sidebarTitle: Patch +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/status.mdx b/content/docs/en/apis/chainhook-api/reference/info/status.mdx similarity index 73% rename from content/docs/en/apis/chainhook-api/reference/status.mdx rename to content/docs/en/apis/chainhook-api/reference/info/status.mdx index 690b71997..092888e4f 100644 --- a/content/docs/en/apis/chainhook-api/reference/status.mdx +++ b/content/docs/en/apis/chainhook-api/reference/info/status.mdx @@ -1,12 +1,12 @@ --- title: API Status -sidebarTitle: Get +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/register-chainhook.mdx b/content/docs/en/apis/chainhook-api/reference/register-chainhook.mdx deleted file mode 100644 index c579cc7fe..000000000 --- a/content/docs/en/apis/chainhook-api/reference/register-chainhook.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Register a chainhook -sidebarTitle: Post -description: Allows users to register a new Chainhook -full: true ---- - - diff --git a/content/docs/en/apis/chainhook-api/reference/delete-consumer-secret.mdx b/content/docs/en/apis/chainhook-api/reference/secrets/delete-consumer-secret.mdx similarity index 72% rename from content/docs/en/apis/chainhook-api/reference/delete-consumer-secret.mdx rename to content/docs/en/apis/chainhook-api/reference/secrets/delete-consumer-secret.mdx index 1e06c01f0..95df97d3c 100644 --- a/content/docs/en/apis/chainhook-api/reference/delete-consumer-secret.mdx +++ b/content/docs/en/apis/chainhook-api/reference/secrets/delete-consumer-secret.mdx @@ -1,12 +1,12 @@ --- title: Delete consumer secret -sidebarTitle: Delete +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/rotate-consumer-secret.mdx b/content/docs/en/apis/chainhook-api/reference/secrets/rotate-consumer-secret.mdx similarity index 73% rename from content/docs/en/apis/chainhook-api/reference/rotate-consumer-secret.mdx rename to content/docs/en/apis/chainhook-api/reference/secrets/rotate-consumer-secret.mdx index 5b3b8b025..b531aa573 100644 --- a/content/docs/en/apis/chainhook-api/reference/rotate-consumer-secret.mdx +++ b/content/docs/en/apis/chainhook-api/reference/secrets/rotate-consumer-secret.mdx @@ -1,12 +1,12 @@ --- title: Rotate consumer secret -sidebarTitle: Post +sidebarTitle: Rotate consumer secret description: Generates and returns a new Chainhooks event payload consumer secret full: true --- diff --git a/content/docs/en/resources/archive/download-guide.mdx b/content/docs/en/resources/archive/download-guide.mdx index a78699448..4e09e4433 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/using-pyth-price-feeds.mdx b/content/docs/en/resources/guides/using-pyth-price-feeds.mdx index 8e7d40811..a1fc297c0 100644 --- a/content/docs/en/resources/guides/using-pyth-price-feeds.mdx +++ b/content/docs/en/resources/guides/using-pyth-price-feeds.mdx @@ -355,7 +355,7 @@ Batch multiple price updates when possible: ### Common issues - + VAA verification fails 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..dcba7f8a9 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 @@ -185,4 +187,4 @@ $ curl http://localhost:3000/ordinals/v1/inscriptions/0 :::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-sdk)/edit-update.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx index ffe918503..cea831da1 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx @@ -3,42 +3,15 @@ title: Edit & Update description: Modify existing chainhooks using the SDK --- -# Edit & Update +Modify existing chainhooks including filters, actions, and options. The Platform UI doesn't support editing - use the SDK instead. -Learn how to update existing chainhooks using the Chainhook SDK. Updates allow you to modify filters, actions, and options without recreating the chainhook. - -:::callout{type="info"} -**Platform Limitation**: The Platform UI does not currently support editing chainhooks. You must use the SDK or API to make updates. +:::callout +The Platform UI does not currently support editing chainhooks. You must use the SDK or API to make updates. ::: ## updateChainhook -Updates an existing chainhook by UUID. You can modify any mutable fields, but `chain` and `network` cannot be changed. - -### Basic Update - -```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', - }, - ], - }, -}); -``` - -### Immutable vs Mutable Fields +### Mutable vs Immutable Fields | Mutable (Can Update) | Immutable (Cannot Update) | |---------------------|---------------------------| @@ -47,88 +20,42 @@ await client.updateChainhook('chainhook-uuid', { | `action` | | | `options` | | ---- - -## Additive Updates - -For additive changes (like adding a new event filter), fetch the current definition first: - -### Adding Event Filters +### Basic Update Example ```typescript -// Step 1: Fetch current chainhook -const current = await client.getChainhook('chainhook-uuid'); +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; -// Step 2: Spread existing events and add new one -await client.updateChainhook('chainhook-uuid', { - filters: { - events: [ - ...(current.definition.filters.events ?? []), - { - type: 'contract_call', - contract_identifier: 'SP...XYZ.counter', - function_name: 'decrement', - }, - ], - }, +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, }); -``` -This preserves existing filters while adding new ones. - -### Adding Options - -```typescript -// Fetch current chainhook -const current = await client.getChainhook('chainhook-uuid'); - -// Spread existing options and add/override await client.updateChainhook('chainhook-uuid', { - options: { - ...current.definition.options, - decode_clarity_values: true, - include_contract_abi: true, + name: 'Updated chainhook name', + filters: { + events: [{ type: 'ft_transfer', asset_identifier: 'SP...ABC.token::usdc' }], }, }); ``` ---- - ## Common Update Patterns -### Update Webhook URL - -Change where chainhook events are sent: - -```typescript -await client.updateChainhook('chainhook-uuid', { - action: { - type: 'http_post', - url: 'https://new-server.com/webhooks', - }, -}); -``` +| Update Type | Example | +|-------------|---------| +| **Webhook URL** | `{ action: { type: 'http_post', url: 'https://new-server.com/webhooks' } }` | +| **Name** | `{ name: 'production-ft-tracker' }` | +| **Filters** | `{ filters: { events: [{ type: 'nft_transfer' }] } }` | -### Update Chainhook Name +### Add Event Filter (Preserving Existing) ```typescript -await client.updateChainhook('chainhook-uuid', { - name: 'production-ft-tracker', -}); -``` - -### Replace All Filters - -Completely replace the filter configuration: +const current = await client.getChainhook('chainhook-uuid'); -```typescript await client.updateChainhook('chainhook-uuid', { filters: { events: [ - { - type: 'nft_transfer', - asset_identifier: 'SP...COLL.nft::collectible', - }, + ...(current.definition.filters.events ?? []), + { type: 'contract_call', contract_identifier: 'SP...XYZ.counter' }, ], }, }); @@ -139,137 +66,41 @@ await client.updateChainhook('chainhook-uuid', { ```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, - include_block_metadata: true, - }, + filters: { events: [{ type: 'stx_transfer', sender: 'SP...SENDER' }] }, + action: { type: 'http_post', url: 'https://new-url.com/webhooks' }, + options: { decode_clarity_values: true }, }); ``` ---- - -## cURL Examples - -### Update Name - -```bash -curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ - "name": "Updated chainhook name" - }' -``` +## cURL Example -### Update Filters +### Update Chainhook ```bash curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/" \ -H "content-type: application/json" \ -H "x-api-key: $HIRO_API_KEY" \ -d '{ - "filters": { - "events": [ - { - "type": "ft_transfer", - "asset_identifier": "SP...ABC.token::usdc" - } - ] - } + "name": "Updated chainhook name", + "action": { "type": "http_post", "url": "https://new-server.com/webhooks" } }' ``` -### Update Action URL - -```bash -curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ - "action": { - "type": "http_post", - "url": "https://new-server.com/webhooks" - } - }' -``` - -### Update Options - -```bash -curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ - "options": { - "decode_clarity_values": true, - "include_contract_abi": true - } - }' -``` - ---- - -## Response - -Successful updates return HTTP `204 No Content`. +### Response -To verify the update, fetch the chainhook: +- **Success**: HTTP `204 No Content` +- **Verify**: Fetch the chainhook to confirm changes ```typescript await client.updateChainhook('chainhook-uuid', { name: 'New name' }); - -// Verify const updated = await client.getChainhook('chainhook-uuid'); -console.log('Updated name:', updated.definition.name); +console.log('Updated:', updated.definition.name); ``` ---- - -## deleteChainhook - -Remove a chainhook permanently. This cannot be undone. - -### TypeScript - -```typescript -await client.deleteChainhook('chainhook-uuid'); - -console.log('Chainhook deleted'); -``` - -### cURL - -```bash -curl -sS -X DELETE "https://api.testnet.hiro.so/chainhooks/me/" \ - -H "x-api-key: $HIRO_API_KEY" -``` - -### Response - -Returns HTTP `204 No Content` on success. - ---- - -## Best Practices - -### 1. Fetch Before Additive Updates - -Always fetch the current definition for additive updates: +### Example: Safe Additive Update ```typescript -// ✅ Good: Fetch first for additive updates +// ✅ Good: Fetch first const current = await client.getChainhook(uuid); await client.updateChainhook(uuid, { filters: { @@ -277,226 +108,13 @@ await client.updateChainhook(uuid, { }, }); -// ❌ Bad: Might overwrite existing filters +// ❌ Bad: Might overwrite await client.updateChainhook(uuid, { - filters: { - events: [newEvent], - }, + filters: { events: [newEvent] }, }); ``` -### 2. Validate Updates - -Verify updates were applied: - -```typescript -await client.updateChainhook(uuid, { name: 'New name' }); - -const updated = await client.getChainhook(uuid); -if (updated.definition.name !== 'New name') { - throw new Error('Update failed'); -} -``` - -### 3. Disable Before Major Updates - -Disable the chainhook before making significant changes: - -```typescript -// Disable while updating -await client.enableChainhook(uuid, false); - -// Make updates -await client.updateChainhook(uuid, { - filters: { /* new filters */ }, - action: { /* new action */ }, -}); - -// Re-enable -await client.enableChainhook(uuid, true); -``` - -### 4. Use Descriptive Names - -Update names to reflect the chainhook's purpose: - -```typescript -await client.updateChainhook(uuid, { - name: `${environment}-${feature}-${version}`, -}); -// Example: "production-ft-tracker-v2" -``` - -### 5. Document Changes - -Store update history in your database: - -```typescript -await client.updateChainhook(uuid, { /* updates */ }); - -// Log the change -await db.chainhookUpdates.create({ - chainhookUuid: uuid, - timestamp: new Date(), - changes: { /* what changed */ }, - updatedBy: userId, -}); -``` - ---- - -## Error Handling - -### Chainhook Not Found - -```typescript -try { - await client.updateChainhook('invalid-uuid', { name: 'New name' }); -} catch (error) { - if (error.status === 404) { - console.error('Chainhook not found'); - } -} -``` - -### Invalid Update - -```typescript -try { - await client.updateChainhook(uuid, { - chain: 'bitcoin', // Immutable field - }); -} catch (error) { - if (error.status === 400) { - console.error('Invalid update:', error.message); - } -} -``` - ---- - -## Migration Example - -Migrate all chainhooks from one webhook URL to another: - -```typescript -async function migrateWebhookUrl(oldUrl: string, newUrl: string) { - // Get all chainhooks - const allChainhooks = await client.getChainhooks({ limit: 100 }); - - // Filter by old URL - const toMigrate = allChainhooks.results.filter( - ch => ch.definition.action.url === oldUrl - ); - - console.log(`Migrating ${toMigrate.length} chainhooks...`); - - // Update each one - for (const ch of toMigrate) { - await client.updateChainhook(ch.uuid, { - action: { - type: 'http_post', - url: newUrl, - }, - }); - console.log(`✅ Migrated: ${ch.definition.name}`); - } - - console.log('Migration complete!'); -} - -await migrateWebhookUrl( - 'https://old-server.com/webhooks', - 'https://new-server.com/webhooks' -); -``` - -### Safer Migration with Bulk Operations - -For safer migrations, use `bulkEnableChainhooks` to disable chainhooks during updates, then re-enable them: - -```typescript -async function safeWebhookMigration(oldUrl: string, newUrl: string) { - // Step 1: Disable all chainhooks for the old URL - console.log('Disabling chainhooks...'); - await client.bulkEnableChainhooks({ - enabled: false, - filters: { - webhook_url: oldUrl, - }, - }); - - // Step 2: Update webhook URLs - console.log('Updating webhook URLs...'); - const allChainhooks = await client.getChainhooks({ limit: 100 }); - const toMigrate = allChainhooks.results.filter( - ch => ch.definition.action.url === oldUrl - ); - - for (const ch of toMigrate) { - await client.updateChainhook(ch.uuid, { - action: { - type: 'http_post', - url: newUrl, - }, - }); - console.log(`✅ Updated: ${ch.definition.name}`); - } - - // Step 3: Re-enable all chainhooks with new URL - console.log('Re-enabling chainhooks...'); - await client.bulkEnableChainhooks({ - enabled: true, - filters: { - webhook_url: newUrl, - }, - }); - - console.log('Migration complete!'); -} - -await safeWebhookMigration( - 'https://old-server.com/webhooks', - 'https://new-server.com/webhooks' -); -``` - -This approach prevents chainhooks from firing during the migration window, avoiding potential errors or duplicate events. - -### Bulk Operations for Maintenance - -Use bulk enable/disable for maintenance windows: - -```typescript -// Disable all chainhooks during server maintenance -await client.bulkEnableChainhooks({ - enabled: false, - filters: { - webhook_url: 'https://api.myapp.com/webhooks', - }, -}); - -// Perform maintenance... -console.log('Server maintenance in progress...'); - -// Re-enable after maintenance -await client.bulkEnableChainhooks({ - enabled: true, - filters: { - webhook_url: 'https://api.myapp.com/webhooks', - }, -}); -``` - -:::callout{type="info"} -Learn more about bulk operations in the [Register & Enable guide](/tools/chainhook/register-enable#bulkenablechainhooks). -::: - ---- - -## Next Steps - :::next-steps -* [List & Fetch](/tools/chainhook/list-fetch): Retrieve chainhook information before updating -* [Filter Reference](/tools/chainhook/reference/filters): Explore all filter options +- [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 index 0836fdb3c..4ccde2ae5 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/evaluate.mdx +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/evaluate.mdx @@ -3,15 +3,18 @@ title: Evaluate Chainhook description: Test chainhooks against specific past blocks --- -# Evaluate Chainhook - -The `evaluateChainhook` method allows you to test your chainhook configuration against specific past blocks. This is useful for testing, debugging, and historical indexing. +Test your chainhook configuration against specific past blocks for testing, debugging, and historical indexing. ## evaluateChainhook -Evaluate a chainhook against a specific block by height or hash. If the chainhook's filters match any events in the specified block, a webhook payload will be sent to your configured endpoint. +### Evaluation Methods + +| Method | Parameter | Example | +|--------|-----------|---------| +| **By height** | `block_height` | `{ block_height: 100000 }` | +| **By hash** | `index_block_hash` | `{ index_block_hash: '0xa204...' }` | -### By Block Height +### Example ```typescript import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; @@ -26,410 +29,36 @@ await client.evaluateChainhook('chainhook-uuid', { }); ``` -### By Block Hash - -```typescript -await client.evaluateChainhook('chainhook-uuid', { - index_block_hash: '0xa204da7ae59b86a569d66c237721937e1708b72913c6a67abf58360b8f5935b7', -}); -``` - -### Response - -Returns HTTP `204 No Content` on success. If the chainhook filter matches any events in the specified block, a webhook payload will be sent to your configured `action.url`. +Returns HTTP `204 No Content`. If filters match, webhook payload is sent to your `action.url`. ---- - -## cURL Examples - -### Evaluate by Height +### cURL ```bash -curl -sS -X POST "https://api.testnet.hiro.so/chainhooks/me//evaluate" \ - -H "content-type: application/json" \ +curl -X POST \ + "https://api.testnet.hiro.so/chainhooks/me/chainhook-uuid/evaluate" \ -H "x-api-key: $HIRO_API_KEY" \ - -d '{ - "block_height": 100000 - }' -``` - -### Evaluate by Hash - -```bash -curl -sS -X POST "https://api.testnet.hiro.so/chainhooks/me//evaluate" \ -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ - "index_block_hash": "0xa204da7ae59b86a569d66c237721937e1708b72913c6a67abf58360b8f5935b7" - }' + -d '{ "block_height": 100000 }' ``` ---- - ## Use Cases -### 1. Test Chainhook Configuration - -Verify your chainhook works against a known block before enabling it: - -```typescript -// Create chainhook (disabled) -const chainhook = await client.registerChainhook({ - version: '1', - name: 'test-chainhook', - chain: 'stacks', - network: 'testnet', - filters: { - events: [ - { - type: 'ft_transfer', - asset_identifier: 'SP...ABC.token::usdc', - }, - ], - }, - action: { - type: 'http_post', - url: 'https://example.com/webhooks', - }, - // No enable_on_registration -}); - -// Test against a block with known FT transfers -await client.evaluateChainhook(chainhook.uuid, { - block_height: 150000, -}); - -// Check your webhook endpoint for the payload -// If it looks good, enable the chainhook -await client.enableChainhook(chainhook.uuid, true); -``` - -### 2. Historical Indexing - -Process past blocks to build up historical state: - -```typescript -async function indexHistoricalBlocks( - chainhookUuid: string, - startHeight: number, - endHeight: number -) { - console.log(`Indexing blocks ${startHeight} to ${endHeight}...`); - - for (let height = startHeight; height <= endHeight; height++) { - await client.evaluateChainhook(chainhookUuid, { - block_height: height, - }); - - // Log progress every 100 blocks - if (height % 100 === 0) { - console.log(`Processed ${height - startHeight + 1} blocks...`); - } - - // Add delay to avoid rate limits - await new Promise(resolve => setTimeout(resolve, 100)); - } - - console.log('Indexing complete!'); -} - -// Index 1000 blocks -await indexHistoricalBlocks('chainhook-uuid', 100000, 101000); -``` - -### 3. Debug Missed Events - -If you think your chainhook missed an event, evaluate against that specific block: - -```typescript -// User reports missing event at block 155000 -await client.evaluateChainhook('chainhook-uuid', { - block_height: 155000, -}); - -// Check your webhook logs to see if it triggers now -``` - -### 4. Re-process Specific Block - -Re-evaluate a block after fixing your webhook handler: - -```typescript -// Your webhook had a bug, fix it, then re-process -await client.evaluateChainhook('chainhook-uuid', { - block_height: 145000, -}); -``` - -### 5. Backfill Data - -Backfill data after creating a new chainhook: - -```typescript -// Create new chainhook for NFT transfers -const chainhook = await client.registerChainhook({ - version: '1', - name: 'nft-tracker', - chain: 'stacks', - network: 'mainnet', - filters: { - events: [ - { - type: 'nft_transfer', - asset_identifier: 'SP...COLL.nft::collectible', - }, - ], - }, - action: { - type: 'http_post', - url: 'https://example.com/webhooks', - }, - options: { - enable_on_registration: true, - }, -}); - -// Backfill last 30 days (~4320 blocks) -const currentHeight = 200000; // Get from API -const startHeight = currentHeight - 4320; - -for (let height = startHeight; height < currentHeight; height++) { - await client.evaluateChainhook(chainhook.uuid, { - block_height: height, - }); - await new Promise(resolve => setTimeout(resolve, 100)); // Rate limit -} -``` - ---- - -## Advanced Patterns - -### Batch Evaluation with Concurrency - -Process multiple blocks concurrently: - -```typescript -async function evaluateBatch( - chainhookUuid: string, - heights: number[], - concurrency = 5 -) { - // Split into batches - const batches = []; - for (let i = 0; i < heights.length; i += concurrency) { - batches.push(heights.slice(i, i + concurrency)); - } - - // Process each batch - for (const batch of batches) { - await Promise.all( - batch.map(height => - client.evaluateChainhook(chainhookUuid, { block_height: height }) - ) - ); - console.log(`Processed ${batch.length} blocks`); - } -} - -// Evaluate blocks 100000-100100 with concurrency of 5 -const heights = Array.from({ length: 101 }, (_, i) => 100000 + i); -await evaluateBatch('chainhook-uuid', heights, 5); -``` - -### Evaluate with Error Handling - -Handle errors gracefully during bulk evaluation: - -```typescript -async function safeEvaluate( - chainhookUuid: string, - height: number -): Promise<{ height: number; success: boolean; error?: string }> { - try { - await client.evaluateChainhook(chainhookUuid, { block_height: height }); - return { height, success: true }; - } catch (error) { - return { - height, - success: false, - error: error.message, - }; - } -} - -// Evaluate range with error tracking -const results = await Promise.all( - [100000, 100001, 100002].map(height => - safeEvaluate('chainhook-uuid', height) - ) -); - -const failed = results.filter(r => !r.success); -if (failed.length > 0) { - console.error('Failed blocks:', failed); -} -``` - -### Resume Interrupted Indexing - -Track progress and resume if interrupted: - -```typescript -async function indexWithCheckpoint( - chainhookUuid: string, - startHeight: number, - endHeight: number, - checkpointKey: string -) { - // Load last checkpoint - let currentHeight = loadCheckpoint(checkpointKey) || startHeight; - - try { - while (currentHeight <= endHeight) { - await client.evaluateChainhook(chainhookUuid, { - block_height: currentHeight, - }); - - // Save checkpoint every 10 blocks - if (currentHeight % 10 === 0) { - saveCheckpoint(checkpointKey, currentHeight); - } - - currentHeight++; - } - - // Clear checkpoint when complete - clearCheckpoint(checkpointKey); - } catch (error) { - console.error(`Failed at height ${currentHeight}:`, error); - console.log(`Resume with: indexWithCheckpoint(..., ${currentHeight}, ...)`); - throw error; - } -} -``` - ---- +| 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 | ## Best Practices -### 1. Rate Limiting - -Add delays between requests to avoid rate limits: - -```typescript -for (let height = start; height <= end; height++) { - await client.evaluateChainhook(uuid, { block_height: height }); - await new Promise(resolve => setTimeout(resolve, 100)); // 100ms delay -} -``` - -### 2. Test Before Production - -Always test against a few blocks before bulk evaluation: - -```typescript -// Test on 3 sample blocks first -for (const height of [100000, 100500, 101000]) { - await client.evaluateChainhook(uuid, { block_height: height }); -} - -// Verify webhooks work correctly, then do full range -``` - -### 3. Use Block Height for Ranges - -Block heights are easier for ranges than hashes: - -```typescript -// ✅ Good: Easy to iterate -for (let height = 100000; height <= 101000; height++) { - await client.evaluateChainhook(uuid, { block_height: height }); -} - -// ❌ Bad: Hard to iterate with hashes -``` - -### 4. Monitor Webhook Success - -Track webhook deliveries when evaluating: - -```typescript -// In your webhook handler -app.post('/webhooks', (req, res) => { - const { event, chainhook } = req.body; - const blockHeight = event.apply[0].block_identifier.index; - - console.log(`Received event for block ${blockHeight}`); - - // Process event... - - res.sendStatus(200); -}); -``` - -### 5. Consider Expiration Options - -Use expiration options to avoid processing too many blocks: - -```typescript -const chainhook = await client.registerChainhook({ - // ... config - options: { - expire_after_evaluations: 5000, // Stop after 5000 blocks - }, -}); -``` - ---- - -## Troubleshooting - -### No Webhook Received - -If you don't receive a webhook after evaluation: - -1. **Check filter matches**: The block may not contain matching events -2. **Verify webhook URL**: Ensure your endpoint is accessible -3. **Check chainhook status**: Verify the chainhook exists and is configured correctly - -```typescript -const chainhook = await client.getChainhook(uuid); -console.log('Webhook URL:', chainhook.definition.action.url); -console.log('Filters:', chainhook.definition.filters); -``` - -### Rate Limit Errors - -If you hit rate limits: - -```typescript -// Increase delay between requests -await new Promise(resolve => setTimeout(resolve, 200)); // 200ms instead of 100ms - -// Or reduce concurrency -await evaluateBatch(uuid, heights, 3); // 3 concurrent instead of 5 -``` - -### Block Not Found - -If you get a 404 for a block: - -```typescript -try { - await client.evaluateChainhook(uuid, { block_height: 999999999 }); -} catch (error) { - if (error.status === 404) { - console.error('Block not found - may not exist yet'); - } -} -``` - ---- +| Practice | Example | +|----------|---------| +| **Rate limiting** | Add 100ms delay between requests | +| **Test first** | Evaluate 2-3 sample blocks before bulk processing | +| **Use height** | Easier to iterate than block hashes | -## Next Steps :::next-steps -* [Register & Enable](/tools/chainhook/register-enable): Create chainhooks to evaluate -* [Filter Reference](/tools/chainhook/reference/filters): Configure which events to match +- [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)/index.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/introduction.mdx similarity index 61% rename from content/docs/en/tools/chainhook/(chainhook-sdk)/index.mdx rename to content/docs/en/tools/chainhook/(chainhook-sdk)/introduction.mdx index a23798cee..8f4c5b750 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/index.mdx +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/introduction.mdx @@ -1,45 +1,29 @@ --- -title: Chainhook SDK +title: Introduction description: TypeScript/JavaScript SDK for managing chainhooks programmatically --- -# Chainhook SDK +## Overview -The Chainhook SDK (`@hirosystems/chainhooks-client`) provides a TypeScript/JavaScript client for programmatically managing chainhooks. Use the SDK when you need to create, update, or manage chainhooks from your application code. - -## Why Use the SDK? - -**Use the SDK when you need to:** -- Edit existing chainhooks (not available in Platform UI) -- Automate chainhook operations -- Integrate chainhook management into your application -- Manage chainhooks in CI/CD pipelines -- Handle multiple chainhooks programmatically -- Evaluate chainhooks against past blocks -- Rotate webhook secrets for security - -**Use the [Platform UI](/tools/chainhook/platform-usage) when you need to:** -- Create simple chainhooks visually -- View chainhook status and activity -- Get started quickly without code +The Chainhook SDK (`@hirosystems/chainhooks-client`) provides a TypeScript/JavaScript client for programmatically managing chainhooks. ## Installation ```terminal !! npm - npm install @hirosystems/chainhooks-client + $ npm install @hirosystems/chainhooks-client ``` ```terminal !! yarn - yarn add @hirosystems/chainhooks-client + $ yarn add @hirosystems/chainhooks-client ``` ```terminal !! pnpm - pnpm add @hirosystems/chainhooks-client + $ pnpm add @hirosystems/chainhooks-client ``` ```terminal !! bun - bun add @hirosystems/chainhooks-client + $ bun add @hirosystems/chainhooks-client ``` @@ -83,21 +67,39 @@ console.log('Chainhook created:', chainhook.uuid); ## Base URLs -The SDK provides constants for both networks: +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 -CHAINHOOKS_BASE_URL.testnet // https://api.testnet.hiro.so +// Testnet (for development) +const testnetClient = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); -// Mainnet -CHAINHOOKS_BASE_URL.mainnet // https://api.mainnet.hiro.so +// Mainnet (for production) +const mainnetClient = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, + apiKey: process.env.HIRO_API_KEY!, +}); ``` ## Authentication -All SDK methods require a Hiro API key. Get your API key from the [Hiro Platform](https://platform.hiro.so). +### 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({ @@ -106,8 +108,10 @@ const client = new ChainhooksClient({ }); ``` -:::callout{type="warning"} -**Never commit API keys to version control.** Always use environment variables or secure secret management. +:::callout +type: warn +### API keys +Never commit API keys to version control. Always use environment variables or secure secret management. ::: ## SDK Methods @@ -140,20 +144,22 @@ The SDK provides the following methods: ## TypeScript Support -The SDK is written in TypeScript and provides full type definitions: +### Available Types + +The SDK provides full TypeScript type definitions: ```typescript import type { - ChainhookDefinitionSchema, - ChainhookStatusSchema, - EvaluateChainhookRequest, - BulkEnableChainhooksRequest, + 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 +- [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 index 62712ccc1..7a5ea6f95 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx @@ -70,11 +70,12 @@ curl -sS "https://api.testnet.hiro.so/chainhooks/me?limit=20&offset=0" \ "status": { "enabled": true, "type": "active", - "info": { - "last_evaluated_block_height": 150000, - "number_of_times_triggered": 42, - "number_of_blocks_evaluated": 1000 - } + "last_evaluated_at": 1696432154, + "last_evaluated_block_height": 150000, + "last_occurrence_at": 1696432100, + "last_occurrence_block_height": 149980, + "evaluated_block_count": 1000, + "occurrence_count": 42 } }, // ... more chainhooks @@ -82,35 +83,6 @@ curl -sS "https://api.testnet.hiro.so/chainhooks/me?limit=20&offset=0" \ } ``` -### Pagination Example - -Fetch all chainhooks using pagination: - -```typescript -async function getAllChainhooks() { - const allChainhooks = []; - let offset = 0; - const limit = 100; - - while (true) { - const response = await client.getChainhooks({ limit, offset }); - allChainhooks.push(...response.results); - - // Check if we've fetched all chainhooks - if (offset + limit >= response.total) { - break; - } - - offset += limit; - } - - return allChainhooks; -} - -const allMyChainhooks = await getAllChainhooks(); -console.log(`Found ${allMyChainhooks.length} chainhooks`); -``` - --- ## getChainhook @@ -121,11 +93,6 @@ Retrieve a specific chainhook by UUID. ```typescript const chainhook = await client.getChainhook('be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185'); - -console.log('Name:', chainhook.definition.name); -console.log('Enabled:', chainhook.status.enabled); -console.log('Network:', chainhook.definition.network); -console.log('Times triggered:', chainhook.status.info.number_of_times_triggered); ``` ### cURL @@ -165,182 +132,17 @@ curl -sS "https://api.testnet.hiro.so/chainhooks/me/" \ "status": { "enabled": true, "type": "active", - "info": { - "last_evaluated_block_height": 150000, - "last_occurrence": 149850, - "number_of_times_triggered": 42, - "number_of_blocks_evaluated": 1000 - } - } -} -``` - ---- - -## Understanding Status - -The `status` object provides information about the chainhook's state: - -### Status Fields - -```typescript -{ - "status": { - "enabled": true, // Whether the chainhook is active - "type": "active", // Status type (active, inactive, expired, etc.) - "info": { - "last_evaluated_block_height": 150000, // Most recent block checked - "last_occurrence": 149850, // Most recent block where filter matched - "number_of_times_triggered": 42, // Total matches - "number_of_blocks_evaluated": 1000, // Total blocks processed - "expired_at_block_height": null // When chainhook expired (if applicable) - } + "last_evaluated_at": 1696432154, + "last_evaluated_block_height": 150000, + "last_occurrence_at": 1696432100, + "last_occurrence_block_height": 149980, + "evaluated_block_count": 1000, + "occurrence_count": 42 } } ``` -### Status Types - -| Type | Description | -|------|-------------| -| `active` | Chainhook is enabled and processing blocks | -| `inactive` | Chainhook is disabled (paused) | -| `expired` | Chainhook expired based on options | -| `scanning` | Chainhook is catching up with chain tip | - -### Check if Enabled - -```typescript -const chainhook = await client.getChainhook(uuid); - -if (chainhook.status.enabled) { - console.log('Chainhook is active'); -} else { - console.log('Chainhook is disabled'); -} -``` - -### Check Activity - -```typescript -const chainhook = await client.getChainhook(uuid); -const { info } = chainhook.status; - -console.log(`Triggered ${info.number_of_times_triggered} times`); -console.log(`Evaluated ${info.number_of_blocks_evaluated} blocks`); -console.log(`Last trigger at block ${info.last_occurrence}`); -``` - ---- - -## Common Use Cases - -### Find Chainhooks by Name - -```typescript -const allChainhooks = await client.getChainhooks({ limit: 100 }); - -const myChain hook = allChainhooks.results.find( - ch => ch.definition.name === 'ft-transfer-tracker' -); - -if (myChainhook) { - console.log('Found:', myChainhook.uuid); -} -``` - -### List All Enabled Chainhooks - -```typescript -const allChainhooks = await client.getChainhooks({ limit: 100 }); - -const enabledChainhooks = allChainhooks.results.filter( - ch => ch.status.enabled -); - -console.log(`${enabledChainhooks.length} enabled chainhooks`); -``` - -### Monitor Chainhook Activity - -```typescript -async function monitorChainhook(uuid: string) { - const chainhook = await client.getChainhook(uuid); - const { info } = chainhook.status; - - console.log(` - Chainhook: ${chainhook.definition.name} - Status: ${chainhook.status.enabled ? 'Active' : 'Inactive'} - Last evaluated: Block ${info.last_evaluated_block_height} - Total triggers: ${info.number_of_times_triggered} - Blocks processed: ${info.number_of_blocks_evaluated} - `); -} - -// Call periodically to monitor -setInterval(() => monitorChainhook('uuid-here'), 60000); // Every minute -``` - -### List Chainhooks by Webhook URL - -```typescript -const allChainhooks = await client.getChainhooks({ limit: 100 }); - -const webhookUrl = 'https://example.com/webhooks'; -const matchingChainhooks = allChainhooks.results.filter( - ch => ch.definition.action.url === webhookUrl -); - -console.log(`${matchingChainhooks.length} chainhooks POST to ${webhookUrl}`); -``` - -### Check for Expired Chainhooks - -```typescript -const allChainhooks = await client.getChainhooks({ limit: 100 }); - -const expiredChainhooks = allChainhooks.results.filter( - ch => ch.status.type === 'expired' -); - -for (const ch of expiredChainhooks) { - console.log(`Expired: ${ch.definition.name} at block ${ch.status.info.expired_at_block_height}`); -} -``` - ---- - -## Error Handling - -### Chainhook Not Found - -```typescript -try { - const chainhook = await client.getChainhook('invalid-uuid'); -} catch (error) { - if (error.status === 404) { - console.error('Chainhook not found'); - } else { - throw error; - } -} -``` - -### Handle Pagination Errors - -```typescript -try { - const chainhooks = await client.getChainhooks({ limit: 1000, offset: 0 }); -} catch (error) { - console.error('Failed to fetch chainhooks:', error); -} -``` - ---- - -## Next Steps - :::next-steps -* [Edit & Update](/tools/chainhook/edit-update): Modify existing chainhooks -* [Register & Enable](/tools/chainhook/register-enable): Create new chainhooks +- [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 index 2ef4e237d..ae729e00b 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx @@ -1,489 +1,67 @@ --- title: Manage Keys -description: API key setup and webhook secret management +description: Rotate consumer secrets and validate every Chainhook delivery --- -# Manage Keys +## What you'll learn -Learn how to manage your Hiro API keys and webhook secrets for secure chainhook operations. - -## Hiro API Keys - -API keys are required to authenticate requests to the Chainhook API. Get your API key from the [Hiro Platform](https://platform.hiro.so). - -### Getting 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 - -### Using API Keys with the SDK - -Store your API key securely in environment variables: - -#### Environment Variables - -Create a `.env` file in your project root: - -```bash -HIRO_API_KEY=your_api_key_here -``` - -:::callout{type="warning"} -**Never commit `.env` files to version control.** Add `.env` to your `.gitignore` file. +:::objectives +- Create/rotate a Chainhook consumer secret. +- Validate webhook requests by checking the `Authorization` header. ::: -#### Load Environment Variables - -Use a package like `dotenv` to load environment variables: +## Prerequisites - - ```terminal !! npm - npm install dotenv - ``` +:::prerequisites +- Hiro API key stored as `CHAINHOOKS_API_KEY`. +- Chainhook UUID you want to protect. +- Node.js runtime (the example uses Fastify). +::: - ```terminal !! yarn - yarn add dotenv - ``` +## Validating webhook requests with a consumer secret - ```terminal !! pnpm - pnpm add dotenv - ``` +Chainhooks attach an `Authorization: Bearer ` header to every webhook attempt, giving you a simple shared-secret handshake. - ```terminal !! bun - bun add dotenv - ``` - +1. Rotate the secret with `await client.rotateConsumerSecret(chainhookUuid)` (or the `/chainhooks/{uuid}/secret` API) whenever you need a new token. +2. Persist the returned `secret` in your secret manager and reload it at process start or via a short refresh loop. +3. Reject webhook deliveries whose `Authorization` header does not equal `Bearer `. -Then load in your code: +### Rotate/create consumer secret -```typescript -import 'dotenv/config'; +```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!, // Loaded from .env -}); -``` - -### Authentication Header - -The SDK automatically adds the `x-api-key` header to all requests: - -```typescript -// SDK handles this automatically -await client.getChainhooks(); - -// Equivalent cURL: -// curl -H "x-api-key: YOUR_API_KEY" https://api.testnet.hiro.so/chainhooks/me -``` - -### Using API Keys Directly (cURL) - -For direct API calls, include the `x-api-key` header: - -```bash -curl -sS "https://api.testnet.hiro.so/chainhooks/me" \ - -H "x-api-key: $HIRO_API_KEY" -``` - ---- - -## Webhook Secrets - -Webhook secrets (consumer secrets) are used to verify that webhook payloads are sent by Hiro and not a third party. - -### How Webhook Secrets Work - -1. When you create a chainhook, Hiro generates a unique consumer secret -2. This secret is included in the headers of every webhook payload sent to your endpoint -3. Your server validates the secret to ensure the request is authentic - -### Webhook Secret Header - -Every webhook request includes: - -```http -POST /webhooks HTTP/1.1 -Host: example.com -Content-Type: application/json -X-Chainhook-Consumer-Secret: your-secret-here - -{ - "event": { ... }, - "chainhook": { ... } -} -``` - -### Verifying Webhook Requests - -Validate incoming webhooks by checking the secret: - -```typescript -import express from 'express'; - -const app = express(); -app.use(express.json()); - -// Store your chainhook secrets (from database or config) -const CHAINHOOK_SECRETS = new Map([ - ['chainhook-uuid-1', 'secret-1'], - ['chainhook-uuid-2', 'secret-2'], -]); - -app.post('/webhooks', (req, res) => { - const providedSecret = req.headers['x-chainhook-consumer-secret']; - const chainhookUuid = req.body.chainhook.uuid; - const expectedSecret = CHAINHOOK_SECRETS.get(chainhookUuid); - - // Verify secret - if (providedSecret !== expectedSecret) { - console.error('Invalid webhook secret'); - return res.sendStatus(401); - } - - // Process webhook - const { event, chainhook } = req.body; - console.log('Valid webhook received from:', chainhook.name); - - // Your processing logic here... - - res.sendStatus(200); -}); - -app.listen(3000, () => console.log('Webhook server running')); -``` - ---- - -## rotateConsumerSecret - -Rotate the webhook secret for a chainhook. This generates a new secret that will be used for all future webhook deliveries. - -### TypeScript - -```typescript -const newSecret = await client.rotateConsumerSecret('chainhook-uuid'); - -console.log('New secret:', newSecret.consumer_secret); - -// Store the new secret securely -await db.chainhooks.update({ - uuid: 'chainhook-uuid', - secret: newSecret.consumer_secret, + baseUrl: CHAINHOOKS_BASE_URL.mainnet, // or .testnet / custom URL + apiKey: process.env.CHAINHOOKS_API_KEY!, }); -``` - -### cURL - -```bash -curl -sS -X POST "https://api.testnet.hiro.so/chainhooks/me//rotate-secret" \ - -H "x-api-key: $HIRO_API_KEY" -``` - -### Response - -```json -{ - "consumer_secret": "new-secret-value-here" -} -``` - -### When to Rotate Secrets - -Rotate webhook secrets when: - -1. **Security breach**: A secret has been exposed or compromised -2. **Regular rotation**: Part of your security policy (e.g., every 90 days) -3. **Team changes**: When team members with access leave -4. **Testing**: When you need to test secret rotation - -### Secret Rotation Example - -```typescript -async function rotateSecret(chainhookUuid: string) { - try { - // Rotate the secret - const { consumer_secret } = await client.rotateConsumerSecret(chainhookUuid); - - // Update your database - await db.chainhooks.update({ - uuid: chainhookUuid, - secret: consumer_secret, - lastRotated: new Date(), - }); - - console.log(`✅ Secret rotated for ${chainhookUuid}`); - return consumer_secret; - } catch (error) { - console.error('Failed to rotate secret:', error); - throw error; - } -} -``` - -### Rotating Secrets for All Chainhooks - -```typescript -async function rotateAllSecrets() { - const chainhooks = await client.getChainhooks({ limit: 100 }); - - for (const ch of chainhooks.results) { - const { consumer_secret } = await client.rotateConsumerSecret(ch.uuid); - - // Update storage - await db.chainhooks.update({ - uuid: ch.uuid, - secret: consumer_secret, - }); - - console.log(`✅ Rotated: ${ch.definition.name}`); - - // Rate limit - await new Promise(resolve => setTimeout(resolve, 100)); - } - console.log('All secrets rotated'); -} +let consumerSecret: string = await client.rotateConsumerSecret(chainhookUuid).secret; ``` ---- - -## Best Practices - -### 1. Store Secrets Securely - -Never hardcode secrets in your application: - -```typescript -// ❌ Bad: Hardcoded secret -const SECRET = 'my-secret-here'; - -// ✅ Good: From environment or secure storage -const SECRET = process.env.WEBHOOK_SECRET; - -// ✅ Better: From encrypted database -const SECRET = await getSecretFromDatabase(chainhookUuid); -``` - -### 2. Use Environment Variables - -Keep sensitive data in environment variables: - -```bash -# .env file -HIRO_API_KEY=your_api_key -WEBHOOK_SECRET_1=secret_for_chainhook_1 -WEBHOOK_SECRET_2=secret_for_chainhook_2 -``` - -Load with: - -```typescript -import 'dotenv/config'; - -const apiKey = process.env.HIRO_API_KEY; -``` - -### 3. Rotate Secrets Regularly - -Implement a rotation schedule: - -```typescript -// Rotate secrets every 90 days -async function scheduleSecretRotation() { - const chainhooks = await client.getChainhooks({ limit: 100 }); - - for (const ch of chainhooks.results) { - const lastRotated = await db.getLastRotationDate(ch.uuid); - const daysSinceRotation = (Date.now() - lastRotated) / (1000 * 60 * 60 * 24); +### Example Fastify server - if (daysSinceRotation >= 90) { - await rotateSecret(ch.uuid); - } - } -} - -// Run daily -setInterval(scheduleSecretRotation, 24 * 60 * 60 * 1000); -``` - -### 4. Validate All Incoming Webhooks - -Always verify webhook secrets before processing: - -```typescript -function validateWebhook(req: Request): boolean { - const providedSecret = req.headers['x-chainhook-consumer-secret']; - const chainhookUuid = req.body.chainhook?.uuid; - - if (!providedSecret || !chainhookUuid) { - return false; - } - - const expectedSecret = getStoredSecret(chainhookUuid); - return providedSecret === expectedSecret; -} - -app.post('/webhooks', (req, res) => { - if (!validateWebhook(req)) { - return res.sendStatus(401); - } - - // Process webhook... -}); -``` - -### 5. Handle Secret Rotation Gracefully +```ts +import Fastify from 'fastify'; -During rotation, briefly accept both old and new secrets: +const server = Fastify(); -```typescript -// Store both current and previous secret -interface ChainhookSecrets { - current: string; - previous?: string; - rotatedAt?: Date; -} - -function validateWebhook(req: Request): boolean { - const providedSecret = req.headers['x-chainhook-consumer-secret']; - const secrets = getStoredSecrets(req.body.chainhook.uuid); - - // Accept current or previous secret (for 5 minutes after rotation) - if (providedSecret === secrets.current) { - return true; +server.post('/webhook', async (request, reply) => { + if (!consumerSecret) { + reply.code(503).send({ error: 'consumer secret unavailable' }); + return; } - if (secrets.previous && secrets.rotatedAt) { - const minutesSinceRotation = (Date.now() - secrets.rotatedAt.getTime()) / (1000 * 60); - if (minutesSinceRotation < 5) { - return providedSecret === secrets.previous; - } - } - - return false; -} -``` - -### 6. Monitor Failed Validations - -Track and alert on failed secret validations: - -```typescript -app.post('/webhooks', (req, res) => { - if (!validateWebhook(req)) { - // Log the failure - logger.warn('Webhook validation failed', { - chainhookUuid: req.body.chainhook?.uuid, - ip: req.ip, - timestamp: new Date(), - }); - - // Alert if too many failures - await checkForSecurityThreats(req.body.chainhook?.uuid); - - return res.sendStatus(401); + const authHeader = request.headers.authorization; + if (authHeader !== `Bearer ${consumerSecret}`) { + reply.code(401).send({ error: 'invalid consumer secret' }); + return; } - // Process webhook... -}); -``` - ---- - -## Security Considerations - -### API Key Security - -- **Never commit API keys** to version control -- **Use environment variables** for local development -- **Use secret managers** for production (AWS Secrets Manager, HashiCorp Vault, etc.) -- **Rotate keys periodically** as part of security policy -- **Use different keys** for different environments (dev, staging, production) - -### Webhook Secret Security - -- **Always validate** webhook secrets on your server -- **Rotate secrets** if you suspect compromise -- **Log validation failures** for security monitoring -- **Use HTTPS** for webhook endpoints -- **Implement rate limiting** to prevent abuse - -### Production Setup - -For production, use a secret manager: - -```typescript -import { SecretsManager } from 'aws-sdk'; - -const secretsManager = new SecretsManager(); - -async function getApiKey(): Promise { - const secret = await secretsManager.getSecretValue({ - SecretId: 'hiro-api-key-production', - }).promise(); - - return JSON.parse(secret.SecretString!).apiKey; -} - -const client = new ChainhooksClient({ - baseUrl: CHAINHOOKS_BASE_URL.mainnet, - apiKey: await getApiKey(), + const event = request.body; + console.log(`received chainhook ${event.chainhook.uuid}`); + reply.code(204).send(); }); -``` - ---- -## Troubleshooting - -### 401 Unauthorized - -If you get 401 errors: - -```typescript -// Check API key is set -console.log('API Key:', process.env.HIRO_API_KEY ? 'Set' : 'Missing'); - -// Verify it's correct -try { - await client.getChainhooks(); - console.log('✅ API key valid'); -} catch (error) { - if (error.status === 401) { - console.error('❌ Invalid API key'); - } -} +await server.listen({ port: Number(process.env.PORT) || 3000 }); ``` - -### Webhook Validation Failing - -If webhook validation fails: - -1. Check the secret matches: - ```typescript - console.log('Provided:', req.headers['x-chainhook-consumer-secret']); - console.log('Expected:', storedSecret); - ``` - -2. Verify the chainhook UUID: - ```typescript - console.log('Chainhook UUID:', req.body.chainhook.uuid); - ``` - -3. Check if secret was recently rotated: - ```typescript - const lastRotated = await db.getLastRotationDate(uuid); - console.log('Last rotated:', lastRotated); - ``` - ---- - -## Next Steps - -:::next-steps -* [Register & Enable](/tools/chainhook/register-enable): Create chainhooks with your API key -* [Quickstart](/tools/chainhook/quickstart): Complete end-to-end example -::: diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json b/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json index c14c21b64..6f7b2996a 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json @@ -1,6 +1,8 @@ { + "title": "Chainhook SDK", "pages": [ - "index", + "---Chainhook SDK---", + "introduction", "quickstart", "register-enable", "list-fetch", diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/quickstart.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/quickstart.mdx deleted file mode 100644 index caa9beeaf..000000000 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/quickstart.mdx +++ /dev/null @@ -1,275 +0,0 @@ ---- -title: SDK Quickstart -description: Get started with the Chainhook SDK in 5 minutes ---- - -# SDK Quickstart - -This guide will help you create and enable your first chainhook using the Chainhook SDK in just a few minutes. - -## Prerequisites - -- Node.js 16+ installed -- A Hiro API key (get one from [Hiro Platform](https://platform.hiro.so)) -- A webhook endpoint to receive events (you can use [ngrok](https://ngrok.com/) or [webhook.site](https://webhook.site) for testing) - -## Step 1: Install the SDK - - - ```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 - ``` - - -## Step 2: Set Up Environment Variables - -Create a `.env` file in your project root: - -```bash -HIRO_API_KEY=your_api_key_here -WEBHOOK_URL=https://your-webhook-url.com/chainhook -``` - -:::callout{type="warning"} -Never commit your `.env` file to version control. Add it to `.gitignore`. -::: - -## Step 3: Initialize the Client - -Create a file called `chainhook-example.ts` (or `.js`): - -```typescript -import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; - -const client = new ChainhooksClient({ - baseUrl: CHAINHOOKS_BASE_URL.testnet, // Use testnet for development - apiKey: process.env.HIRO_API_KEY!, -}); -``` - -## Step 4: Create Your First Chainhook - -Let's create a chainhook that triggers on FT transfers: - -```typescript -async function createChainhook() { - const chainhook = await client.registerChainhook({ - version: '1', - name: 'my-first-chainhook', - chain: 'stacks', - network: 'testnet', - filters: { - events: [ - { - type: 'ft_transfer', - }, - ], - }, - action: { - type: 'http_post', - url: process.env.WEBHOOK_URL!, - }, - options: { - decode_clarity_values: true, - enable_on_registration: true, // Enable immediately - }, - }); - - console.log('✅ Chainhook created and enabled!'); - console.log('UUID:', chainhook.uuid); - console.log('Status:', chainhook.status); - - return chainhook; -} - -createChainhook().catch(console.error); -``` - -## Step 5: Run Your Code - -Execute your script: - - - ```terminal !! Node.js - node chainhook-example.js - ``` - - ```terminal !! TypeScript - ts-node chainhook-example.ts - ``` - - ```terminal !! Bun - bun run chainhook-example.ts - ``` - - -You should see output like: - -``` -✅ Chainhook created and enabled! -UUID: be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185 -Status: { enabled: true, type: 'active', ... } -``` - -## Step 6: Verify Your Chainhook - -Check that your chainhook is active: - -```typescript -async function verifyChainhook(uuid: string) { - const chainhook = await client.getChainhook(uuid); - - console.log('Name:', chainhook.definition.name); - console.log('Enabled:', chainhook.status.enabled); - console.log('Network:', chainhook.definition.network); -} -``` - -## Step 7: Receive Webhook Events - -When FT transfers occur on Stacks testnet, your webhook endpoint will receive payloads like: - -```json -{ - "event": { - "apply": [ - { - "transactions": [ - { - "operations": [ - { - "type": "token_transfer", - "amount": { "value": "1000000" }, - "account": { "address": "SP..." } - } - ] - } - ] - } - ] - }, - "chainhook": { - "name": "my-first-chainhook", - "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185" - } -} -``` - -## Complete Example - -Here's a complete working example: - -```typescript -import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; -import 'dotenv/config'; - -const client = new ChainhooksClient({ - baseUrl: CHAINHOOKS_BASE_URL.testnet, - apiKey: process.env.HIRO_API_KEY!, -}); - -async function main() { - try { - // Create and enable chainhook - const chainhook = await client.registerChainhook({ - version: '1', - name: 'ft-transfer-tracker', - chain: 'stacks', - network: 'testnet', - filters: { - events: [ - { - type: 'ft_transfer', - }, - ], - }, - action: { - type: 'http_post', - url: process.env.WEBHOOK_URL!, - }, - options: { - decode_clarity_values: true, - enable_on_registration: true, - }, - }); - - console.log('✅ Chainhook created!'); - console.log('UUID:', chainhook.uuid); - console.log('Enabled:', chainhook.status.enabled); - - // Verify it was created - const retrieved = await client.getChainhook(chainhook.uuid); - console.log('✅ Verified chainhook:', retrieved.definition.name); - - // List all your chainhooks - const allChainhooks = await client.getChainhooks({ limit: 10 }); - console.log(`📋 You have ${allChainhooks.total} chainhook(s)`); - - } catch (error) { - console.error('❌ Error:', error); - } -} - -main(); -``` - -## Testing Your Webhook - -To test your webhook endpoint locally: - -1. **Use ngrok** to expose your local server: - ```bash - ngrok http 3000 - ``` - -2. **Use webhook.site** to inspect incoming payloads: - - Visit [webhook.site](https://webhook.site) - - Copy the unique URL - - Use it as your `WEBHOOK_URL` - -3. **Trigger a test event** on Stacks testnet (make an FT transfer) - -## Next Steps - -Now that you have a basic chainhook running, explore more advanced features: - -:::next-steps -* [Register & Enable](/tools/chainhook/register-enable): Learn about all registration options -* [Filter Reference](/tools/chainhook/reference/filters): Explore all available event filters -::: - -## Troubleshooting - -### API Key Error - -If you see `401 Unauthorized`: -- Verify your API key is correct -- Ensure it's properly set in environment variables -- Check that you're using the correct base URL (testnet vs mainnet) - -### Webhook Not Receiving Events - -If your webhook isn't receiving payloads: -- Verify the webhook URL is publicly accessible -- Check that `enable_on_registration: true` is set -- Ensure your filter matches actual blockchain events -- Verify the chainhook status with `getChainhook()` - -### TypeScript Errors - -If you encounter TypeScript errors: -```bash -npm install --save-dev @types/node -``` diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx index 1caae1bcb..5ae15bffb 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx @@ -258,84 +258,7 @@ curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/enabled" \ } ``` ---- - -## Best Practices - -### 1. Enable on Registration for Production - -For production chainhooks, enable immediately to avoid missing events: - -```typescript -{ - options: { - enable_on_registration: true, - } -} -``` - -### 2. Test Before Enabling - -For testing, create disabled and [evaluate against known blocks](/tools/chainhook/evaluate) first: - -```typescript -// Create disabled -const chainhook = await client.registerChainhook({ - // ... config - // No enable_on_registration -}); - -// Test against a known block -await client.evaluateChainhook(chainhook.uuid, { - block_height: 150000, -}); - -// If working as expected, enable it -await client.enableChainhook(chainhook.uuid, true); -``` - -### 3. Use Bulk Operations for Multiple Chainhooks - -When managing many chainhooks, use bulk operations: - -```typescript -// Disable all chainhooks for a specific webhook during maintenance -await client.bulkEnableChainhooks({ - enabled: false, - filters: { - webhook_url: 'https://api.myapp.com/webhooks', - }, -}); - -// Re-enable after maintenance -await client.bulkEnableChainhooks({ - enabled: true, - filters: { - webhook_url: 'https://api.myapp.com/webhooks', - }, -}); -``` - -### 4. Store UUIDs - -Always store the returned UUID for later reference: - -```typescript -const chainhook = await client.registerChainhook({ /* ... */ }); - -// Store this in your database -await db.chainhooks.create({ - uuid: chainhook.uuid, - name: chainhook.definition.name, - enabled: chainhook.status.enabled, -}); -``` - ---- - -## Next Steps - :::next-steps -* [Evaluate](/tools/chainhook/evaluate): Test chainhooks against past blocks -* [Filter Reference](/tools/chainhook/reference/filters): Explore all filter options +- [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/(overview)/faq.mdx b/content/docs/en/tools/chainhook/(overview)/faq.mdx index 2220f321f..3d7fe6d8c 100644 --- a/content/docs/en/tools/chainhook/(overview)/faq.mdx +++ b/content/docs/en/tools/chainhook/(overview)/faq.mdx @@ -3,133 +3,172 @@ title: FAQ description: Frequently asked questions about Chainhook 2.0 Beta --- -# Frequently Asked Questions - ## Chainhook 2.0 Beta -### What is the goal/purpose of the 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 [support@hiro.so](mailto:support@hiro.so). + + -### Is the Chainhook 2.0 Beta free? - + + 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? - + + 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? - + + 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? - + + + 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 or via the API during and after the Beta period will run on v2.0. The API will also not support Chainhooks running on v1.0. -:::callout{type="info"} +:::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 access both and what will change/stay the same? - + + If v2.0 and v1.0 will be live at the same time, how do we access 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. - ---- + + + ## Platform vs SDK -### Can I edit my v2 Chainhooks? - + + + 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. - -:::callout{type="warning"} -When creating a chainhook in the Platform, make sure all details are correct before creating it. You will not be able to edit it later in the Platform UI - you'll need to delete and recreate it, or use the SDK/API to make updates. -::: - -### 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 - ---- + + + + + 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](https://platform.hiro.so) -2. Sign in or create an account -3. Navigate to the API Keys section -4. Generate or copy your API key - -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: - -```typescript -import { CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + + + 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
-```
-
----
+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** - ```typescript - 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](/tools/chainhook/reference/filters) 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: - -```typescript -// Verify API key is set + + + 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');
 }
@@ -140,15 +179,17 @@ try {
   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:
-
-```typescript
-// Create chainhook (disabled by default)
+}`}
+
+
+
+ + + 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
@@ -161,21 +202,23 @@ await client.evaluateChainhook(chainhook.uuid, {
 
 // 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:
-
-```typescript
-app.post('/webhooks', async (req, res) => {
+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`);
+    console.log(\`Block \${blockHeight} already processed, skipping\`);
     return res.sendStatus(200);
   }
 
@@ -184,29 +227,38 @@ app.post('/webhooks', async (req, res) => {
 
   // Return 200 immediately
   res.sendStatus(200);
-});
-```
-
----
+});`}
+
+
+
+
## Common Questions -### Can I use chainhooks for real-time notifications? - + + + 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](/tools/chainhook/usage#handling-blockchain-reorganizations). - -### 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: - -```typescript -{ + + + + + 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' },
@@ -214,24 +266,32 @@ Yes! You can include multiple filters in the `events` array. The chainhook will
       { type: 'contract_call', contract_identifier: 'SP...DEX.swap' },
     ],
   }
-}
-```
-
-### What happens if my webhook endpoint is down?
-
+}`}
+
+
+
+ + + 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](https://ngrok.com/) or [webhook.site](https://webhook.site) to expose your local development server: - -```bash -# Expose local port 3000 + + + + + 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
-```
+# https://abc123.ngrok-free.app/webhooks`}
+
+
+
+
--- @@ -247,6 +307,6 @@ ngrok http 3000 ## Next Steps :::next-steps -* [Migration Guide](/tools/chainhook/migration): Migrate from v1 to v2 -* [SDK Quickstart](/tools/chainhook/quickstart): Get started with the SDK +- [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 cf9bb582e..1d584866a 100644 --- a/content/docs/en/tools/chainhook/(overview)/meta.json +++ b/content/docs/en/tools/chainhook/(overview)/meta.json @@ -1,7 +1,4 @@ { - "pages": [ - "usage", - "migration", - "faq" - ] + "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 index 69423e7bd..666cc2bd8 100644 --- a/content/docs/en/tools/chainhook/(overview)/migration.mdx +++ b/content/docs/en/tools/chainhook/(overview)/migration.mdx @@ -3,196 +3,109 @@ title: Migrating from v1 to v2 description: Guide for migrating legacy v1 chainhooks to Chainhook 2.0 Beta --- -# Migrating from v1 to v2 - -This guide helps you migrate your legacy v1 chainhooks to the new Chainhook 2.0 Beta. Learn how to manage v1 chainhooks programmatically and recreate them using v2. - -## Overview - -### What Changed - -**Chainhook v1** used the Hiro Platform API with predicate-based JSON files and CLI tools. - -**Chainhook v2** introduces: -- New REST API with simpler endpoint structure -- TypeScript/JavaScript SDK for programmatic management -- Updated filter syntax and options -- Improved Platform UI (creation only, no editing yet) - -### Platform API vs Chainhook API - -There are now **two separate APIs** for managing chainhooks: - -| API | Purpose | Base URL | Auth | -|-----|---------|----------|------| -| **Platform API** | Manage legacy v1 chainhooks | `https://api.platform.hiro.so` | API key in path | -| **Chainhook API** | Manage v2 chainhooks | `https://api.testnet.hiro.so` or `https://api.mainnet.hiro.so` | `x-api-key` header | - -:::callout{type="info"} -**v1 chainhooks are read-only in the Platform UI.** You must use the Platform API to manage them programmatically. +:::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). ::: ---- - -## Migration Strategy +## What you'll learn -### Recommended Approach - -1. **Audit v1 chainhooks** - List all your v1 chainhooks via Platform API -2. **Recreate in v2** - Use the new Chainhook SDK/API to create equivalent v2 chainhooks -3. **Test v2 chainhooks** - Verify v2 chainhooks work as expected -4. **Delete v1 chainhooks** - Remove old v1 chainhooks after successful migration - -:::callout{type="warning"} -**Do not delete v1 chainhooks until you've verified v2 replacements are working correctly.** +:::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 -## Step 1: List v1 Chainhooks +:::prerequisites +- API access to both the Platform API and Chainhook REST API (same `HIRO_API_KEY`). +- Local SDK (`@hirosystems/chainhooks-client`) or `curl` for REST calls. +- Environment variable `HIRO_API_KEY` set for the CLI and code samples. +::: -Use the Platform API to retrieve all your existing v1 chainhooks. + -### cURL + +### Inventory v1 chainhooks +Use the Platform API to fetch every chainhook that still fires in production. +#### CLI ```bash curl -sS "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks" \ -H "content-type: application/json" ``` -### JavaScript/TypeScript - +#### TypeScript ```typescript -async function listV1Chainhooks() { +export async function listV1Chainhooks() { const response = await fetch( `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks`, - { - headers: { - 'content-type': 'application/json', - }, - } + { headers: { 'content-type': 'application/json' } } ); - if (!response.ok) { - throw new Error(`Failed to list chainhooks: ${response.statusText}`); - } - - const chainhooks = await response.json(); - return chainhooks; + if (!response.ok) throw new Error(response.statusText); + return (await response.json()) as any[]; } -const v1Chainhooks = await listV1Chainhooks(); -console.log(`Found ${v1Chainhooks.length} v1 chainhooks`); +const chainhooks = await listV1Chainhooks(); +console.log(`Found ${chainhooks.length} v1 chainhooks`); ``` + -### Response Example - -```json -[ - { - "uuid": "aa3626dc-2090-49cd-8f1e-8f9994393aed", - "name": "my-v1-chainhook", - "version": 1, - "chain": "stacks", - "networks": { - "mainnet": { - "if_this": { - "scope": "stx_event", - "actions": ["transfer"] - }, - "then_that": { - "http_post": { - "url": "https://example.com/webhooks", - "authorization_header": "Bearer token" - } - } - } - } - } -] -``` - ---- - -## Step 2: Get v1 Chainhook Details - -Retrieve detailed information about a specific v1 chainhook. - -### cURL + +### Inspect a chainhook +Pull the full definition for each UUID so you can convert custom filters and metadata. +#### CLI ```bash -curl -sS "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks/$CHAINHOOK_UUID" \ +curl -sS \ + "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks/$CHAINHOOK_UUID" \ -H "content-type: application/json" ``` -### JavaScript/TypeScript - +#### TypeScript ```typescript -async function getV1Chainhook(uuid: string) { +export async function getV1Chainhook(uuid: string) { const response = await fetch( `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks/${uuid}`, - { - headers: { - 'content-type': 'application/json', - }, - } + { headers: { 'content-type': 'application/json' } } ); - if (!response.ok) { - throw new Error(`Failed to get chainhook: ${response.statusText}`); - } - + if (!response.ok) throw new Error(response.statusText); return await response.json(); } - -const v1Chainhook = await getV1Chainhook('aa3626dc-2090-49cd-8f1e-8f9994393aed'); -console.log('v1 Chainhook:', v1Chainhook); ``` - ---- - -## Step 3: Create Equivalent v2 Chainhooks - -Recreate your v1 chainhooks using the v2 SDK or API with updated syntax. - -### v1 to v2 Mapping - -| v1 Concept | v2 Equivalent | -|------------|---------------| -| `if_this.scope` | `filters.events[].type` | -| `if_this.actions` | Part of event type (e.g., `ft_transfer`) | -| `then_that.http_post` | `action.type: "http_post"` | -| `networks.mainnet` | `network: "mainnet"` | -| `authorization_header` | Removed (use webhook secrets) | - -### Example Migration - -**v1 Chainhook (Platform API format)**: -```json + + + +### Map configuration to v2 +Translate v1 structures to v2 fields before provisioning new hooks. + +| v1 Concept | v2 Target | 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 Conversion +```jsonc +// v1 (Platform API) { "name": "stx-transfers", - "version": 1, - "chain": "stacks", "networks": { "mainnet": { - "if_this": { - "scope": "stx_event", - "actions": ["transfer"] - }, - "then_that": { - "http_post": { - "url": "https://example.com/webhooks", - "authorization_header": "Bearer token" - } - }, - "start_block": 100000 + "if_this": { "scope": "stx_event", "actions": ["transfer"] }, + "then_that": { "http_post": { "url": "https://example.com/webhooks" } } } } } ``` -**v2 Chainhook (SDK)**: ```typescript +// v2 (SDK) import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; const client = new ChainhooksClient({ @@ -200,469 +113,190 @@ const client = new ChainhooksClient({ apiKey: process.env.HIRO_API_KEY!, }); -const v2Chainhook = await client.registerChainhook({ +await client.registerChainhook({ version: '1', name: 'stx-transfers', chain: 'stacks', network: 'mainnet', filters: { - events: [ - { - type: 'stx_transfer', // Changed from scope + actions - }, - ], + events: [{ type: 'stx_transfer' }], }, action: { type: 'http_post', url: 'https://example.com/webhooks', - // Note: Authorization header removed, use webhook secrets instead }, options: { decode_clarity_values: true, enable_on_registration: true, }, }); +``` + + + +### Create v2 chainhooks +Provision each chainhook with the SDK or REST API, mirroring the mapped filters. -console.log('Created v2 chainhook:', v2Chainhook.uuid); +#### REST +```bash +curl -sS -X POST "https://api.mainnet.hiro.so/chainhooks/v1/me/" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d @v2-chainhook.json ``` ---- +#### Chainhook SDK +```typescript +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL[config.network], + apiKey: process.env.HIRO_API_KEY!, +}); +const chainhook = await client.registerChainhook(config); +``` + -## Step 4: Delete v1 Chainhooks + +### Validate and retire v1 +Stream events through both versions, confirm delivery, then clean up the legacy definitions. -After verifying v2 chainhooks work correctly, delete the old v1 chainhooks. +:::callout +type: info +### Best practices +Keep both v1 and v2 hooks active until you verify delivery parity. +::: -### cURL +#### Enablement Checks +```typescript +const chainhook = await client.getChainhook(v2Uuid); +console.log(chainhook.status.enabled); +``` +#### Delete with cURL ```bash curl -sS -X DELETE \ "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks/$CHAINHOOK_UUID" \ -H "content-type: application/json" ``` -### JavaScript/TypeScript - +#### Delete with SDK ```typescript -async function deleteV1Chainhook(uuid: string) { +export async function deleteV1Chainhook(uuid: string) { 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', - }, - } + { method: 'DELETE', headers: { 'content-type': 'application/json' } } ); - if (!response.ok) { - throw new Error(`Failed to delete chainhook: ${response.statusText}`); - } - - const result = await response.json(); - return result; + if (!response.ok) throw new Error(response.statusText); + return await response.json(); } - -await deleteV1Chainhook('aa3626dc-2090-49cd-8f1e-8f9994393aed'); -console.log('v1 chainhook deleted'); ``` + -### Response + -```json -{ - "status": "success", - "chainhookUuid": "aa3626dc-2090-49cd-8f1e-8f9994393aed", - "message": "Chainhook deleted successfully" -} -``` - ---- +## Automation Template +Use this script as a starting point when you have many similar chainhooks to move. -## Complete Migration Script - -Here's a complete script to migrate all v1 chainhooks to v2: + + + Bulk migration script + ```typescript import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; -const PLATFORM_API_BASE = 'https://api.platform.hiro.so'; +const PLATFORM_API = 'https://api.platform.hiro.so'; const API_KEY = process.env.HIRO_API_KEY!; +const scopeMap: Record> = { + stx_event: { transfer: 'stx_transfer' }, + ft_event: { transfer: 'ft_transfer' }, + nft_event: { transfer: 'nft_transfer' }, +}; -// Initialize v2 client const v2Client = new ChainhooksClient({ baseUrl: CHAINHOOKS_BASE_URL.mainnet, apiKey: API_KEY, }); -// Helper: List v1 chainhooks -async function listV1Chainhooks() { - const response = await fetch( - `${PLATFORM_API_BASE}/v1/ext/${API_KEY}/chainhooks` - ); - return await response.json(); -} - -// Helper: Delete v1 chainhook -async function deleteV1Chainhook(uuid: string) { - await fetch( - `${PLATFORM_API_BASE}/v1/ext/${API_KEY}/chainhooks/${uuid}`, - { method: 'DELETE' } - ); -} - -// Convert v1 filter to v2 -function convertV1ToV2(v1Chainhook: any) { - const network = v1Chainhook.networks.mainnet || v1Chainhook.networks.testnet; - const scope = network.if_this.scope; - const actions = network.if_this.actions || []; - - // Map v1 scopes to v2 event types - let eventType: string; - if (scope === 'stx_event') { - eventType = actions.includes('transfer') ? 'stx_transfer' : 'stx_event'; - } else if (scope === 'contract_call') { - eventType = 'contract_call'; - } else if (scope === 'ft_event') { - eventType = actions.includes('transfer') ? 'ft_transfer' : 'ft_event'; - } else if (scope === 'nft_event') { - eventType = actions.includes('transfer') ? 'nft_transfer' : 'nft_event'; - } else { - eventType = scope; // Fallback - } - - return { - version: '1', - name: v1Chainhook.name, - chain: v1Chainhook.chain, - network: v1Chainhook.networks.mainnet ? 'mainnet' : 'testnet', - filters: { - events: [ - { - type: eventType, - // Add additional filter properties based on v1 config - ...(network.if_this.contract_identifier && { - contract_identifier: network.if_this.contract_identifier, - }), - }, - ], - }, - action: { - type: 'http_post', - url: network.then_that.http_post.url, - }, - options: { - decode_clarity_values: true, - enable_on_registration: true, - }, - }; -} - -// Main migration function -async function migrateAllChainhooks() { - console.log('🔄 Starting migration from v1 to v2...\n'); - - // Step 1: List v1 chainhooks - const v1Chainhooks = await listV1Chainhooks(); - console.log(`Found ${v1Chainhooks.length} v1 chainhooks\n`); - - const migrated = []; - const failed = []; - - // Step 2: Migrate each v1 chainhook to v2 - for (const v1Chainhook of v1Chainhooks) { - try { - console.log(`Migrating: ${v1Chainhook.name} (${v1Chainhook.uuid})`); - - // Convert and create v2 chainhook - const v2Config = convertV1ToV2(v1Chainhook); - const v2Chainhook = await v2Client.registerChainhook(v2Config); - - console.log(`✅ Created v2 chainhook: ${v2Chainhook.uuid}`); - - // Store migration info - migrated.push({ - v1Uuid: v1Chainhook.uuid, - v2Uuid: v2Chainhook.uuid, - name: v1Chainhook.name, - }); - - // Wait a bit between API calls - await new Promise(resolve => setTimeout(resolve, 500)); - } catch (error) { - console.error(`❌ Failed to migrate ${v1Chainhook.name}:`, error.message); - failed.push({ uuid: v1Chainhook.uuid, name: v1Chainhook.name, error }); - } - } - - console.log(`\n📊 Migration Summary:`); - console.log(`- Migrated: ${migrated.length}`); - console.log(`- Failed: ${failed.length}\n`); - - // Step 3: Prompt to delete v1 chainhooks - if (migrated.length > 0) { - console.log('⚠️ Review v2 chainhooks before deleting v1 versions'); - console.log('Run deleteV1Chainhooks() when ready to clean up\n'); - - return { migrated, failed }; - } -} - -// Optional: Delete v1 chainhooks after verification -async function deleteV1Chainhooks(uuids: string[]) { - console.log(`🗑️ Deleting ${uuids.length} v1 chainhooks...\n`); +const pickNetwork = (networks: any) => + networks.mainnet ? ['mainnet', networks.mainnet] : ['testnet', networks.testnet]; + +async function migrate() { + const res = await fetch(`${PLATFORM_API}/v1/ext/${API_KEY}/chainhooks`); + if (!res.ok) throw new Error(res.statusText); + const hooks = (await res.json()) as any[]; + + for (const hook of hooks) { + const [network, details] = pickNetwork(hook.networks); + const action = details.if_this.actions?.[0] ?? ''; + const eventType = scopeMap[details.if_this.scope]?.[action] ?? details.if_this.scope; + + const payload = { + version: '1', + name: hook.name, + chain: hook.chain, + network, + filters: { + events: [ + { + type: eventType, + ...(details.if_this.contract_identifier && { + contract_identifier: details.if_this.contract_identifier, + }), + }, + ], + }, + action: { type: 'http_post', url: details.then_that.http_post.url }, + options: { decode_clarity_values: true, enable_on_registration: true }, + }; - for (const uuid of uuids) { try { - await deleteV1Chainhook(uuid); - console.log(`✅ Deleted v1 chainhook: ${uuid}`); - await new Promise(resolve => setTimeout(resolve, 500)); + const created = await v2Client.registerChainhook(payload); + console.log(`Migrated ${hook.uuid} → ${created.uuid}`); } catch (error) { - console.error(`❌ Failed to delete ${uuid}:`, error.message); + console.error(`Failed for ${hook.uuid}`, error); } } - - console.log('\n✅ v1 cleanup complete!'); } -// Run migration -const result = await migrateAllChainhooks(); - -// After verifying v2 chainhooks work: -// await deleteV1Chainhooks(result.migrated.map(m => m.v1Uuid)); +migrate(); ``` ---- - -## Filter Conversion Guide - -### STX Events - -**v1**: -```json + + + + +## Filter Translation Reference +### Common scopes +| v1 Scope | Typical Actions | v2 `type` | 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`. | + +### Example +```jsonc +// v1 filter { "scope": "stx_event", "actions": ["transfer"] } ``` -**v2**: -```json -{ - "events": [ - { "type": "stx_transfer" } - ] -} -``` - -### Contract Calls - -**v1**: -```json -{ - "scope": "contract_call", - "contract_identifier": "SP...XYZ.counter", - "method": "increment" -} -``` - -**v2**: -```json -{ - "events": [ - { - "type": "contract_call", - "contract_identifier": "SP...XYZ.counter", - "function_name": "increment" - } - ] -} -``` - -### FT Events - -**v1**: -```json -{ - "scope": "ft_event", - "asset_identifier": "SP...ABC.token::usdc", - "actions": ["transfer"] -} -``` - -**v2**: -```json -{ - "events": [ - { - "type": "ft_transfer", - "asset_identifier": "SP...ABC.token::usdc" - } - ] -} -``` - -### NFT Events - -**v1**: -```json -{ - "scope": "nft_event", - "asset_identifier": "SP...COLL.nft::item", - "actions": ["mint", "transfer"] -} -``` - -**v2**: -```json +```jsonc +// v2 filter { "events": [ { - "type": "nft_mint", - "asset_identifier": "SP...COLL.nft::item" - }, - { - "type": "nft_transfer", - "asset_identifier": "SP...COLL.nft::item" + "type": "stx_transfer", + "recipient": "SP3FBR2AGKQX0..." } ] } ``` ---- - -## Key Differences - -### 1. Authentication - -**v1 (Platform API)**: -- API key in path: `/v1/ext/{apiKey}/chainhooks` - -**v2 (Chainhook API)**: -- API key in header: `x-api-key: YOUR_KEY` -- Or use SDK which handles this automatically - -### 2. Webhook Authorization - -**v1**: -- Used `authorization_header` in config - -**v2**: -- Uses `x-chainhook-consumer-secret` header automatically -- Rotate with `rotateConsumerSecret()` method - -### 3. Filter Syntax - -**v1**: -- `scope` + `actions` approach -- Example: `scope: "stx_event", actions: ["transfer"]` - -**v2**: -- Direct event type -- Example: `type: "stx_transfer"` - -### 4. Network Configuration - -**v1**: -- Nested under `networks.mainnet` or `networks.testnet` - -**v2**: -- Top-level `network` field -- Separate chainhooks for each network - ---- - -## Troubleshooting - -### v1 Chainhook Not Found - -If you can't find a v1 chainhook: -```typescript -const chainhook = await getV1Chainhook(uuid); -if (!chainhook) { - console.error('Chainhook not found - may have been deleted'); -} -``` - -### Migration Fails - -If migration fails, check: -1. API key is valid for both Platform and Chainhook APIs -2. Webhook URL is accessible -3. Filter syntax is correct for v2 - -### v2 Chainhook Not Triggering - -After migration, if events aren't delivered: -1. Verify chainhook is enabled: `chainhook.status.enabled === true` -2. Check webhook secret validation in your endpoint -3. Use `evaluateChainhook()` to test against a known block - ---- - -## Best Practices - -### 1. Test Migration on Testnet First - -Migrate testnet chainhooks first to validate your process: - -```typescript -// Migrate testnet chainhooks first -const v2Client = new ChainhooksClient({ - baseUrl: CHAINHOOKS_BASE_URL.testnet, - apiKey: API_KEY, -}); -``` - -### 2. Keep v1 Running During Testing - -Don't delete v1 chainhooks until you've confirmed v2 works: -- Let both run for 24-48 hours -- Compare webhook deliveries -- Verify no data loss - -### 3. Document Your Mappings - -Keep a record of v1 to v2 UUID mappings: - -```typescript -await db.migrations.create({ - v1Uuid: v1Chainhook.uuid, - v2Uuid: v2Chainhook.uuid, - migratedAt: new Date(), - name: v1Chainhook.name, -}); -``` - -### 4. Monitor Both Systems - -Monitor webhook deliveries during transition: - -```typescript -app.post('/webhooks', (req, res) => { - const { chainhook } = req.body; - - console.log(`Webhook from: ${chainhook.uuid}`); - console.log(`Name: ${chainhook.name}`); - - // Process event... - res.sendStatus(200); -}); -``` - ---- - -## Next Steps - :::next-steps -* [SDK Documentation](/tools/chainhook/chainhook-sdk): Learn the v2 SDK -* [Filter Reference](/tools/chainhook/reference/filters): Explore v2 filters -::: - -:::callout{type="help"} -### Need Migration Help? - -If you need assistance migrating your chainhooks: -- **Discord**: #chainhook channel on [Discord](https://stacks.chat/) -- **Email**: [support@hiro.so](mailto:support@hiro.so) -- **Office Hours**: Every Thursday at 11am ET +- [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)/usage.mdx b/content/docs/en/tools/chainhook/(overview)/usage.mdx deleted file mode 100644 index fa7a07338..000000000 --- a/content/docs/en/tools/chainhook/(overview)/usage.mdx +++ /dev/null @@ -1,325 +0,0 @@ ---- -title: Usage -description: Learn Chainhook core concepts, event filtering, webhook delivery, and best practices ---- - -# Usage - -Learn the core concepts of working with Chainhooks, how they process blockchain events, and best practices for building reliable event-driven applications. - -## How Chainhooks Work - -A chainhook monitors blockchain events and delivers matching data to your application via webhooks. Each chainhook consists of three main components: - -### 1. Filters - -Filters define which blockchain events trigger your chainhook. You can monitor: - -- **Token events** - FT/NFT transfers, mints, and burns -- **STX events** - STX transfers and operations -- **Contract events** - Deployments, function calls, and logs -- **System events** - Block rewards and tenure changes - -Example filter for FT transfers: -```json -{ - "events": [ - { - "type": "ft_transfer", - "asset_identifier": "SP...ABC.token::usdc" - } - ] -} -``` - -Learn more in the [Filter Reference](/tools/chainhook/reference/filters). - -### 2. Actions - -Actions define where chainhook data is delivered. Currently, chainhooks support HTTP POST to your webhook endpoint: - -```json -{ - "action": { - "type": "http_post", - "url": "https://example.com/webhooks" - } -} -``` - -Your endpoint receives a POST request with the event payload whenever a filter matches. - -### 3. Options - -Options customize the payload content and evaluation behavior: - -```json -{ - "options": { - "decode_clarity_values": true, - "include_block_metadata": true, - "enable_on_registration": true - } -} -``` - -Learn more in the [Options Reference](/tools/chainhook/reference/options). - ---- - -## Event Processing Flow - -1. **Block Evaluation** - Chainhook evaluates each new block against your filters -2. **Event Matching** - If events match your filters, a payload is generated -3. **Webhook Delivery** - The payload is POSTed to your configured endpoint -4. **Reorg Handling** - If a reorg occurs, rollback events are sent - -``` -Blockchain → Chainhook → Filter Match → Webhook Delivery → Your Application -``` - ---- - -## Webhook Payloads - -When a chainhook triggers, you receive a payload with this structure: - -```json -{ - "event": { - "apply": [ - { - "block_identifier": { "index": 150000, "hash": "0x..." }, - "transactions": [ - { - "metadata": { /* tx info */ }, - "operations": [ /* state changes */ ] - } - ] - } - ], - "rollback": [], - "chain": "stacks", - "network": "mainnet" - }, - "chainhook": { - "uuid": "chainhook-uuid", - "name": "my-chainhook" - } -} -``` - -Learn more in the [Payload Anatomy](/tools/chainhook/reference/payload-anatomy) reference. - ---- - -## Handling Blockchain Reorganizations - -Chainhook automatically detects blockchain reorganizations (reorgs) and sends both `apply` and `rollback` events to keep your data consistent. - -### Understanding Reorgs - -A reorg occurs when the blockchain's canonical chain changes: -1. Some blocks become orphaned (removed from the canonical chain) -2. New blocks become part of the canonical chain -3. Transactions may be reordered or invalidated - -### Processing Reorg Events - -Always process rollback events before apply events: - -```typescript -app.post('/webhooks', async (req, res) => { - const { apply, rollback } = req.body.event; - - // Step 1: Roll back orphaned blocks - for (const block of rollback) { - await database.revertBlock(block); - console.log(`Rolled back block ${block.block_identifier.index}`); - } - - // Step 2: Apply new canonical blocks - for (const block of apply) { - await database.processBlock(block); - console.log(`Applied block ${block.block_identifier.index}`); - } - - res.sendStatus(200); -}); -``` - -### Best Practices for Reorgs - -1. **Process rollbacks first** - Always revert orphaned blocks before applying new ones -2. **Use transactions** - Wrap database operations in transactions for atomicity -3. **Track block hashes** - Store block hashes to detect reorgs in your data -4. **Test reorg scenarios** - Use the [evaluate method](/tools/chainhook/evaluate) to test reorg handling - ---- - -## SDK vs Platform - -Choose the right tool for your use case: - -### Use the Chainhook SDK When: - -- **Editing required** - Platform UI doesn't support editing chainhooks yet -- **Automation** - Integrate chainhook management into your CI/CD pipeline -- **Programmatic control** - Create/update/delete chainhooks from your application -- **Scale** - Manage many chainhooks with bulk operations -- **Advanced features** - Use evaluate for historical indexing or testing - -### Use the Platform UI When: - -- **Getting started** - Create your first chainhook quickly without code -- **Visual creation** - Prefer a form-based interface -- **Viewing status** - Monitor chainhook activity and metrics -- **Managing keys** - Generate and manage API keys visually - -:::callout{type="info"} -Both approaches use the same underlying Chainhook API and deliver identical webhook payloads. -::: - ---- - -## Chainhook Lifecycle - -### 1. Create -Register a chainhook with filters, action, and options: -- SDK: `client.registerChainhook({ ... })` -- Platform: Fill out the creation form - -### 2. Enable -Activate the chainhook to start receiving events: -- SDK: Set `enable_on_registration: true` or call `client.enableChainhook()` -- Platform: Enable toggle in the UI - -### 3. Monitor -Track chainhook activity and performance: -- SDK: `client.getChainhook(uuid)` to check status -- Platform: View metrics in the dashboard - -### 4. Update -Modify filters, options, or webhook URL: -- SDK: `client.updateChainhook(uuid, { ... })` -- Platform: Not currently available (use SDK) - -### 5. Disable/Delete -Pause or remove a chainhook: -- SDK: `client.enableChainhook(uuid, false)` or `client.deleteChainhook(uuid)` -- Platform: Disable toggle or delete button - ---- - -## Best Practices - -### Webhook Security - -1. **Validate secrets** - Check the `x-chainhook-consumer-secret` header on all requests -2. **Use HTTPS** - Always use encrypted endpoints for webhooks -3. **Rotate secrets** - Periodically rotate webhook secrets with `rotateConsumerSecret()` -4. **Implement retries** - Handle webhook delivery failures gracefully - -### Performance - -1. **Filter precisely** - Use specific filters to reduce unnecessary webhooks -2. **Process asynchronously** - Return 200 quickly, process events in the background -3. **Batch operations** - Use bulk enable/disable for managing multiple chainhooks -4. **Monitor payload size** - Only enable options you need to reduce payload size - -### Reliability - -1. **Handle reorgs** - Always process rollback events correctly -2. **Idempotent processing** - Handle duplicate webhook deliveries -3. **Test before enabling** - Use `evaluateChainhook()` against known blocks -4. **Set expiration** - Use `expire_after_occurrences` for time-limited chainhooks - -### Development - -1. **Start on testnet** - Test your chainhooks on testnet before mainnet -2. **Use decode options** - Enable `decode_clarity_values` for easier debugging -3. **Log webhook deliveries** - Track all incoming webhooks for debugging -4. **Version your chainhooks** - Include version numbers in chainhook names - ---- - -## Common Patterns - -### Track User Activity - -Monitor all activity for a specific address: - -```typescript -await client.registerChainhook({ - name: 'user-activity-tracker', - chain: 'stacks', - network: 'mainnet', - filters: { - events: [ - { type: 'stx_transfer', sender: 'SP...USER' }, - { type: 'stx_transfer', receiver: 'SP...USER' }, - { type: 'ft_transfer', sender: 'SP...USER' }, - { type: 'nft_transfer', sender: 'SP...USER' }, - ], - }, - action: { type: 'http_post', url: 'https://api.myapp.com/user-events' }, - options: { decode_clarity_values: true, enable_on_registration: true }, -}); -``` - -### Monitor Contract Calls - -Track specific contract function calls: - -```typescript -await client.registerChainhook({ - name: 'dex-swap-monitor', - chain: 'stacks', - network: 'mainnet', - filters: { - events: [ - { - type: 'contract_call', - contract_identifier: 'SP...DEX.swap-contract', - function_name: 'swap', - }, - ], - }, - action: { type: 'http_post', url: 'https://api.myapp.com/swaps' }, - options: { decode_clarity_values: true, enable_on_registration: true }, -}); -``` - -### Time-Limited Monitoring - -Create a chainhook that expires after a certain number of events: - -```typescript -await client.registerChainhook({ - name: 'limited-nft-mints', - chain: 'stacks', - network: 'mainnet', - filters: { - events: [ - { - type: 'nft_mint', - asset_identifier: 'SP...COLL.nft::collectible', - }, - ], - }, - action: { type: 'http_post', url: 'https://api.myapp.com/mints' }, - options: { - enable_on_registration: true, - expire_after_occurrences: 1000, // Auto-expire after 1000 mints - }, -}); -``` - ---- - -## Next Steps - -:::next-steps -* [SDK Quickstart](/tools/chainhook/quickstart): Start building with the SDK -* [Filter Reference](/tools/chainhook/reference/filters): Explore all event filters -::: diff --git a/content/docs/en/tools/chainhook/(platform-usage)/register-enable.mdx b/content/docs/en/tools/chainhook/(platform-usage)/create-enable-chainhooks.mdx similarity index 100% rename from content/docs/en/tools/chainhook/(platform-usage)/register-enable.mdx rename to content/docs/en/tools/chainhook/(platform-usage)/create-enable-chainhooks.mdx diff --git a/content/docs/en/tools/chainhook/(platform-usage)/index.mdx b/content/docs/en/tools/chainhook/(platform-usage)/index.mdx deleted file mode 100644 index 86b3b66ab..000000000 --- a/content/docs/en/tools/chainhook/(platform-usage)/index.mdx +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Platform Usage -description: Manage chainhooks visually with the Hiro Platform web interface ---- - diff --git a/content/docs/en/tools/chainhook/(platform-usage)/manage-keys.mdx b/content/docs/en/tools/chainhook/(platform-usage)/manage-api-keys.mdx similarity index 100% rename from content/docs/en/tools/chainhook/(platform-usage)/manage-keys.mdx rename to content/docs/en/tools/chainhook/(platform-usage)/manage-api-keys.mdx diff --git a/content/docs/en/tools/chainhook/(platform-usage)/meta.json b/content/docs/en/tools/chainhook/(platform-usage)/meta.json index 622a2a746..de89b7c43 100644 --- a/content/docs/en/tools/chainhook/(platform-usage)/meta.json +++ b/content/docs/en/tools/chainhook/(platform-usage)/meta.json @@ -1,9 +1,11 @@ { + "title": "Platform", "pages": [ - "index", - "quickstart", - "register-enable", - "list-fetch", - "manage-keys" + "---Platform---", + "platform-usage", + "platform-quickstart", + "create-enable-chainhooks", + "view-chainhooks", + "manage-api-keys" ] } diff --git a/content/docs/en/tools/chainhook/(platform-usage)/quickstart.mdx b/content/docs/en/tools/chainhook/(platform-usage)/platform-quickstart.mdx similarity index 100% rename from content/docs/en/tools/chainhook/(platform-usage)/quickstart.mdx rename to content/docs/en/tools/chainhook/(platform-usage)/platform-quickstart.mdx 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)/list-fetch.mdx b/content/docs/en/tools/chainhook/(platform-usage)/view-chainhooks.mdx similarity index 100% rename from content/docs/en/tools/chainhook/(platform-usage)/list-fetch.mdx rename to content/docs/en/tools/chainhook/(platform-usage)/view-chainhooks.mdx diff --git a/content/docs/en/tools/chainhook/index.mdx b/content/docs/en/tools/chainhook/index.mdx index 756d4e06c..fad7a6920 100644 --- a/content/docs/en/tools/chainhook/index.mdx +++ b/content/docs/en/tools/chainhook/index.mdx @@ -5,8 +5,10 @@ description: Chainhook is a reorg-aware indexer that serves reliable blockchain llm: false --- -:::callout{type="info"} -**Chainhook 2.0 Beta**: Chainhook 2.0 is currently in beta. During this period, we're focused on learning about reliability, performance, and developer experience. If you encounter issues or have feedback, please reach out to [support@hiro.so](mailto:support@hiro.so). +:::callout +type: warn +### Chainhook 2.0 Beta +Chainhook 2.0 is currently in beta. During this period, we're focused on learning about reliability, performance, and developer experience. If you encounter issues or have feedback, please reach out to [support@hiro.so](mailto:support@hiro.so). ::: ## Overview @@ -20,41 +22,19 @@ With Chainhook 2.0, you can manage chainhooks through: To explore Chainhook features with AI, copy and paste [llms.txt](/tools/chainhook/llms.txt) into your LLM of choice. -![Chainhook overview](/images/tools/chainhook/overview.svg) - ## 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 -- **Webhook delivery** - Receive blockchain events at your HTTP endpoint +- **Event filtering** - Define custom logic to trigger actions on specific blockchain events - **Historical evaluation** - Test chainhooks against past blocks for indexing or debugging -## Getting Started - -### Choose Your Approach - -**Use the SDK when you need to:** -- Edit existing chainhooks (not available in Platform UI) -- Automate chainhook operations -- Integrate chainhook management into your application -- Manage chainhooks programmatically at scale - -**Use the Platform UI when you need to:** -- Create chainhooks visually without code -- View chainhook status and activity -- Manage API keys -- Get started quickly - -## Next Steps - :::next-steps * [SDK Quickstart](/tools/chainhook/quickstart): Get started with the Chainhook SDK in 5 minutes * [Platform Quickstart](/tools/chainhook/platform-quickstart): Create your first chainhook in the Platform UI ::: -:::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. +:::callout +type: help +### 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 db453a8f4..10c94440d 100644 --- a/content/docs/en/tools/chainhook/meta.json +++ b/content/docs/en/tools/chainhook/meta.json @@ -1,11 +1,5 @@ { "title": "Chainhook", "root": true, - "pages": [ - "index", - "...(overview)", - "...(chainhook-sdk)", - "...(platform-usage)", - "...reference" - ] + "pages": ["---Chainhook---", "index", "...(overview)", "...(chainhook-sdk)", "...reference"] } diff --git a/content/docs/en/tools/chainhook/reference/meta.json b/content/docs/en/tools/chainhook/reference/meta.json index 3523c644f..16999a7fb 100644 --- a/content/docs/en/tools/chainhook/reference/meta.json +++ b/content/docs/en/tools/chainhook/reference/meta.json @@ -1,7 +1,4 @@ { - "pages": [ - "filters", - "options", - "payload-anatomy" - ] + "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 index c9e8d5481..4a733cfa8 100644 --- a/content/docs/en/tools/chainhook/reference/options.mdx +++ b/content/docs/en/tools/chainhook/reference/options.mdx @@ -7,8 +7,8 @@ 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{type="info"} -**Defaults**: All boolean options default to `false` if omitted. Integer options are optional. +:::callout +All boolean options default to `false` if omitted. Integer options are optional. ::: --- diff --git a/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx b/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx index 6153c4623..8f29d7f21 100644 --- a/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx +++ b/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx @@ -60,7 +60,7 @@ The `apply` array contains blocks being added to the canonical chain. Each block The `rollback` array contains blocks being removed during a chain reorganization. Same structure as `apply`. -:::callout{type="info"} +:::callout Chainhook automatically handles reorgs by sending rollback events. Your application should reverse any state changes from rolled-back blocks. ::: @@ -197,7 +197,7 @@ Contract function invocation: } ``` -:::callout{type="info"} +:::callout The `args` array includes `repr` and `type` fields when `decode_clarity_values` is enabled in options. ::: diff --git a/content/docs/es/resources/archive/download-guide.mdx b/content/docs/es/resources/archive/download-guide.mdx index 08141c98e..3793559e9 100644 --- a/content/docs/es/resources/archive/download-guide.mdx +++ b/content/docs/es/resources/archive/download-guide.mdx @@ -149,7 +149,7 @@ El `marf.sqlite.blobs` El archivo puede ser muy grande y puede llevar un tiempo ## Preguntas frecuentes - + ¿Por qué siguen fallando las descargas? diff --git a/content/docs/es/resources/guides/using-pyth-price-feeds.mdx b/content/docs/es/resources/guides/using-pyth-price-feeds.mdx index 5fbb2635d..e3d299cc9 100644 --- a/content/docs/es/resources/guides/using-pyth-price-feeds.mdx +++ b/content/docs/es/resources/guides/using-pyth-price-feeds.mdx @@ -360,7 +360,7 @@ Agrupe múltiples actualizaciones de precios cuando sea posible: ### Problemas comunes - + La verificación de VAA falla From 6177ce9986601d5196f01e72c72c0d104d9b4926 Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 08:45:30 -0600 Subject: [PATCH 06/16] update playground components to use api key --- components/layout/mobile-navigation.tsx | 19 ++- components/layouts/docs.tsx | 2 +- components/layouts/links.tsx | 48 +++++- components/openapi/api-page.tsx | 6 + components/openapi/api-playground/index.tsx | 160 +++++++++++++++--- .../api-playground/request-executor.tsx | 20 +-- components/openapi/operation-section.tsx | 6 + 7 files changed, 208 insertions(+), 53 deletions(-) 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..b494dfba3 100644 --- a/components/layouts/docs.tsx +++ b/components/layouts/docs.tsx @@ -395,7 +395,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({ /> + ) : 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} /> )} From 4ee6c4f46b0bf7c8663200b23adec073c6033209 Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 08:45:40 -0600 Subject: [PATCH 07/16] update layouts and components --- app/[locale]/[...slug]/page.tsx | 46 +++++++++++++++++++++------------ app/layout.config.tsx | 8 +++++- app/layout.tsx | 21 ++++++++------- 3 files changed, 49 insertions(+), 26 deletions(-) diff --git a/app/[locale]/[...slug]/page.tsx b/app/[locale]/[...slug]/page.tsx index 5364fb4f7..c0677d6e3 100644 --- a/app/[locale]/[...slug]/page.tsx +++ b/app/[locale]/[...slug]/page.tsx @@ -26,6 +26,7 @@ import { API } from '@/components/reference/api-page'; import { Badge } from '@/components/ui/badge'; import * as customIcons from '@/components/ui/icon'; import { TagFilterSystem } from '@/components/ui/tag-filter-system'; +import { getAPIConfig } from '@/lib/api-config'; import { i18n } from '@/lib/i18n'; import { getAllFilterablePages, source } from '@/lib/source'; import type { HeadingProps } from '@/types'; @@ -120,6 +121,7 @@ export default async function Page(props: { interactiveLinks, // Use the processed links with React components }; + return ( {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/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} + + + From d0991faaf9963a423768429037cd3b89c0ef5a54 Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 08:45:50 -0600 Subject: [PATCH 08/16] add chainhook api to openapi scripts --- scripts/fetch-openapi-specs.mts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fetch-openapi-specs.mts b/scripts/fetch-openapi-specs.mts index fe62751f3..18de18227 100644 --- a/scripts/fetch-openapi-specs.mts +++ b/scripts/fetch-openapi-specs.mts @@ -44,7 +44,7 @@ const API_SPECS: ApiSpec[] = [ url: 'https://runes-api.vercel.app/openapi.json', }, { - name: 'chainhooks', + name: 'chainhook', url: 'https://chainhooks-api.vercel.app/openapi.json', }, ]; From 304fbdaf68e620fb8aa31c0262582244c9aec65d Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 11:48:54 -0600 Subject: [PATCH 09/16] add es docs --- content/docs/en/resources/guides/meta.json | 2 - .../guides/using-pyth-price-feeds.mdx | 418 ----------------- .../bitcoin-indexer/(indexer)/full-sync.mdx | 2 - .../tools/chainhook/(chainhook-sdk)/meta.json | 1 - .../chainhook/reference/bitcoin-scopes.mdx | 281 ------------ .../en/tools/chainhook/reference/filters.mdx | 28 +- .../en/tools/chainhook/reference/options.mdx | 91 +--- .../chainhook/reference/payload-anatomy.mdx | 242 +--------- .../chainhook/reference/stacks-scopes.mdx | 351 --------------- content/docs/es/apis/chainhook-api/index.mdx | 102 +++++ .../chainhooks/bulk-enable-chainhooks.mdx | 7 + .../reference/chainhooks/delete-chainhook.mdx | 7 + .../chainhooks/evaluate-chainhook.mdx | 7 + .../reference/chainhooks/get-chainhook.mdx | 7 + .../reference/chainhooks/get-chainhooks.mdx | 7 + .../reference/chainhooks/index.mdx | 6 + .../chainhooks/register-chainhook.mdx | 7 + .../chainhooks/update-chainhook-enabled.mdx | 7 + .../reference/chainhooks/update-chainhook.mdx | 7 + .../chainhook-api/reference/info/index.mdx | 6 + .../chainhook-api/reference/info/status.mdx | 7 + .../secrets/delete-consumer-secret.mdx | 7 + .../chainhook-api/reference/secrets/index.mdx | 6 + .../secrets/rotate-consumer-secret.mdx | 7 + content/docs/es/apis/chainhook-api/usage.mdx | 287 ++++++++++++ .../es/resources/archive/download-guide.mdx | 367 ++++++++++++--- .../guides/using-pyth-price-feeds.mdx | 426 ------------------ .../bitcoin-indexer/(indexer)/full-sync.mdx | 236 ++++++++-- .../chainhook/(chainhook-sdk)/edit-update.mdx | 123 +++++ .../chainhook/(chainhook-sdk)/evaluate.mdx | 62 +++ .../(chainhook-sdk)/introduction.mdx | 166 +++++++ .../chainhook/(chainhook-sdk)/list-fetch.mdx | 147 ++++++ .../chainhook/(chainhook-sdk)/manage-keys.mdx | 66 +++ .../(chainhook-sdk)/register-enable.mdx | 265 +++++++++++ .../es/tools/chainhook/(overview)/faq.mdx | 376 ++++++++++++++++ .../tools/chainhook/(overview)/migration.mdx | 334 ++++++++++++++ .../create-enable-chainhooks.mdx | 4 + .../(platform-usage)/manage-api-keys.mdx | 4 + .../(platform-usage)/platform-quickstart.mdx | 4 + .../(platform-usage)/platform-usage.mdx | 60 +++ .../(platform-usage)/view-chainhooks.mdx | 4 + content/docs/es/tools/chainhook/index.mdx | 54 +-- .../es/tools/chainhook/reference/filters.mdx | 366 +++++++++++++++ .../es/tools/chainhook/reference/options.mdx | 237 ++++++++++ .../chainhook/reference/payload-anatomy.mdx | 249 ++++++++++ idioma.lock | 190 +++++--- 46 files changed, 3652 insertions(+), 1988 deletions(-) delete mode 100644 content/docs/en/resources/guides/using-pyth-price-feeds.mdx delete mode 100644 content/docs/en/tools/chainhook/reference/bitcoin-scopes.mdx delete mode 100644 content/docs/en/tools/chainhook/reference/stacks-scopes.mdx create mode 100644 content/docs/es/apis/chainhook-api/index.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/chainhooks/bulk-enable-chainhooks.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/chainhooks/delete-chainhook.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/chainhooks/evaluate-chainhook.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/chainhooks/get-chainhook.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/chainhooks/get-chainhooks.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/chainhooks/index.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/chainhooks/register-chainhook.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/chainhooks/update-chainhook-enabled.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/chainhooks/update-chainhook.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/info/index.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/info/status.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/secrets/delete-consumer-secret.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/secrets/index.mdx create mode 100644 content/docs/es/apis/chainhook-api/reference/secrets/rotate-consumer-secret.mdx create mode 100644 content/docs/es/apis/chainhook-api/usage.mdx delete mode 100644 content/docs/es/resources/guides/using-pyth-price-feeds.mdx create mode 100644 content/docs/es/tools/chainhook/(chainhook-sdk)/edit-update.mdx create mode 100644 content/docs/es/tools/chainhook/(chainhook-sdk)/evaluate.mdx create mode 100644 content/docs/es/tools/chainhook/(chainhook-sdk)/introduction.mdx create mode 100644 content/docs/es/tools/chainhook/(chainhook-sdk)/list-fetch.mdx create mode 100644 content/docs/es/tools/chainhook/(chainhook-sdk)/manage-keys.mdx create mode 100644 content/docs/es/tools/chainhook/(chainhook-sdk)/register-enable.mdx create mode 100644 content/docs/es/tools/chainhook/(overview)/faq.mdx create mode 100644 content/docs/es/tools/chainhook/(overview)/migration.mdx create mode 100644 content/docs/es/tools/chainhook/(platform-usage)/create-enable-chainhooks.mdx create mode 100644 content/docs/es/tools/chainhook/(platform-usage)/manage-api-keys.mdx create mode 100644 content/docs/es/tools/chainhook/(platform-usage)/platform-quickstart.mdx create mode 100644 content/docs/es/tools/chainhook/(platform-usage)/platform-usage.mdx create mode 100644 content/docs/es/tools/chainhook/(platform-usage)/view-chainhooks.mdx create mode 100644 content/docs/es/tools/chainhook/reference/filters.mdx create mode 100644 content/docs/es/tools/chainhook/reference/options.mdx create mode 100644 content/docs/es/tools/chainhook/reference/payload-anatomy.mdx 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 a1fc297c0..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/tools/bitcoin-indexer/(indexer)/full-sync.mdx b/content/docs/en/tools/bitcoin-indexer/(indexer)/full-sync.mdx index dcba7f8a9..f9fe6a3bc 100644 --- a/content/docs/en/tools/bitcoin-indexer/(indexer)/full-sync.mdx +++ b/content/docs/en/tools/bitcoin-indexer/(indexer)/full-sync.mdx @@ -182,8 +182,6 @@ 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. diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json b/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json index 6f7b2996a..241c650f1 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json @@ -3,7 +3,6 @@ "pages": [ "---Chainhook SDK---", "introduction", - "quickstart", "register-enable", "list-fetch", "edit-update", 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 index 88ceb2978..768447ca5 100644 --- a/content/docs/en/tools/chainhook/reference/filters.mdx +++ b/content/docs/en/tools/chainhook/reference/filters.mdx @@ -5,7 +5,25 @@ description: Complete reference for all Chainhook filter types # Filter Reference -Filters define which blockchain events will trigger your chainhook. Use any of the following filter types in your `filters.events[]` array. +Filters define which blockchain events will trigger your chainhook—use the matrix below to pick the right entry for `filters.events[]`. + +| Filter | When to use | +|--------|-------------| +| `ft_event` | FT: catch *every* SIP-010 transfer, mint, or burn across assets. | +| `ft_transfer` | FT: follow a single asset such as USDC; optionally add `sender`/`receiver` for wallet-level triggers. | +| `ft_mint` | FT: track supply expansions or bridge inflows for one asset (set `asset_identifier`). | +| `ft_burn` | FT: track redemptions or supply contractions (set `asset_identifier`). | +| `nft_event` | NFT: monitor every transfer, mint, or burn for all collections. | +| `nft_transfer` | NFT: follow a SIP-009 collection; add `sender`, `receiver`, or `value` for owner/token targeting. | +| `nft_mint` | NFT: watch every new mint for a collection (set `asset_identifier`). | +| `nft_burn` | NFT: catch burns or redemptions (set `asset_identifier`). | +| `stx_event` | STX: capture all native transfers, mints, or burns. | +| `stx_transfer` | STX: trace every transfer; add `sender` or `receiver` to spotlight specific principals. | +| `contract_deploy` | Contracts: react to new contracts entering the network. | +| `contract_call` | Contracts: observe every invocation; narrow with `contract_identifier` and `function_name`. | +| `contract_log` | Contracts: catch `print`/log output from a contract (set `contract_identifier`). | +| `coinbase` | System: watch miner rewards hitting the chain. | +| `tenure_change` | System: track Proof-of-Transfer tenure updates; add `cause` (`"block_found"` or `"extended"`) for specificity. | ## Fungible Token Events (FT) @@ -343,11 +361,7 @@ You can combine multiple filters in the `filters.events` array. A chainhook will This chainhook will trigger on either DIKO transfers **or** calls to the `increment` function. ---- - -## Next Steps - :::next-steps -* [Options Reference](/tools/chainhook/reference/options): Configure payload enrichment and evaluation windows -* [Register & Enable](/tools/chainhook/register-enable): Create your first chainhook +- [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/options.mdx b/content/docs/en/tools/chainhook/reference/options.mdx index 4a733cfa8..9a0dcbbca 100644 --- a/content/docs/en/tools/chainhook/reference/options.mdx +++ b/content/docs/en/tools/chainhook/reference/options.mdx @@ -3,8 +3,6 @@ title: Options Reference description: Complete reference for all Chainhook configuration options --- -# Options Reference - Options control payload enrichment and evaluation windows for your chainhook. The `options` field is optional and can be omitted or set to `null`. :::callout @@ -13,7 +11,20 @@ All boolean options default to `false` if omitted. Integer options are optional. --- -## Payload Enrichment Options +## 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 @@ -204,77 +215,7 @@ This chainhook will expire after triggering 250 times. --- -## Combined Examples - -### Development Setup - -For local development with maximum visibility: - -```json -{ - "options": { - "decode_clarity_values": true, - "include_contract_abi": true, - "include_contract_source_code": true, - "include_post_conditions": true, - "include_block_metadata": true, - "enable_on_registration": true - } -} -``` - -### Production Setup - -For production with essential data only: - -```json -{ - "options": { - "decode_clarity_values": true, - "enable_on_registration": true - } -} -``` - -### Time-Limited Chainhook - -For a chainhook that auto-expires after 1000 blocks or 100 matches: - -```json -{ - "options": { - "decode_clarity_values": true, - "enable_on_registration": true, - "expire_after_evaluations": 1000, - "expire_after_occurrences": 100 - } -} -``` - -The chainhook will expire when **either** condition is met. - ---- - -## Options Summary Table - -| 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 | - ---- - -## Next Steps - :::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 +- [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 index 8f29d7f21..dcf7ea5c1 100644 --- a/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx +++ b/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx @@ -3,10 +3,6 @@ title: Payload Anatomy description: Understanding the structure of Chainhook webhook payloads --- -# Payload Anatomy - -This guide explains the structure of webhook payloads delivered by Chainhook when your event filters match. - ## Payload Overview A Chainhook payload consists of two main sections: @@ -229,8 +225,6 @@ FT/NFT/STX transfers: } ``` -**Note**: Negative values indicate debits (sender), positive values indicate credits (receiver). - ### Contract Log Operation Print statements from contracts: @@ -250,239 +244,7 @@ Print statements from contracts: } ``` ---- - -## Complete Example - -Here's a complete payload from a contract call that transfers fungible tokens: - -```json -{ - "event": { - "apply": [ - { - "timestamp": 1757977309, - "transactions": [ - { - "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 - }, - "operations": [ - { - "type": "fee", - "amount": { - "value": "-3000", - "currency": { - "symbol": "STX", - "decimals": 6 - } - }, - "status": "success", - "account": { - "address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9" - }, - "metadata": { - "sponsored": false - }, - "operation_identifier": { - "index": 0 - } - }, - { - "type": "contract_call", - "status": "success", - "account": { - "address": "SPV2YDN4RZ506655KZK493YKM31JQPKJ9NN3QCX9" - }, - "metadata": { - "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))" - } - ], - "function_name": "transfer", - "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token" - }, - "operation_identifier": { - "index": 1 - } - }, - { - "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 - } - }, - { - "type": "token_transfer", - "amount": { - "value": "10352515582", - "currency": { - "symbol": "", - "decimals": 0, - "metadata": { - "token_type": "ft", - "asset_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token::diko" - } - } - }, - "status": "success", - "account": { - "address": "SPBM1X391HWMCVHKH6GK8XJRDR6B7REZQYQ8KBCK" - }, - "operation_identifier": { - "index": 3 - } - }, - { - "type": "contract_log", - "status": "success", - "metadata": { - "topic": "print", - "value": "0x09", - "contract_identifier": "SP2C2YFP12AJZB4MABJBAJ55XECVS7E4PMMZ89YZR.arkadiko-token" - }, - "operation_identifier": { - "index": 4 - } - } - ], - "transaction_identifier": { - "hash": "0x09defc9a6cd9318b5c458389d4dd57597203ec539818aec0de3cfcfd7af0c2ab" - } - } - ], - "block_identifier": { - "hash": "0xa204da7ae59b86a569d66c237721937e1708b72913c6a67abf58360b8f5935b7", - "index": 3549902 - }, - "parent_block_identifier": { - "hash": "0xad0acff4e24c398f00ea07079b36753f5136c1dfc021d19e58cfdcbcee265e5c", - "index": 3549901 - } - } - ], - "chain": "stacks", - "network": "mainnet", - "rollback": [] - }, - "chainhook": { - "name": "blocksurvey", - "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185" - } -} -``` - ---- - -## Operation Types - -Chainhook payloads can include the following operation types: - -| Type | Description | -|------|-------------| -| `fee` | Transaction fee payment | -| `contract_call` | Contract function invocation | -| `contract_deploy` | Contract deployment | -| `token_transfer` | FT/NFT/STX transfer | -| `contract_log` | Contract print statement | -| `coinbase` | Block reward | -| `tenure_change` | Nakamoto tenure change | - ---- - -## Best Practices - -### Handle Reorgs - -Always process the `rollback` array before the `apply` array: - -```typescript -const { apply, rollback } = payload.event; - -// Reverse state changes from rolled-back blocks -for (const block of rollback) { - await revertBlock(block); -} - -// Apply new canonical blocks -for (const block of apply) { - await processBlock(block); -} -``` - -### Index Operations - -Operations are indexed sequentially. Use `operation_identifier.index` to maintain order when processing events. - -### Check Transaction Status - -Always verify `metadata.status === "success"` before processing transaction data. Failed transactions are included in payloads but should typically be ignored. - ---- - -## Next Steps - :::next-steps -* [Filter Reference](/tools/chainhook/reference/filters): Configure which events trigger your chainhook -* [Options Reference](/tools/chainhook/reference/options): Customize payload content +- [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/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 3793559e9..503fa07b4 100644 --- a/content/docs/es/resources/archive/download-guide.mdx +++ b/content/docs/es/resources/archive/download-guide.mdx @@ -1,52 +1,216 @@ --- -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 grandes del archivo de Hiro con consejos prácticos para evitar errores. --- -## Visió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. +## Descripción general + +Los archivos de Hiro Archive son conjuntos de datos masivos (desde varios GB hasta varios *cientos* de GB) alojados en Google Cloud Storage. Las descargas pueden cortarse por límites de red o tiempos de espera, así que necesitas estrategias fiables y comandos que reanuden el trabajo cuando algo falla. ## Tamaños y requisitos de archivos -Antes de descargar, asegúrate de tener suficiente: +Antes de iniciar cualquier descarga, confirma estos puntos básicos: + +- **Espacio en disco**: Desde 10 GB (APIs) hasta cientos de GB para datos de blockchain. +- **Ancho de banda**: Algunas descargas pueden tardar horas; planifica horarios de baja congestión. +- **Almacenamiento para extracción**: Al descomprimir, los archivos crecen 2‑3×. Reserva espacio adicional. -* **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 +```ts +// Verifica espacio libre en macOS o Linux +$ df -h | grep archive +# Muestra el porcentaje de uso y te ayuda a decidir si necesitas otro volumen +``` ## 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) -El `wget` comando con el `-c` la bandera permite reanudar descargas interrumpidas: +El comando `wget` con la bandera `-c` reanuda cualquier descarga interrumpida y mantiene una barra de progreso legible. :::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. +Instala la utilidad con `brew install wget` si tu sistema no la incluye. Como alternativa, usa el método `curl` mostrado más abajo (viene preinstalado). ::: ```terminal $ wget -c https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz $ wget -c --progress=bar:force:noscroll https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz +# La barra forzada evita que el log crezca sin control durante descargas largas ``` -**Ventajas:** +**Ventajas** +- Retoma descargas automáticamente. +- Disponible en la mayoría de sistemas Unix. +- Sintaxis corta y fácil de automatizar. + +**Desventajas** +- Descarga en un solo hilo. +- Puede sufrir reinicios si tu ISP corta conexiones largas. + +### Método 2: curl con reintentos automáticos + +`curl` ofrece control granular sobre reintentos, tiempos de espera y nombres de archivo. + +```terminal +$ curl --continue-at - --retry 10 --retry-delay 5 --retry-max-time 0 \ + --progress-bar \ + --output mainnet-stacks-blockchain-latest.tar.gz \ + https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz +# --continue-at - retoma el archivo parcial; --retry maneja caídas temporales +``` + +**Ventajas** +- Reintentos configurables con retardo exponencial. +- Permite personalizar cabeceras y autenticación si la necesitas. +- Funciona en macOS sin instalaciones adicionales. + +**Desventajas** +- Sintaxis más larga. +- Necesitas scripts adicionales para mostrar progreso detallado. + +### Método 3: gcloud storage cp (más rápido, requiere autenticación) + +La CLI de Google Cloud usa transferencias paralelas cuando descargas desde Google Cloud Storage. + +```terminal +$ gcloud auth login # Abre el navegador y vincula tu cuenta de Google +$ gcloud storage cp \ + gs://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz . +# Descarga al directorio actual usando varias conexiones paralelas +``` + +Si tu espacio en disco es limitado, transmite directamente al proceso de extracción: + +```terminal +$ gcloud storage cp \ + gs://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz - \ + | tar -xz -C /ruta/de/destino +# Evita archivos temporales grandes, pero desactiva la paralelización del tar +``` + +**Ventajas** +- 2‑3× más rápido gracias a descargas paralelas. +- Maneja reintentos y reanudación por ti. +- Permite streaming a otros comandos (`tar`, `pv`, etc.). -* Reanuda automáticamente las descargas interrumpidas -* Incorporado en la mayoría de los sistemas Unix -* Fácil de usar +**Desventajas** +- Necesita `gcloud` y autenticación. +- Algunas redes empresariales bloquean el flujo OAuth. + +### Método 4: Gestores de descarga (ej. JDownloader) + +Si prefieres una GUI o necesitas dividir la descarga en múltiples conexiones, usa un gestor. + +```ts +// Flujo básico con JDownloader +const url = 'https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz'; +queue.add(url); // Copia y pega el enlace +queue.setChunks(8); // Ajusta los hilos paralelos +queue.start(); // Observa las métricas de velocidad y tiempo restante +``` + +**Ventajas** +- Interfaz visual con métricas de velocidad. +- Soporta reanudación con un clic. +- Configuras conexiones paralelas sin escribir scripts. + +**Desventajas** +- Requiere Java y más memoria. +- No siempre funciona bien en entornos sin GUI. + +## Verificación y extracción + +Después de descargar, verifica que el archivo no esté corrupto y descomprímelo de forma segura. + +```terminal +$ wget https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.sha256 +$ shasum -a 256 mainnet-stacks-blockchain-latest.tar.gz +# Compara el hash mostrado con el archivo .sha256 descargado +``` + +```terminal +$ tar -zxvf mainnet-stacks-blockchain-latest.tar.gz -C /data/stacks +# Usa -C para extraer directamente en el volumen final y evitar copias extra +``` + +:::callout +type: info +### Extracciones pesadas +El archivo `marf.sqlite.blobs` puede tardar bastante en extraerse. No interrumpas `tar` a menos que falle; podrías terminar con datos incompletos. +::: + +## Consejos de rendimiento + +1. **Prefiere `gcloud` si tienes credenciales**: obtienes descargas paralelas y mejores reintentos. +2. **Programa las descargas en horarios valle**: menos congestión significa menos reinicios. +3. **Usa conexiones por cable**: el Wi-Fi introduce microcortes que dañan descargas largas. +4. **Monitorea el espacio libre**: los archivos extraídos ocupan más del doble. +5. **Transmite al extraer** cuando el espacio sea limitado (`gcloud ... - | tar -xz`). + +```ts +// Recordatorio rápido para automatizar descargas nocturnas +cron.schedule('0 1 * * *', () => { + execFile('wget', ['-c', ARCHIVE_URL], console.log); +}); +// Corre en servidores Linux y lanza la descarga todos los días a la 1 AM +``` + +## Preguntas frecuentes + + + + ¿Por qué las descargas fallan repetidamente? + + Los archivos grandes en la nube pueden sufrir límites de velocidad o desconexiones. Usa `wget -c`, `curl --continue-at -` o `gcloud storage cp` para reanudar automáticamente. + + + + ¿Qué método debo elegir? + + `gcloud storage cp` es el más rápido si tienes acceso. `wget -c` es la opción más sencilla. Un gestor como JDownloader sirve cuando prefieres interfaz gráfica. + + + + ¿Cuánto demora la descarga? + + Depende del archivo y de tu ancho de banda. El archivo principal de blockchain (cientos de GB) puede tardar de 6 a 24 horas en conexiones domésticas. + + + + ¿Puedo reanudar una descarga fallida? + + Sí. Usa `wget -c`, `curl --continue-at -` o `gcloud storage cp` con el mismo destino para continuar donde te quedaste. + + + + ¿Por qué gcloud es más rápido? + + La CLI abre varias conexiones en paralelo y está optimizada para Google Cloud Storage, lo que reduce la latencia y maneja los reintentos sin intervención manual. + + + + ¿Debo verificar los archivos? + + Siempre compara el hash SHA256 publicado con el resultado de `shasum -a 256` para asegurarte de que los datos no se corrompieron. + + + **Desventajas:** * Descargas de un solo hilo -* Aún puede experimentar reinicios de conexión +* Es posible que aún experimente restablecimientos de conexión ### 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: +Utilizar `curl` con reintentos automáticos para descargas robustas. The `--continue-at -` bandera reanuda descargas parciales, mientras que `--output` title: Mi Canción +description: Descripción de la canción +sidebarTitle: --- Detalles --- +metadata: + composer: Compositor + artists: --- Intérpretes --- + album: Álbum ```terminal $ curl --continue-at - --retry 10 --retry-delay 5 --retry-max-time 0 \ @@ -58,50 +222,55 @@ $ curl --continue-at - --retry 10 --retry-delay 5 --retry-max-time 0 \ **Ventajas:** * Mecanismo de reintento automático -* Reanudar capacidad con `-C -` +* Capacidad de reanudación 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 en paralelo. Primero autenticarse con `gcloud auth login`, entonces descarga el archivo al disco o transmite directamente a la extracción: -#### Descargar archivo al directorio actual +#### Descargar archivo en el directorio actual ```terminal $ gcloud storage cp gs://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz . ``` -#### O transmitir directamente a la extracción (ahorra espacio en disco pero es más lento debido a la descarga secuencial) +#### O transmitir directamente a extracción (ahorra espacio en disco pero más lento debido a la descarga secuencial) ```terminal $ gcloud storage cp gs://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz - | tar -xz ``` -**Ventajas:** +**Ventajas: + +title: Clarinet +description: --- Clarinet is a key framework for writing maintainable, scalable and performant applications on the Ethereum blockchain. --- +sidebarTitle: Stacks +sidebarText: --- Clarity is a smart contract language designed for security. ---** -* Descargas significativamente más rápidas (mejora de velocidad de 2 a 3 veces) -* Transferencias paralelas incorporadas -* Manejo automático de reintentos +* Descargas significativamente más rápidas (mejora de velocidad de 2-3x) +* Construido en transferencias paralelas +* Manejo automático de reintento * 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: Descargadores de archivos (JDownloader) -Para usuarios que prefieren aplicaciones con interfaz gráfica o necesitan gestión avanzada de descargas: +Para usuarios que prefieren aplicaciones GUI o necesitan una gestión avanzada de descargas: 1. Descargar e instalar [JDownloader](https://jdownloader.org/download/index) -2. Copie la URL del archivo en JDownloader -3. Configurar conexiones paralelas para descargas más rápidas +2. Copia la URL del archivo en JDownloader +3. Configura conexiones paralelas para descargas más rápidas **Ventajas:** * Interfaz gráfica -* Descarga en paralelo -* Mecanismos avanzados de reintento +* Descarga paralela +* Avanzados mecanismos de reintento * Soporte multiplataforma ## Verificación y extracción @@ -111,22 +280,22 @@ Después de descargar, verifique la integridad del archivo: :::callout type: info -### Disponibilidad de Suma de Comprobación +### Comprobante de disponibilidad 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 comprobació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,57 +305,139 @@ 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. +The + +title: Clarity Cheatsheet + +description: A quick reference for using Clarity on the Stacks blockchain. + +\--- + +\## Quick Start + +Get started with Clarity in just a few steps: + +1\. Install the Clarinet CLI +2\. Create a new Clarity project +3\. Write your first smart contract +4\. Deploy and test your contract + +\--- + +\## Clarity Basics + +Clarity is a domain-specific language (DSL) for writing smart contracts on the Stacks blockchain. It is a statically-typed, decidable language with a focus on safety and predictability. + +Some key features of Clarity: + +\- \*\*Explicitly Typed\*\*: All values in Clarity have an explicit type, which is checked at compile-time. +\- \*\*Decidable\*\*: Clarity programs are guaranteed to complete in a finite number of steps. +\- \*\*Deterministic\*\*: Clarity programs always produce the same output given the same inputs. +\- \*\*Secure\*\*: Clarity has a focus on security, with no runtime exceptions and no undefined behavior. + +\--- + +sidebarTitle: Additional Resources + +\- \[Clarity Documentation]\(https://docs.stacks.co/learn/clarity/) +\- \[Clarinet CLI]\(https://github.com/hirosystems/clarinet) +\- \[Clarity by Example]\(https://clarity-by-example.org/) `marf.sqlite.blobs` El archivo puede ser muy grande y puede tardar mucho tiempo en extraerlo. Asegúrese de tener suficiente espacio en el 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. **Usar gcloud para las descargas más rápidas** - requiere autenticación pero proporciona mejoras de velocidad significativas +2. **Descargar durante horas fuera de pico** - normalmente por la noche tarde o temprano por la mañana +3. **Use conexiones con cable** - evitar Wi-Fi para descargas grandes cuando sea posible +4. **Monitor el espacio en disco** - los archivos extraídos pueden ser 2-3 veces más grandes que los archivos comprimidos +5. **Consider la extracción por streaming** con gcloud para ahorrar espacio en el disco -## Preguntas frecuentes +FAQ + +\--- +title: Quick Answers to Common Questions +\--- + +\- \*\*title:\*\* Quick Answers to Common Questions +\- \*\*sidebarTitle:\*\* FAQ +\- \*\*description:\*\* Get quick answers to frequently asked questions about Clarity, Stacks, and developing on Clarinet. + +\--- + +\### What is Clarity? +Clarity is a smart contract language and platform for building decentralized applications (dApps) on the Bitcoin-based Stacks blockchain. + +\### What is Stacks? +Stacks is a layer-1 blockchain network that enables smart contracts and decentralized apps on Bitcoin. Stacks connects to the Bitcoin network but has its own native token, STX. + +\### What is Clarinet? +Clarinet is a local development environment and testing framework for building Clarity smart contracts. It allows developers to quickly write, test, and deploy their Clarity contracts. + +\--- +---- - + ¿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`. + Descargas de archivos grandes desde almacenamiento en la nube pueden ser interrumpidas debido a problemas de red, limitación de tarifas, o tiempos de espera de conexión. Utilice herramientas con capacidad de reanudación como `wget -c` o `gcloud storage cp`. - - ¿Qué método de descarga debo usar? + + ¿Cuál 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 las velocidades más rápidas, use + + Clarinet + + \---, Stacks, Clarity + + title: Clarinet Deployment + description: Deploy the Clarinet stack + sidebarTitle: Clarinet `gcloud storage cp` título: Estilo de diseño clarinete + descripción: --- un gran estilo de diseño de interfaz de usuario basado en Clarinet + sidebarTitle: Clarinet UI + + \--- + + Clarinet UI es un conjunto de herramientas de diseño e implementación que ayudan a los equipos a construir aplicaciones web con un estilo de interfaz de usuario coherente y moderno. + + \--- + + Características: + + \- Sólida base de componentes de interfaz de usuario listos para usar + \- Diseño y documentación de alta calidad + \- Totalmente compatible con Stacks y Clarity + \- Escalable y fácil de personalizar + \- Soporte continuo y actualizaciones regulares `wget -c`. Para confiabilidad sin autenticación, intenta un administrador de descargas como JDownloader. - - ¿Cuánto tiempo debería tardar una descarga? + + Cuánto tiempo debería tardar 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. + 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 mainnet (varios cientos de GB+) puede tardar 6-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í, use `wget -c`, `curl -C -`, o `gcloud storage cp` 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 cuando descarga desde Google Cloud Storage, lo que resulta en mejoras de velocidad de 2 a 3 veces. 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 e3d299cc9..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/tools/bitcoin-indexer/(indexer)/full-sync.mdx b/content/docs/es/tools/bitcoin-indexer/(indexer)/full-sync.mdx index d3e98d810..5964b74b9 100644 --- a/content/docs/es/tools/bitcoin-indexer/(indexer)/full-sync.mdx +++ b/content/docs/es/tools/bitcoin-indexer/(indexer)/full-sync.mdx @@ -1,66 +1,212 @@ --- title: Sincronización completa sidebarTitle: Sincronización completa -description: Ejecutar el Indexador de Bitcoin desde cero. +description: Ejecuta el Bitcoin Indexer desde cero y monitorea su progreso. isNew: true --- + import { ArrowRight, Check } from 'lucide-react'; -## Lo que aprenderás +## Qué aprenderás :::objectives -* Configurar e inicializar el Indexador de Bitcoin -* Ejecutar el indexador desde cero -* Monitorear el progreso de indexación de manera efectiva +- Configurar el Bitcoin Indexer antes de sincronizar. +- Ejecutar los servicios desde cero. +- Vigilar el avance de indexación y reaccionar a tiempo. ::: +```ts +// Herramientas mínimas +const requirements = ['bitcoind con txindex=1', 'PostgreSQL', '12+ núcleos recomendados']; +console.table(requirements); +``` + +## Prerrequisitos + :::prerequisites -* Bitcoin Core nodo ejecutándose con `txindex=1` -* PostgreSQL ejecutándose con bases de datos creadas -* Espacio de almacenamiento suficiente +- Nodo de Bitcoin Core corriendo con `txindex=1`. +- PostgreSQL con las bases de datos creadas. +- Espacio de almacenamiento suficiente para datos y logs. ::: :::callout -### Archivo bootstrap 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. +### Usa archivos bootstrap cuando sea posible +Si aún no lo haces, considera [inicializar desde archivos](/tools/bitcoin-indexer/archive-bootstrap) para ahorrarte días de sincronización. ::: -## Iniciar servicios de indexación +## Inicia los servicios de indexación -El Indexador de Bitcoin ejecuta servicios separados para cada metaprotocolo. Navega a tu directorio del indexador: +El Bitcoin Indexer ejecuta un servicio por metaprotocolo. Muévete al directorio del proyecto: ```terminal $ cd ~/bitcoin-indexer/bitcoin-indexer +# Todos los comandos siguientes asumen esta ruta base ``` -### Ordinales de Índice (incluye BRC-20) +### Indexar Ordinals (incluye BRC-20) ```terminal $ ./target/release/bitcoin-indexer ordinals service start --config-path=bitcoin-indexer-config.toml +# Crea las tablas necesarias y empieza a consumir tu nodo de Bitcoin ``` -### Runas de Índice +### Indexar Runes -En una terminal separada: +Abre otro terminal para mantener procesos separados: ```terminal $ ./target/release/bitcoin-indexer runes service start --config-path=bitcoin-indexer-config.toml +# Los dos servicios comparten configuración pero manejan colas independientes ``` :::callout type: info +### Compilar desde código fuente +Si descargaste el código desde GitHub, ejecuta `cargo build --release` antes de iniciar servicios para generar `target/release/bitcoin-indexer`. +::: + +## Monitorea el progreso + +### Revisa los logs + +El indexador imprime estadísticas continuas: + +```terminal +$ tail -f ~/bitcoin-indexer/bitcoin-indexer/ordinals.log +# Busca líneas con "blocks_behind" para confirmar que la brecha disminuye +``` + +### Consulta el estado vía API + +Cuando el servidor HTTP arranca (después de unos bloques): + +```terminal +$ curl http://localhost:3000/ordinals/v1/status +{ + "block_height": 819600, + "inscriptions_indexed": 52342567, + "latest_inscription_number": 52342567, + "sync_percentage": 99.93, + "blocks_behind": 523 +} +``` + +## Administración de servicios + +### Detener procesos con seguridad + +```terminal +$ ps aux | grep bitcoin-indexer +# Usa kill solo para los PIDs mostrados; evita SIGKILL para no corromper colas +``` + +### Reiniciar tras una interrupción + +El indexador retoma desde el último bloque procesado: + +```terminal +$ ./target/release/bitcoin-indexer ordinals service start --config-path=bitcoin-indexer-config.toml +# No necesitas limpiar PostgreSQL ni volver a descargar datos +``` + +## Configura systemd (opcional) + +Para entornos de producción conviene delegar el manejo a systemd. + +### Servicio de Ordinals + +```bash title="/etc/systemd/system/bitcoin-indexer-ordinals.service" +[Unit] +Description=Bitcoin Indexer - Ordinals Service +After=network.target postgresql.service bitcoind.service + +[Service] +Type=simple +User=bitcoin-indexer +WorkingDirectory=/home/bitcoin-indexer/bitcoin-indexer +ExecStart=/home/bitcoin-indexer/bitcoin-indexer/target/release/bitcoin-indexer ordinals service start --config-path=bitcoin-indexer-config.toml +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target +``` + +### Servicio de Runes + +```bash title="/etc/systemd/system/bitcoin-indexer-runes.service" +[Unit] +Description=Bitcoin Indexer - Runes Service +After=network.target postgresql.service bitcoind.service + +[Service] +Type=simple +User=bitcoin-indexer +WorkingDirectory=/home/bitcoin-indexer/bitcoin-indexer +ExecStart=/home/bitcoin-indexer/bitcoin-indexer/target/release/bitcoin-indexer runes service start --config-path=bitcoin-indexer-config.toml +Restart=on-failure +RestartSec=30 + +[Install] +WantedBy=multi-user.target +``` + +### Habilita y arranca + +```terminal +$ sudo systemctl daemon-reload +$ sudo systemctl enable bitcoin-indexer-ordinals bitcoin-indexer-runes +$ sudo systemctl start bitcoin-indexer-ordinals bitcoin-indexer-runes +$ sudo systemctl status bitcoin-indexer-ordinals +``` -### Compilando desde el código fuente +## Optimiza el rendimiento -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. +### Durante la sincronización inicial + +Asigna más recursos mientras alcanzas la punta de la cadena: + +```toml title="bitcoin-indexer-config.toml" +[resources] +cpu_core_available = 12 +bitcoind_rpc_threads = 10 +# Incrementa también la RAM disponible para PostgreSQL si es posible +``` + +### Después de alcanzar la punta + +Reduce el consumo para operación continua: + +```toml +[resources] +cpu_core_available = 4 +bitcoind_rpc_threads = 4 +# Mantén hilos mínimos mientras solo procesas nuevos bloques +``` + +## Verifica la indexación + +### Consulta endpoints clave + +```terminal +$ curl http://localhost:3000/ordinals/v1/inscriptions/0 +# Recibir un payload válido confirma que la API está lista +``` + +:::next-steps +- [Archivos de Hiro](/tools/bitcoin-indexer/archive-bootstrap): evita semanas de sincronización descargando datos preindexados. +- [Configuración avanzada](/tools/bitcoin-indexer/configuration): ajusta recursos y metaprotocolos para workloads específicos. ::: +\- Error indexing app XYZ: success -## Monitorear el progreso de indexación +Missing dependencies: +\- dependency B +\- dependency C -### Revisar los registros del servicio +Failed jobs: 1 +\- app ABC -El indexador genera información detallada sobre el progreso: +Completed jobs: 22 ```terminal $ tail -f ~/bitcoin-indexer/bitcoin-indexer/ordinals.log @@ -68,7 +214,7 @@ $ tail -f ~/bitcoin-indexer/bitcoin-indexer/ordinals.log ### Consultar el estado del indexador a través de la API -Una vez que el servidor API se inicia (generalmente después de unos pocos bloques): +Una vez que el servidor API se inicie (generalmente después de algunos bloques): ```terminal $ curl http://localhost:3000/ordinals/v1/status @@ -83,25 +229,37 @@ $ curl http://localhost:3000/ordinals/v1/status ## Gestión de servicios -### Detener servicios de manera controlada +### Detener los servicios de forma gradual ```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: +La indexadora se 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 + +title: Systemd service setup +description: Cómo configurar un servicio Systemd +sidebarTitle: Systemd service + +\--- +\# Systemd service setup + +Cómo configurar un servicio Systemd. + +Clarinet, Stacks y Clarity son términos que se deben mantener sin traducir. +--------------------------------------------------------------------------- -Para implementaciones de producción, utilice systemd para gestionar servicios: +Para despliegues de producción, utilice systemd para administrar servicios: -### Crear servicio de Ordinales +### Crear servicio de Ordinals ```bash title="/etc/systemd/system/bitcoin-indexer-ordinals.service" [Unit] @@ -120,7 +278,7 @@ RestartSec=30 WantedBy=multi-user.target ``` -### Crear servicio de Runas +### Crear servicio Runes ```bash title="/etc/systemd/system/bitcoin-indexer-runes.service" [Unit] @@ -139,7 +297,7 @@ RestartSec=30 WantedBy=multi-user.target ``` -### Habilitar e iniciar servicios +### Habilitar y comenzar servicios ```terminal $ sudo systemctl daemon-reload @@ -148,11 +306,11 @@ $ sudo systemctl start bitcoin-indexer-ordinals bitcoin-indexer-runes $ sudo systemctl status bitcoin-indexer-ordinals ``` -## Optimización del rendimiento +## Optimización de rendimiento ### Durante la sincronización inicial -Optimizar el rendimiento durante la puesta al día: +Optimizar para el rendimiento durante la recuperación: ```toml title="bitcoin-indexer-config.toml" [resources] @@ -161,7 +319,7 @@ cpu_core_available = 12 bitcoind_rpc_threads = 10 ``` -### Después de alcanzar la punta de la cadena +### Después de alcanzar el extremo de la cadena Reducir el uso de recursos para la operación en estado estable: @@ -172,19 +330,19 @@ cpu_core_available = 4 bitcoind_rpc_threads = 4 ``` -## Verificar la indexación exitosa +## Verificar indexación exitosa -### Verificar puntos finales de API +### Revisar puntos finales de API -Prueba que las APIs estén devolviendo datos: +Comprobar que las APIs devuelven datos: ```terminal -o $ curl http://localhost:3000/ordinals/v1/inscriptions/0 ``` -## Próximos pasos +## Siguientes 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 de Hiro](/tools/bitcoin-indexer/archive-bootstrap): Salta semanas de indexación al arrancar desde los archivos previamente indexados de Hiro. +- [Configuración avanzada](/tools/bitcoin-indexer/configuration): Configura el Bitcoin Indexer para un rendimiento óptimo y personaliza la configuración de cada metaprotocolo. ::: 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..7c5cdf56a --- /dev/null +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/edit-update.mdx @@ -0,0 +1,123 @@ +--- +title: Editar y Actualizar +description: Modificar chainhooks existentes usando el SDK +--- +Modifica chainhooks existentes incluyendo filtros, acciones y opciones. La interfaz de usuario de la plataforma no admite edición - usa el SDK en su lugar. + +:::callout +La interfaz de usuario de la plataforma no admite actualmente la edición de chainhooks. Debes usar el SDK o API para realizar actualizaciones. +::: + +## updateChainhook + +### Campos Mutables vs Inmutables + +| Mutable (Puede Actualizar) | Immutable (No Puede Actualizar) | +|---------------------|---------------------------| +| `name` | `chain` | +| `filters` I notice that you've provided the translation rules and setup, but the actual text to translate appears to be just a single vertical bar "|" character. + +Since this is just a formatting character and not text content that needs translation, the translation would be: + +| `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' }], + }, +}); +``` + +## Patrones de Actualización Comunes + +| Update Type | Example | +|-------------|---------| +| **URL del webhook** | `{ action: { type: 'http_post', url: 'https://new-server.com/webhooks' } }` | +| **Nombre** | `{ name: 'production-ft-tracker' }` | +| **Filtros** | `{ filters: { events: [{ type: 'nft_transfer' }] } }` | + +### Agregar Filtro de Evento (Conservando el Existente) + +```typescript +const current = await client.getChainhook('chainhook-uuid'); + +await client.updateChainhook('chainhook-uuid', { + filters: { + events: [ + ...(current.definition.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 }, +}); +``` + +## Ejemplo de cURL + +### Actualizar Chainhook + +```bash +curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ + "name": "Updated chainhook name", + "action": { "type": "http_post", "url": "https://new-server.com/webhooks" } + }' +``` + +### Respuesta + +* **Éxito**: HTTP `204 No Content` +* **Verificar**: Obtener el chainhook para confirmar cambios + +```typescript +await client.updateChainhook('chainhook-uuid', { name: 'New name' }); +const updated = await client.getChainhook('chainhook-uuid'); +console.log('Updated:', updated.definition.name); +``` + +### Ejemplo: Actualización Aditiva Segura + +```typescript +// ✅ Good: Fetch first +const current = await client.getChainhook(uuid); +await client.updateChainhook(uuid, { + filters: { + events: [...current.definition.filters.events, newEvent], + }, +}); + +// ❌ Bad: Might overwrite +await client.updateChainhook(uuid, { + filters: { events: [newEvent] }, +}); +``` + +:::next-steps +* [Lista y Obtener](/tools/chainhook/list-fetch): Recuperar información de chainhook antes de actualizar +* [Referencia de Filtros](/tools/chainhook/reference/filters): Explora 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..d94e4a28d --- /dev/null +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/evaluate.mdx @@ -0,0 +1,62 @@ +--- +title: Evaluar Chainhook +description: Probar chainhooks contra bloques pasados específicos +--- +Prueba tu configuración de chainhook contra bloques pasados específicos para pruebas, depuración e indexación histórica. + +## evaluateChainhook + +### Métodos de Evaluación + +| Method | Parameter | Example | +|--------|-----------|---------| +| **Por altura** | `block_height` I don't see any text provided to translate. Please provide the text you'd like me to translate from English to Spanish, and I'll follow all the critical rules you've specified. `{ block_height: 100000 }` | +| **Por hash** | `index_block_hash` | `{ index_block_hash: '0xa204...' }` | + +### 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!, +}); + +await client.evaluateChainhook('chainhook-uuid', { + block_height: 100000, +}); +``` + +Returns HTTP `204 No Content`. Si los filtros coinciden, la carga útil del webhook se envía a tu `action.url`. + +### cURL + +```bash +curl -X POST \ + "https://api.testnet.hiro.so/chainhooks/me/chainhook-uuid/evaluate" \ + -H "x-api-key: $HIRO_API_KEY" \ + -H "content-type: application/json" \ + -d '{ "block_height": 100000 }' +``` + +## Casos de Uso + +| Caso de Uso | Descripción | Ejemplo | +|----------|-------------|---------| +| **Depurar** | Investigar eventos perdidos | Evaluar bloque específico que debería haber activado | +| **Rellenar** | Indexar datos históricos | Procesar bloques pasados después de crear chainhook | +| **Reprocesar** | Corregir problemas del manejador de webhook | Re-evaluar después de corregir errores | + +## Mejores Prácticas + +| Práctica | Ejemplo | +|----------|---------| +| **Limitación de velocidad** | Agregar retraso de 100ms entre solicitudes | +| **Prueba primero** | Evaluar 2-3 bloques de muestra antes del procesamiento masivo | +| **Usar altura** | Más fácil de iterar que los hashes de bloques | + +:::next-steps +* [Registrar & Habilitar](/tools/chainhook/register-enable): Crear chainhooks para evaluar +* [Referencia de Filtros](/tools/chainhook/reference/filters): Configura qué eventos coincidir +::: 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..959061ee5 --- /dev/null +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/list-fetch.mdx @@ -0,0 +1,147 @@ +--- +title: Lista y Obtención +description: Recuperar información de chainhook usando el SDK +--- +# Lista y Obtener + +Aprende cómo listar y obtener chainhooks usando el SDK de Chainhook. + +## getChainhooks + +Recuperar una lista paginada de todos tus chainhooks. + +### TypeScript + +```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 de Paginación + +```typescript +// Get specific page with custom limit +const chainhooks = await client.getChainhooks({ + limit: 50, + offset: 100, +}); + +// This fetches 50 chainhooks starting from position 100 +``` + +### cURL + +```bash +curl -sS "https://api.testnet.hiro.so/chainhooks/me?limit=20&offset=0" \ + -H "x-api-key: $HIRO_API_KEY" +``` + +### Respuesta + +```json +{ + "limit": 20, + "offset": 0, + "total": 45, + "results": [ + { + "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", + "definition": { + "version": "1", + "name": "my-chainhook", + "chain": "stacks", + "network": "testnet", + "filters": { ... }, + "action": { ... } + }, + "status": { + "enabled": true, + "type": "active", + "last_evaluated_at": 1696432154, + "last_evaluated_block_height": 150000, + "last_occurrence_at": 1696432100, + "last_occurrence_block_height": 149980, + "evaluated_block_count": 1000, + "occurrence_count": 42 + } + }, + // ... more chainhooks + ] +} +``` + +*** + +## getChainhook + +Retrieve a specific chainhook by UUID. + +### TypeScript + +```typescript +const chainhook = await client.getChainhook('be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185'); +``` + +### cURL + +```bash +curl -sS "https://api.testnet.hiro.so/chainhooks/me/" \ + -H "x-api-key: $HIRO_API_KEY" +``` + +### Respuesta + +```json +{ + "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", + "definition": { + "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 + } + }, + "status": { + "enabled": true, + "type": "active", + "last_evaluated_at": 1696432154, + "last_evaluated_block_height": 150000, + "last_occurrence_at": 1696432100, + "last_occurrence_block_height": 149980, + "evaluated_block_count": 1000, + "occurrence_count": 42 + } +} +``` + +:::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..5cf3c8102 --- /dev/null +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/manage-keys.mdx @@ -0,0 +1,66 @@ +--- +title: Gestionar Claves +description: Rotar secretos de consumidor y validar cada entrega de Chainhook +--- +## Lo que aprenderás + +:::objectives +* Crear/rotar un secreto de consumidor Chainhook. +* Valida las solicitudes de webhook verificando el `Authorization` header. +::: + +## Requisitos previos + +:::prerequisites +* Clave API de Hiro almacenada como `CHAINHOOKS_API_KEY`. +* Chainhook UUID you want to protect. +* Runtime de Node.js (el ejemplo usa Fastify). +::: + +## Validación de solicitudes de webhook con un secreto de consumidor + +Los chainhooks adjuntan un `Authorization: Bearer ` encabezado a cada intento de webhook, proporcionándote un simple protocolo de intercambio de secreto compartido. + +1. Rotar el secreto con `await client.rotateConsumerSecret(chainhookUuid)` (o el `/chainhooks/{uuid}/secret` API) cada vez que necesites un nuevo token. +2. Persistir lo devuelto `secret` en tu administrador de secretos y recárgalo al inicio del proceso o a través de un bucle de actualización corto. +3. Rechazar las entregas de webhook cuyas `Authorization` el encabezado no es igual a `Bearer `. + +### Rotar/crear secreto del consumidor + +```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.CHAINHOOKS_API_KEY!, +}); + +let consumerSecret: string = await client.rotateConsumerSecret(chainhookUuid).secret; +``` + +### Servidor Fastify de ejemplo + +```ts +import Fastify from 'fastify'; + +const server = Fastify(); + +server.post('/webhook', async (request, reply) => { + if (!consumerSecret) { + reply.code(503).send({ error: 'consumer secret unavailable' }); + return; + } + + const authHeader = request.headers.authorization; + if (authHeader !== `Bearer ${consumerSecret}`) { + 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..b7efea2fc --- /dev/null +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/register-enable.mdx @@ -0,0 +1,265 @@ +--- +title: Registrar y Habilitar +description: Crear y activar chainhooks usando el SDK +--- +# Registrar y Habilitar + +Aprende cómo crear y activar chainhooks usando el SDK de Chainhook. + +## registrarChainhook + +Crea una nueva configuración de chainhook. Por defecto, los nuevos chainhooks se crean en estado deshabilitado a menos que `enable_on_registration` se establece en `true`. + +### TypeScript + +```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!, +}); + +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, // Enable immediately + }, +}); + +console.log('Chainhook UUID:', chainhook.uuid); +console.log('Enabled:', chainhook.status.enabled); // true +``` + +### cURL + +```bash +curl -sS -X POST "https://api.testnet.hiro.so/chainhooks/me" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ + "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 + } + }' +``` + +### Respuesta + +```json +{ + "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", + "definition": { + "version": "1", + "name": "my-chainhook", + "chain": "stacks", + "network": "testnet", + "filters": { ... }, + "action": { ... }, + "options": { ... } + }, + "status": { + "enabled": true, + "type": "active" + } +} +``` + +### Habilitar más tarde + +Si no estableces `enable_on_registration`, el chainhook será creado pero deshabilitado: + +```typescript +const chainhook = await client.registerChainhook({ + version: '1', + name: 'my-chainhook', + chain: 'stacks', + network: 'testnet', + filters: { /* ... */ }, + action: { /* ... */ }, + // No options.enable_on_registration +}); + +console.log('Enabled:', chainhook.status.enabled); // false + +// Enable it later +await client.enableChainhook(chainhook.uuid, true); +``` + +*** + +## enableChainhook + +Enable or disable a single chainhook by UUID. This allows you to pause a chainhook without deleting it. + +### TypeScript + +```typescript +// Enable a chainhook +await client.enableChainhook('chainhook-uuid', true); + +// Disable a chainhook +await client.enableChainhook('chainhook-uuid', false); +``` + +### cURL + +Habilitar: + +```bash +curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me//enabled" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ "enabled": true }' +``` + +Desactivar: + +```bash +curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me//enabled" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ "enabled": false }' +``` + +### Respuesta + +Returns HTTP `204 No Content` en caso de éxito. + +*** + +## bulkEnableChainhooks + +Habilitar o deshabilitar múltiples chainhooks a la vez usando filtros. Esto es útil para gestionar muchos chainhooks de forma programática. + +### Por UUIDs + +Habilitar chainhooks específicos por sus UUID (máximo 200): + +```typescript +await client.bulkEnableChainhooks({ + enabled: true, + filters: { + 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: { + webhook_url: 'https://example.com/webhooks', + }, +}); +``` + +### Por Estado + +Habilitar todos los chainhooks con un estado específico: + +```typescript +await client.bulkEnableChainhooks({ + enabled: true, + filters: { + statuses: ['inactive'], + }, +}); +``` + +### Filtros Combinados + +Usa múltiples filtros juntos: + +```typescript +await client.bulkEnableChainhooks({ + enabled: false, // Disable matching chainhooks + filters: { + webhook_url: 'https://old-server.com/webhooks', + statuses: ['active'], + }, +}); +``` + +This will disable all active chainhooks that POST to the old webhook URL. + +### cURL + +```bash +curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/enabled" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d '{ + "enabled": true, + "filters": { + "uuids": ["uuid-1", "uuid-2"], + "webhook_url": "https://example.com/webhooks" + } + }' +``` + +### Respuesta + +```json +{ + "updated": 5, + "results": [ + { + "uuid": "uuid-1", + "enabled": true + }, + { + "uuid": "uuid-2", + "enabled": true + } + ] +} +``` + +:::next-steps +* [Evaluar](/tools/chainhook/evaluate): Prueba 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/(overview)/faq.mdx b/content/docs/es/tools/chainhook/(overview)/faq.mdx new file mode 100644 index 000000000..69111f31f --- /dev/null +++ b/content/docs/es/tools/chainhook/(overview)/faq.mdx @@ -0,0 +1,376 @@ +--- +title: FAQ +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 contacta a [support@hiro.so](mailto\:support@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 determinarán los precios de Chainhooks después de la Beta? + + + Los Chainhooks se cobrarán usando un modelo de créditos por cada entrega, con usuarios capaces de seleccionar diferentes límites de créditos dependiendo de su uso. Para los usuarios de Chainhooks 2.0 Beta, sus límites post-beta serán inicialmente determinados por su nivel de suscripción existente de Hiro. + + + + +## Gestión de Versiones + + + + ¿Qué pasará con los Chainhooks heredados existentes que se 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 o a través de la API durante y después del período Beta 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 van a estar activas al mismo tiempo, ¿cómo accedemos a ambas? + + + En la Plataforma, los chainhooks v1 son de solo lectura. Puedes verlos y seguirá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. Ver el [Guía de Migración](/tools/chainhook/migration) para un tutorial completo. + + + + +## Plataforma vs SDK + + + + ¿Puedo editar mis v2 Chainhooks? + + + La Plataforma actualmente no admite la edición de chainhooks existentes. Debe 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 frente al SDK? + + +
+

Usa la Plataforma cuando:

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

Utiliza el SDK/API cuando:

+ +
    +
  • Necesitas editar chainhooks existentes
  • +
  • Gestión programática de chainhook
  • +
  • Automatizando operaciones de chainhook
  • +
  • Integración en pipelines de CI/CD
  • +
  • Gestión de muchos chainhooks a escala
  • +
+
+
+
+
+ +## Empezando + + + + ¿Cómo obtengo una clave API de Hiro? + + +
+
    +
  1. Visita 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 de API
  8. +
+ +

Almacena tu clave de 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? + + +
+
    +
  • Mainnet - 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 el Referencia de Filtros para sintaxis correcta
  • +
+ +

4. Verificar logs del endpoint del webhook

+ +
    +
  • Ensure your endpoint returns HTTP 200
  • +
  • Busca mensajes de error en los registros de tu aplicación
  • +
+
+
+
+ + + Estoy recibiendo 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? + + +
+

Utiliza el método evaluateChainhook() para probar tu configuración contra bloques específicos antes de habilitarla:

+ +
+          
+            {`// 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 Comunes + + + + ¿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 cadena. + + + + + ¿Cómo manejan los chainhooks las reorganizaciones de blockchain? + + +
+

Chainhook detecta automáticamente las reorgs y envía ambos rollback y aplicar eventos. Siempre procesa primero los eventos de rollback 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 el manejo de errores y monitoreo adecuados 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 al **#chainhook** canal encendido [Discord](https://stacks.chat/) bajo las Herramientas para Desarrolladores de Hiro +* **Horario de Oficina Semanal**: Todos los jueves a las 11am ET ([agregar al calendario](https://www.addevent.com/event/oL21905919)) +* **Soporte por Email**: [support@hiro.so](mailto\:support@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 +* [SDK Inicio Rápido](/tools/chainhook/quickstart): Comience 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..566cbb7b3 --- /dev/null +++ b/content/docs/es/tools/chainhook/(overview)/migration.mdx @@ -0,0 +1,334 @@ +--- +title: Migrando de v1 a v2 +description: Guía para migrar chainhooks v1 heredados a Chainhook 2.0 Beta +--- +:::callout +### Gestión de chainhooks v1 + +Los chainhooks heredados v1 permanecen de solo lectura en la interfaz de usuario 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. +* Convertir filtros basados en predicados en definiciones explícitas de eventos v2. +* Registra v2 chainhooks, prueba la entrega y retira los originales de forma segura. +::: + +## Prerrequisitos + +:::prerequisites +* API access to both the Platform API and Chainhook REST API (same `HIRO_API_KEY`). +* SDK Local (`@hirosystems/chainhooks-client`) o `curl` for REST calls. +* Variable de entorno `HIRO_API_KEY` configurado para la CLI y ejemplos de código. +::: + + + + ### Chainhooks de inventario v1 + + Utiliza la API de la Plataforma para obtener todos los chainhooks que aún se activan en producción. + + #### CLI + + ```bash + curl -sS "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks" \ + -H "content-type: application/json" + ``` + + #### TypeScript + + ```typescript + export async function listV1Chainhooks() { + const response = await fetch( + `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks`, + { headers: { 'content-type': 'application/json' } } + ); + + if (!response.ok) throw new Error(response.statusText); + return (await response.json()) as any[]; + } + + const chainhooks = await listV1Chainhooks(); + console.log(`Found ${chainhooks.length} v1 chainhooks`); + ``` + + + + ### Inspeccionar un chainhook + + Pull the full definition for each UUID so you can convert custom filters and metadata. + + #### CLI + + ```bash + curl -sS \ + "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks/$CHAINHOOK_UUID" \ + -H "content-type: application/json" + ``` + + #### TypeScript + + ```typescript + export async function getV1Chainhook(uuid: string) { + const response = await fetch( + `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks/${uuid}`, + { headers: { 'content-type': 'application/json' } } + ); + + if (!response.ok) throw new Error(response.statusText); + return await response.json(); + } + ``` + + + + ### Configuración de mapa a v2 + + Traduce las estructuras v1 a campos v2 antes de aprovisionar nuevos hooks. + + | v1 Concepto | v2 Objetivo | Notas | + |-------------|-------------|-------| + | `if_this.scope` I notice that you've provided the translation rules and setup, but the actual text to translate appears to be just a single vertical bar "|" character. + + Since this is just a formatting character and not text content, the translation would be: + + | `filters.events[].type` | Reemplazar `scope/action` combos con un solo tipo de evento. | + | `if_this.actions` | `type` | `transfer` mapea a `*_transfer`. | + | `then_that.http_post.url` I notice that you've provided the rules and instructions for translation, but the actual text to translate appears to be just a single vertical bar "|". + + Since there's no meaningful text content to translate from English to Spanish, I'll return the same character: + + | `action.url` | v2 maneja los secretos automáticamente. | + | `networks.mainnet` I don't see any text provided after "Text to translate:" in your message. Could you please provide the text you'd like me to translate from English to Spanish? `network: "mainnet"` | Crea un hook v2 por red. | + | `authorization_header` | Gestión de secretos de webhook | Usar `rotateConsumerSecret()` después del registro. | + + #### Ejemplo de Conversión + + ```jsonc + // v1 (Platform API) + { + "name": "stx-transfers", + "networks": { + "mainnet": { + "if_this": { "scope": "stx_event", "actions": ["transfer"] }, + "then_that": { "http_post": { "url": "https://example.com/webhooks" } } + } + } + } + ``` + + ```typescript + // v2 (SDK) + import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + + const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, + apiKey: process.env.HIRO_API_KEY!, + }); + + 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, + }, + }); + ``` + + + + ### Crear v2 chainhooks + + Provision each chainhook with the SDK or REST API, mirroring the mapped filters. + + #### REST + + ```bash + curl -sS -X POST "https://api.mainnet.hiro.so/chainhooks/v1/me/" \ + -H "content-type: application/json" \ + -H "x-api-key: $HIRO_API_KEY" \ + -d @v2-chainhook.json + ``` + + #### SDK de Chainhook + + ```typescript + const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL[config.network], + apiKey: process.env.HIRO_API_KEY!, + }); + const chainhook = await client.registerChainhook(config); + ``` + + + + ### Validar y retirar v1 + + Transmite eventos a través de ambas versiones, confirma la entrega, luego limpia las definiciones heredadas. + + :::callout + type: info + + ### Mejores prácticas + + Mantén tanto los hooks v1 como v2 activos hasta que verifiques la paridad de entrega. + ::: + + #### Verificaciones de Habilitación + + ```typescript + const chainhook = await client.getChainhook(v2Uuid); + console.log(chainhook.status.enabled); + ``` + + #### Eliminar con cURL + + ```bash + curl -sS -X DELETE \ + "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks/$CHAINHOOK_UUID" \ + -H "content-type: application/json" + ``` + + #### Eliminar con SDK + + ```typescript + export async function deleteV1Chainhook(uuid: string) { + 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' } } + ); + + if (!response.ok) throw new Error(response.statusText); + return await response.json(); + } + ``` + + + +## Plantilla de Automatización + +Usa este script como punto de partida cuando tengas muchos chainhooks similares que mover. + + + + Script de migración masiva + + + ```typescript + import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + + const PLATFORM_API = 'https://api.platform.hiro.so'; + const API_KEY = process.env.HIRO_API_KEY!; + const scopeMap: Record> = { + stx_event: { transfer: 'stx_transfer' }, + ft_event: { transfer: 'ft_transfer' }, + nft_event: { transfer: 'nft_transfer' }, + }; + + const v2Client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.mainnet, + apiKey: API_KEY, + }); + + const pickNetwork = (networks: any) => + networks.mainnet ? ['mainnet', networks.mainnet] : ['testnet', networks.testnet]; + + async function migrate() { + const res = await fetch(`${PLATFORM_API}/v1/ext/${API_KEY}/chainhooks`); + if (!res.ok) throw new Error(res.statusText); + const hooks = (await res.json()) as any[]; + + for (const hook of hooks) { + const [network, details] = pickNetwork(hook.networks); + const action = details.if_this.actions?.[0] ?? ''; + const eventType = scopeMap[details.if_this.scope]?.[action] ?? details.if_this.scope; + + const payload = { + version: '1', + name: hook.name, + chain: hook.chain, + network, + filters: { + events: [ + { + type: eventType, + ...(details.if_this.contract_identifier && { + contract_identifier: details.if_this.contract_identifier, + }), + }, + ], + }, + action: { type: 'http_post', url: details.then_that.http_post.url }, + options: { decode_clarity_values: true, enable_on_registration: true }, + }; + + try { + const created = await v2Client.registerChainhook(payload); + console.log(`Migrated ${hook.uuid} → ${created.uuid}`); + } catch (error) { + console.error(`Failed for ${hook.uuid}`, error); + } + } + } + + migrate(); + ``` + + + + +## Referencia de Traducción de Filtros + +### Ámbitos comunes + +| Alcance v1 | Acciones Típicas | v2 `type` | Extras | +|----------|-----------------|-----------|--------| +| `stx_event` I notice that you've provided the translation rules but the actual text to translate appears to be just a single vertical bar "|". + +| `transfer` I notice that you've provided the translation rules and instructions, but the actual text to translate appears to be just a single vertical bar "|" character. + +Since this is just a formatting character and not actual text content, the translation would be: + +| `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` I don't see any text provided to translate. Could you please share the text you'd like me to translate from English to Spanish? `nft_transfer` · `nft_mint` | Proporcionar `asset_identifier`. | + +### Ejemplo + +```jsonc +// v1 filter +{ + "scope": "stx_event", + "actions": ["transfer"] +} +``` + +```jsonc +// v2 filter +{ + "events": [ + { + "type": "stx_transfer", + "recipient": "SP3FBR2AGKQX0..." + } + ] +} +``` + +:::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..7a3ff1219 100644 --- a/content/docs/es/tools/chainhook/index.mdx +++ b/content/docs/es/tools/chainhook/index.mdx @@ -1,56 +1,44 @@ --- 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. +description: Chainhook es un indexador consciente de reorganización que proporciona datos confiables de blockchain para Bitcoin y Stacks. 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. +:::callout +type: warn -![Descripción general de Chainhook](/images/tools/chainhook/overview.svg) +### Chainhook 2.0 Beta -## Características principales +Chainhook 2.0 se encuentra actualmente en versión beta. Durante este período, nos enfocamos en aprender sobre confiabilidad, rendimiento y experiencia del desarrollador. Si encuentras problemas o tienes comentarios, por favor comunícate con [support@hiro.so](mailto\:support@hiro.so). +::: -* **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 +## Descripción general -## Instalación +Chainhook es un indexador consciente de reorganizaciones que te permite construir flujos de eventos personalizados desde las blockchains de Bitcoin y Stacks. A diferencia de los indexadores tradicionales, Chainhook maneja automáticamente las reorganizaciones de blockchain, asegurando que tus datos se mantengan precisos sin reindexación manual. - - ```terminal !! macOS - $ brew install chainhook - ``` +Con Chainhook 2.0, puedes gestionar chainhooks a través de: - ```terminal !! Linux - $ sudo snap install chainhook - ``` +* **[Chainhook SDK](/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 !! Windows - $ winget install HiroSystems.Chainhook - ``` +Para explorar las funciones de Chainhook con IA, copia y pega [llms.txt](/tools/chainhook/llms.txt) en tu LLM de elección. - ```terminal !! Cargo - $ git clone https://github.com/hirosystems/chainhook.git - $ cd chainhook && cargo chainhook-install - ``` - +## Características principales -## Próximos pasos +* **Indexación consciente de reorganización** - Maneja automáticamente las bifurcaciones y reorganizaciones de 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. +- [Inicio Rápido del SDK](/tools/chainhook/quickstart): Comienza con el SDK de Chainhook en 5 minutos +- [Inicio Rápido de la Plataforma](/tools/chainhook/platform-quickstart): Crea tu primer chainhook en la interfaz de usuario de la plataforma ::: :::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. +Contáctanos en el #chainhook canal encendido [Discord](https://stacks.chat/) bajo la sección Herramientas de Desarrollador de Hiro. ::: 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..4fb1db550 --- /dev/null +++ b/content/docs/es/tools/chainhook/reference/filters.mdx @@ -0,0 +1,366 @@ +--- +title: Referencia de Filtros +description: Referencia completa para todos los tipos de filtros de Chainhook +--- +# Referencia de Filtros + +Los filtros definen qué eventos de blockchain activarán tu chainhook—usa la matriz de abajo para elegir la entrada correcta para `filters.events[]`. + +| Filter | Cuándo usar | +|--------|-------------| +| `ft_event` | FT: capturar *cada* SIP-010 transferir, acuñar o quemar a través de activos. | +| `ft_transfer` | FT: follow a single asset such as USDC; optionally add `sender`/`receiver` para disparadores a nivel de billetera. | +| `ft_mint` | FT: rastrea expansiones de suministro o flujos de entrada de puente para un activo (establecer `asset_identifier`). | +| `ft_burn` | FT: rastrear canjes o contracciones de suministro (establecer `asset_identifier`). | +| `nft_event` | NFT: monitorea cada transferencia, acuñación o quema para todas las colecciones. | +| `nft_transfer` | NFT: seguir una colección SIP-009; agregar `sender`, `receiver`, o `value` para focalización de propietario/token. | +| `nft_mint` | NFT: observa cada nueva acuñación para una colección (establecer `asset_identifier`). | +| `nft_burn` | NFT: capturar quemadas o canjes (establecer `asset_identifier`). | +| `stx_event` | STX: capturar todas las transferencias nativas, acuñaciones o quemas. | +| `stx_transfer` | STX: rastrear cada transferencia; agregar `sender` o `receiver` para destacar directores específicos. | +| `contract_deploy` | Contratos: reaccionar a nuevos contratos que ingresan a la red. | +| `contract_call` | Contratos: observar cada invocación; reducir con `contract_identifier` y `function_name`. | +| `contract_log` | Contratos: capturar `print`/salida de log de un contrato (establecer `contract_identifier`). | +| `coinbase` | Sistema: observar las recompensas de los mineros llegando a la cadena. | +| `tenure_change` | Sistema: rastrear actualizaciones de tenencia de Proof-of-Transfer; agregar `cause` I don't see any text provided 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 Token Fungible (FT) + +### Cualquier Evento FT + +Coincidir con cualquier evento de token fungible (transferencia, quema o acuñación): + +```json +{ + "type": "ft_event" +} +``` + +### Transferencia FT + +Coincidencia de transferencias FT para un activo específico: + +```json +{ + "type": "ft_transfer", + "asset_identifier": "SP...ABC.ft::usdc" +} +``` + +Filtrar por remitente: + +```json +{ + "type": "ft_transfer", + "asset_identifier": "SP...ABC.ft::usdc", + "sender": "SP...FROM" +} +``` + +Filtrar por destinatario: + +```json +{ + "type": "ft_transfer", + "asset_identifier": "SP...ABC.ft::usdc", + "receiver": "SP...TO" +} +``` + +### Acuñación de FT + +Hacer coincidir eventos de acuñación FT: + +```json +{ + "type": "ft_mint", + "asset_identifier": "SP...ABC.ft::usdc" +} +``` + +### FT Burn + +Coincidir eventos de quema FT: + +```json +{ + "type": "ft_burn", + "asset_identifier": "SP...ABC.ft::usdc" +} +``` + +*** + +## Eventos de Tokens No Fungibles (NFT) + +### Cualquier Evento NFT + +Coincide con cualquier evento de NFT (transferencia, quema o acuñación): + +```json +{ + "type": "nft_event" +} +``` + +### Transferencia de NFT + +Coincide transferencias de NFT para una colección específica: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible" +} +``` + +Filtrar por remitente: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + "sender": "SP...FROM" +} +``` + +Filtrar por receptor: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + "receiver": "SP...TO" +} +``` + +Filtrar por ID de token específico: + +```json +{ + "type": "nft_transfer", + "asset_identifier": "SP...COLL.nft::collectible", + "value": "u123" +} +``` + +### Acuñación de NFT + +Coincidir eventos de acuñación de NFT: + +```json +{ + "type": "nft_mint", + "asset_identifier": "SP...COLL.nft::collectible" +} +``` + +### Quema de NFT + +Hacer coincidir eventos de quema de NFT: + +```json +{ + "type": "nft_burn", + "asset_identifier": "SP...COLL.nft::collectible" +} +``` + +*** + +## Eventos STX + +### Cualquier Evento STX + +Coincide 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", + "sender": "SP...SENDER" +} +``` + +Filtrar por receptor: + +```json +{ + "type": "stx_transfer", + "receiver": "SP...RECEIVER" +} +``` + +*** + +## Eventos de Contratos + +### Implementación de Contrato + +Coincidir con cualquier despliegue de contrato: + +```json +{ + "type": "contract_deploy" +} +``` + +Filtrar por implementador: + +```json +{ + "type": "contract_deploy", + "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", + "contract_identifier": "SP...XYZ.counter" +} +``` + +Coincidir llamadas a una función específica: + +```json +{ + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter", + "function_name": "increment" +} +``` + +Filtrar por llamador: + +```json +{ + "type": "contract_call", + "contract_identifier": "SP...XYZ.counter", + "function_name": "increment", + "sender": "SP...CALLER" +} +``` + +### Registro de Contrato + +Coincidir eventos de impresión del contrato: + +```json +{ + "type": "contract_log", + "contract_identifier": "SP...XYZ.counter" +} +``` + +Filtrar por remitente de transacción: + +```json +{ + "type": "contract_log", + "contract_identifier": "SP...XYZ.counter", + "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", + "cause": "block_found" +} +``` + +Cambios de tenencia coincidentes por causa (extendido): + +```json +{ + "type": "tenure_change", + "cause": "extended" +} +``` + +*** + +## Combinando Filtros + +Puedes combinar múltiples filtros en el `filters.events` arreglo. 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" + } + ] + } +} +``` + +This chainhook will trigger on either DIKO transfers **o** llamadas al `increment` función. + +:::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..79d038e6d --- /dev/null +++ b/content/docs/es/tools/chainhook/reference/options.mdx @@ -0,0 +1,237 @@ +--- +title: Referencia de 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. El `options` el campo es opcional y puede omitirse o establecerse en `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 despliegues | +| `include_contract_source_code` | booleano | `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 y Bitcoin) | +| `include_block_signatures` | boolean | `false` | Incluir firmas de bloque y firmantes | +| `enable_on_registration` | booleano | `false` | Habilitar chainhook inmediatamente en la creación | +| `expire_after_evaluations` | integer | ninguno | Expira después de N bloques evaluados | +| `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 you've provided the translation rules but the actual text to translate appears to be just a single colon ":". + + : `boolean` +* **Predeterminado**: `false` + +```json +{ + "options": { + "decode_clarity_values": true + } +} +``` + +**I don't see any text provided to translate. Please provide the specific text you'd like me to translate from English to Spanish, and I'll follow all the rules you've outlined: + +1\. Keep "---" exactly as "---" +2\. Don't translate field names like "title:", "description:", "sidebarTitle:" +3\. Only translate the text values after colons +4\. Preserve all Markdown/YAML formatting +5\. Keep "Clarinet", "Stacks", and "Clarity" untranslated + +Please share the text you need translated.**: + +```json +{ + "hex": "0x01000000000000000000000002690ed9fe", + "repr": "u10352515582", + "type": "uint" +} +``` + +*** + +### include\_contract\_abi + +Incluir el ABI del contrato en las operaciones de implementación. Esto mejora la tipificación de argumentos al decodificar. + +* **Tipo**: `boolean` +* **Predeterminado**: `false` + +```json +{ + "options": { + "include_contract_abi": true + } +} +``` + +*** + +### include\_contract\_source\_code + +Incluye el código fuente del contrato en las operaciones de implementación. + +* **Tipo**: `boolean` +* **Por defecto**I notice that the text you provided to translate appears to be just a single colon (:). Since there is no actual content to translate, I'll return it as-is: + + : `false` + +```json +{ + "options": { + "include_contract_source_code": true + } +} +``` + +*** + +### include\_post\_conditions + +Incluir post-condiciones decodificadas en los metadatos de la transacción. + +* **Tipo**: `boolean` +* **Predeterminado**: `false` + +```json +{ + "options": { + "include_post_conditions": true + } +} +``` + +*** + +### include\_raw\_transactions + +Incluir hex de transacción sin procesar en metadatos de transacción. + +* **Tipo**: `boolean` +* **Predeterminado**: `false` + +```json +{ + "options": { + "include_raw_transactions": true + } +} +``` + +*** + +### include\_block\_metadata + +Incluye metadatos de bloque tanto para bloques de Stacks como de Bitcoin, con costos de ejecución, conteo 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 instructions but the actual text to translate appears to be just a single colon ":". + + Since there's no actual content to translate (just a colon), the translation remains: + + : `boolean` +* **Por defecto**: `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 crearse. + +* **Tipo**: `boolean` +* **Predeterminado**: `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 + +Vencer automáticamente el chainhook después de evaluar este número de bloques. + +* **Tipo**: `integer` +* **Por defecto**: Ninguno (sin expiración) + +```json +{ + "options": { + "expire_after_evaluations": 10000 + } +} +``` + +Esta chainhook expirará después de evaluar 10,000 bloques. + +*** + +### expire\_after\_occurrences + +Expira automáticamente el chainhook después de este número de ocurrencias coincidentes. + +* **Tipo**: `integer` +* **Predeterminado**: Ninguno (sin vencimiento) + +```json +{ + "options": { + "expire_after_occurrences": 250 + } +} +``` + +Este chainhook expirará después de activarse 250 veces. + +*** + +:::next-steps +* [Referencia de Filtro](/tools/chainhook/reference/filters): Aprende sobre todos los filtros de eventos disponibles +* [Anatomía del Payload](/tools/chainhook/reference/payload-anatomy): Comprende 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..ccb9f9c10 --- /dev/null +++ b/content/docs/es/tools/chainhook/reference/payload-anatomy.mdx @@ -0,0 +1,249 @@ +--- +title: Anatomía del Payload +description: Entendiendo la estructura de las cargas útiles de webhook de Chainhook +--- +## Descripción General de la Carga Útil + +Un payload de Chainhook consiste en dos secciones principales: + +1. **evento** - Contiene los datos de la blockchain (bloques, transacciones, operaciones) +2. **chainhook** - 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` la matriz 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": [...] +} +``` + +### Revertir 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 reversión. Tu aplicación debe revertir cualquier cambio de estado de los bloques reversados. +::: + +*** + +## Estructura de Transacción + +Cada transacción en el `transactions` la matriz 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" + } +} +``` + +*** + +## Array de Operaciones + +Cada transacción incluye un `operations` array que describe cambios de estado. Las operaciones se indexan 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): Configura qué eventos activan tu chainhook +* [Referencia de Opciones](/tools/chainhook/reference/options): Personalizar contenido del payload +::: diff --git a/idioma.lock b/idioma.lock index c5280a2a3..3958ad9e0 100644 --- a/idioma.lock +++ b/idioma.lock @@ -12,10 +12,6 @@ files: 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: @@ -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: 62f80fcdec2c44b2136a101c9c6d3047 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: 1374bfd8028e4ee7d503de95ed825465 + translations: + es: true + content/docs/en/tools/chainhook/index.mdx: + content: c5fc6d932dfee92e6c17ac3b84a7ef07 + 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: ac17346943a1ea78d977f3dcc9d59e05 + translations: + es: true + content/docs/en/tools/chainhook/reference/options.mdx: + content: ed56067c12b0a843e91b7fbf3c9d5ff1 + translations: + es: true + content/docs/en/tools/chainhook/reference/filters.mdx: + content: 707b20a16c590f7d82d0146753c42514 + 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: c3c2fefd74654e640d2e0d5bb55c0ba5 + translations: + es: true + content/docs/en/tools/chainhook/(overview)/faq.mdx: + content: e62210687f8a8b470e3db19736fe05f1 + translations: + es: true + content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx: + content: 7ec61d2eb0b5a25314ce81bfd886ba25 + translations: + es: true + content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx: + content: 38b7f6f5fd4a9ac0b134b01c7c02b18f + translations: + es: true + content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx: + content: 72572a7c158185675cc64b72570b7168 + 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: 0ae296e25033cbc80fa903c403ad1580 + translations: + es: true + content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx: + content: 299dd17ff88a2943f167de34bd758645 + 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 From 467cde1a3edb41a0d4f2f8cf0b6cfcf4ff1173bd Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 12:29:33 -0600 Subject: [PATCH 10/16] whitelist domains for proxy --- app/api/proxy/route.ts | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/app/api/proxy/route.ts b/app/api/proxy/route.ts index f34102a67..bb5b58857 100644 --- a/app/api/proxy/route.ts +++ b/app/api/proxy/route.ts @@ -1,6 +1,9 @@ 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'; @@ -12,6 +15,20 @@ export async function POST(request: Request) { 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( @@ -23,7 +40,7 @@ export async function POST(request: Request) { const upstreamHeaders = new Headers(); if (headers && typeof headers === 'object') { for (const [key, value] of Object.entries(headers)) { - if (typeof value === 'string') { + if (typeof value === 'string' && !BLOCKED_REQUEST_HEADERS.has(key.toLowerCase())) { upstreamHeaders.set(key, value); } } @@ -38,7 +55,7 @@ export async function POST(request: Request) { requestInit.body = typeof body === 'string' ? body : JSON.stringify(body); } - const upstreamResponse = await fetch(url, requestInit); + const upstreamResponse = await fetch(parsedUrl, requestInit); const contentType = upstreamResponse.headers.get('content-type') ?? ''; let data: unknown; @@ -48,10 +65,16 @@ export async function POST(request: Request) { 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: Object.fromEntries(upstreamResponse.headers), + headers: sanitizedHeaders, data, }); } catch (error) { From 61c89c07b68f925d5b0a7088352b48da44ab9696 Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 12:40:30 -0600 Subject: [PATCH 11/16] update migration guides --- .../tools/chainhook/(overview)/migration.mdx | 75 +----- .../bitcoin-indexer/(indexer)/full-sync.mdx | 234 +++--------------- .../tools/chainhook/(overview)/migration.mdx | 134 ++-------- idioma.lock | 4 +- 4 files changed, 68 insertions(+), 379 deletions(-) diff --git a/content/docs/en/tools/chainhook/(overview)/migration.mdx b/content/docs/en/tools/chainhook/(overview)/migration.mdx index 666cc2bd8..b35fd3191 100644 --- a/content/docs/en/tools/chainhook/(overview)/migration.mdx +++ b/content/docs/en/tools/chainhook/(overview)/migration.mdx @@ -194,81 +194,10 @@ export async function deleteV1Chainhook(uuid: string) { -## Automation Template -Use this script as a starting point when you have many similar chainhooks to move. - - - - Bulk migration script - - -```typescript -import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; - -const PLATFORM_API = 'https://api.platform.hiro.so'; -const API_KEY = process.env.HIRO_API_KEY!; -const scopeMap: Record> = { - stx_event: { transfer: 'stx_transfer' }, - ft_event: { transfer: 'ft_transfer' }, - nft_event: { transfer: 'nft_transfer' }, -}; - -const v2Client = new ChainhooksClient({ - baseUrl: CHAINHOOKS_BASE_URL.mainnet, - apiKey: API_KEY, -}); - -const pickNetwork = (networks: any) => - networks.mainnet ? ['mainnet', networks.mainnet] : ['testnet', networks.testnet]; - -async function migrate() { - const res = await fetch(`${PLATFORM_API}/v1/ext/${API_KEY}/chainhooks`); - if (!res.ok) throw new Error(res.statusText); - const hooks = (await res.json()) as any[]; - - for (const hook of hooks) { - const [network, details] = pickNetwork(hook.networks); - const action = details.if_this.actions?.[0] ?? ''; - const eventType = scopeMap[details.if_this.scope]?.[action] ?? details.if_this.scope; - - const payload = { - version: '1', - name: hook.name, - chain: hook.chain, - network, - filters: { - events: [ - { - type: eventType, - ...(details.if_this.contract_identifier && { - contract_identifier: details.if_this.contract_identifier, - }), - }, - ], - }, - action: { type: 'http_post', url: details.then_that.http_post.url }, - options: { decode_clarity_values: true, enable_on_registration: true }, - }; - - try { - const created = await v2Client.registerChainhook(payload); - console.log(`Migrated ${hook.uuid} → ${created.uuid}`); - } catch (error) { - console.error(`Failed for ${hook.uuid}`, error); - } - } -} - -migrate(); -``` - - - - - ## Filter Translation Reference + ### Common scopes -| v1 Scope | Typical Actions | v2 `type` | Extras | +| 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`. | 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 5964b74b9..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,220 +1,76 @@ --- title: Sincronización completa sidebarTitle: Sincronización completa -description: Ejecuta el Bitcoin Indexer desde cero y monitorea su progreso. +description: Ejecute el Indexador de Bitcoin desde cero. isNew: true --- - import { ArrowRight, Check } from 'lucide-react'; -## Qué aprenderás +## Lo que aprenderás :::objectives -- Configurar el Bitcoin Indexer antes de sincronizar. -- Ejecutar los servicios desde cero. -- Vigilar el avance de indexación y reaccionar a tiempo. +* Configurar y configurar el Indexador de Bitcoin +* Ejecutar el indexador desde cero +* Monitorear el progreso de indexación de manera efectiva ::: -```ts -// Herramientas mínimas -const requirements = ['bitcoind con txindex=1', 'PostgreSQL', '12+ núcleos recomendados']; -console.table(requirements); -``` - -## Prerrequisitos +## Requisitos previos :::prerequisites -- Nodo de Bitcoin Core corriendo con `txindex=1`. -- PostgreSQL con las bases de datos creadas. -- Espacio de almacenamiento suficiente para datos y logs. +* nodo de Bitcoin Core ejecutándose con `txindex=1` +* PostgreSQL ejecutándose con bases de datos creadas +* Espacio de almacenamiento suficiente ::: :::callout -### Usa archivos bootstrap cuando sea posible -Si aún no lo haces, considera [inicializar desde archivos](/tools/bitcoin-indexer/archive-bootstrap) para ahorrarte días de sincronización. +### Arranque de archivo recomendado + +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. ::: -## Inicia los servicios de indexación +## Iniciar servicios de indexación -El Bitcoin Indexer ejecuta un servicio por metaprotocolo. Muévete al directorio del proyecto: +El Indexador de Bitcoin ejecuta servicios separados para cada metaprotocolo. Navega a tu directorio del indexador: ```terminal $ cd ~/bitcoin-indexer/bitcoin-indexer -# Todos los comandos siguientes asumen esta ruta base ``` -### Indexar Ordinals (incluye BRC-20) +### Índice de Ordinales (incluye BRC-20) ```terminal $ ./target/release/bitcoin-indexer ordinals service start --config-path=bitcoin-indexer-config.toml -# Crea las tablas necesarias y empieza a consumir tu nodo de Bitcoin ``` -### Indexar Runes +### Runas Índice -Abre otro terminal para mantener procesos separados: +En una terminal separada: ```terminal $ ./target/release/bitcoin-indexer runes service start --config-path=bitcoin-indexer-config.toml -# Los dos servicios comparten configuración pero manejan colas independientes ``` :::callout type: info -### Compilar desde código fuente -Si descargaste el código desde GitHub, ejecuta `cargo build --release` antes de iniciar servicios para generar `target/release/bitcoin-indexer`. -::: - -## Monitorea el progreso - -### Revisa los logs - -El indexador imprime estadísticas continuas: - -```terminal -$ tail -f ~/bitcoin-indexer/bitcoin-indexer/ordinals.log -# Busca líneas con "blocks_behind" para confirmar que la brecha disminuye -``` - -### Consulta el estado vía API - -Cuando el servidor HTTP arranca (después de unos bloques): - -```terminal -$ curl http://localhost:3000/ordinals/v1/status -{ - "block_height": 819600, - "inscriptions_indexed": 52342567, - "latest_inscription_number": 52342567, - "sync_percentage": 99.93, - "blocks_behind": 523 -} -``` - -## Administración de servicios - -### Detener procesos con seguridad - -```terminal -$ ps aux | grep bitcoin-indexer -# Usa kill solo para los PIDs mostrados; evita SIGKILL para no corromper colas -``` - -### Reiniciar tras una interrupción - -El indexador retoma desde el último bloque procesado: - -```terminal -$ ./target/release/bitcoin-indexer ordinals service start --config-path=bitcoin-indexer-config.toml -# No necesitas limpiar PostgreSQL ni volver a descargar datos -``` - -## Configura systemd (opcional) - -Para entornos de producción conviene delegar el manejo a systemd. - -### Servicio de Ordinals - -```bash title="/etc/systemd/system/bitcoin-indexer-ordinals.service" -[Unit] -Description=Bitcoin Indexer - Ordinals Service -After=network.target postgresql.service bitcoind.service - -[Service] -Type=simple -User=bitcoin-indexer -WorkingDirectory=/home/bitcoin-indexer/bitcoin-indexer -ExecStart=/home/bitcoin-indexer/bitcoin-indexer/target/release/bitcoin-indexer ordinals service start --config-path=bitcoin-indexer-config.toml -Restart=on-failure -RestartSec=30 - -[Install] -WantedBy=multi-user.target -``` - -### Servicio de Runes - -```bash title="/etc/systemd/system/bitcoin-indexer-runes.service" -[Unit] -Description=Bitcoin Indexer - Runes Service -After=network.target postgresql.service bitcoind.service - -[Service] -Type=simple -User=bitcoin-indexer -WorkingDirectory=/home/bitcoin-indexer/bitcoin-indexer -ExecStart=/home/bitcoin-indexer/bitcoin-indexer/target/release/bitcoin-indexer runes service start --config-path=bitcoin-indexer-config.toml -Restart=on-failure -RestartSec=30 - -[Install] -WantedBy=multi-user.target -``` - -### Habilita y arranca - -```terminal -$ sudo systemctl daemon-reload -$ sudo systemctl enable bitcoin-indexer-ordinals bitcoin-indexer-runes -$ sudo systemctl start bitcoin-indexer-ordinals bitcoin-indexer-runes -$ sudo systemctl status bitcoin-indexer-ordinals -``` - -## Optimiza el rendimiento -### Durante la sincronización inicial - -Asigna más recursos mientras alcanzas la punta de la cadena: - -```toml title="bitcoin-indexer-config.toml" -[resources] -cpu_core_available = 12 -bitcoind_rpc_threads = 10 -# Incrementa también la RAM disponible para PostgreSQL si es posible -``` - -### Después de alcanzar la punta - -Reduce el consumo para operación continua: - -```toml -[resources] -cpu_core_available = 4 -bitcoind_rpc_threads = 4 -# Mantén hilos mínimos mientras solo procesas nuevos bloques -``` - -## Verifica la indexación - -### Consulta endpoints clave - -```terminal -$ curl http://localhost:3000/ordinals/v1/inscriptions/0 -# Recibir un payload válido confirma que la API está lista -``` +### Construyendo desde el código fuente -:::next-steps -- [Archivos de Hiro](/tools/bitcoin-indexer/archive-bootstrap): evita semanas de sincronización descargando datos preindexados. -- [Configuración avanzada](/tools/bitcoin-indexer/configuration): ajusta recursos y metaprotocolos para workloads específicos. +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. ::: -\- Error indexing app XYZ: success -Missing dependencies: -\- dependency B -\- dependency C +## Monitorear el progreso de indexación -Failed jobs: 1 -\- app ABC +### Verificar registros del servicio -Completed jobs: 22 +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 inicie (generalmente después de algunos bloques): +Una vez que el servidor API se inicia (generalmente después de unos pocos bloques): ```terminal $ curl http://localhost:3000/ordinals/v1/status @@ -229,7 +85,7 @@ $ curl http://localhost:3000/ordinals/v1/status ## Gestión de servicios -### Detener los servicios de forma gradual +### Detener servicios de manera elegante ```terminal $ ps aux | grep bitcoin-indexer @@ -237,27 +93,15 @@ $ ps aux | grep bitcoin-indexer ### Reiniciar después de interrupción -La indexadora 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 - -title: Systemd service setup -description: Cómo configurar un servicio Systemd -sidebarTitle: Systemd service - -\--- -\# Systemd service setup +## Configuración del servicio Systemd -Cómo configurar un servicio Systemd. - -Clarinet, Stacks y Clarity son términos que se deben mantener sin traducir. ---------------------------------------------------------------------------- - -Para despliegues de producción, utilice systemd para administrar servicios: +Para despliegues en producción, usa systemd para gestionar servicios: ### Crear servicio de Ordinals @@ -278,7 +122,7 @@ RestartSec=30 WantedBy=multi-user.target ``` -### Crear servicio Runes +### Crear servicio de Runes ```bash title="/etc/systemd/system/bitcoin-indexer-runes.service" [Unit] @@ -297,7 +141,7 @@ RestartSec=30 WantedBy=multi-user.target ``` -### Habilitar y comenzar servicios +### Habilitar e iniciar servicios ```terminal $ sudo systemctl daemon-reload @@ -306,11 +150,11 @@ $ sudo systemctl start bitcoin-indexer-ordinals bitcoin-indexer-runes $ sudo systemctl status bitcoin-indexer-ordinals ``` -## Optimización de rendimiento +## Optimización del rendimiento ### Durante la sincronización inicial -Optimizar para el rendimiento durante la recuperación: +Optimizar para el rendimiento durante la actualización: ```toml title="bitcoin-indexer-config.toml" [resources] @@ -319,9 +163,9 @@ cpu_core_available = 12 bitcoind_rpc_threads = 10 ``` -### Después de alcanzar el extremo de la cadena +### 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] @@ -332,17 +176,15 @@ bitcoind_rpc_threads = 4 ## Verificar indexación exitosa -### Revisar puntos finales de API +### Verificar endpoints de API -Comprobar que las APIs devuelven datos: +Prueba que las APIs estén devolviendo datos: ```terminal -o $ curl http://localhost:3000/ordinals/v1/inscriptions/0 ``` -## Siguientes pasos - :::next-steps -- [Archivos de Hiro](/tools/bitcoin-indexer/archive-bootstrap): Salta semanas de indexación al arrancar desde los archivos previamente indexados de Hiro. -- [Configuración avanzada](/tools/bitcoin-indexer/configuration): Configura el Bitcoin Indexer para un rendimiento óptimo y personaliza la configuración de cada 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/(overview)/migration.mdx b/content/docs/es/tools/chainhook/(overview)/migration.mdx index 566cbb7b3..c0ab65dab 100644 --- a/content/docs/es/tools/chainhook/(overview)/migration.mdx +++ b/content/docs/es/tools/chainhook/(overview)/migration.mdx @@ -1,34 +1,34 @@ --- -title: Migrando de v1 a v2 +title: Migración de v1 a v2 description: Guía para migrar chainhooks v1 heredados a Chainhook 2.0 Beta --- :::callout ### Gestión de chainhooks v1 -Los chainhooks heredados v1 permanecen de solo lectura en la interfaz de usuario de la plataforma; los gestionas a través del [API de Plataforma](/apis/platform-api/reference/chainhooks/list). +Los chainhooks v1 heredados permanecen de solo lectura en la interfaz de usuario 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. -* Convertir filtros basados en predicados en definiciones explícitas de eventos v2. -* Registra v2 chainhooks, prueba la entrega y retira los originales de forma segura. +* Captura y analiza 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. ::: -## Prerrequisitos +## Requisitos previos :::prerequisites * API access to both the Platform API and Chainhook REST API (same `HIRO_API_KEY`). * SDK Local (`@hirosystems/chainhooks-client`) o `curl` for REST calls. -* Variable de entorno `HIRO_API_KEY` configurado para la CLI y ejemplos de código. +* Variable de entorno `HIRO_API_KEY` establecido para la CLI y ejemplos de código. ::: - ### Chainhooks de inventario v1 + ### Inventario v1 chainhooks - Utiliza la API de la Plataforma para obtener todos los chainhooks que aún se activan en producción. + Utiliza la API de la Plataforma para obtener cada chainhook que aún se ejecuta en producción. #### CLI @@ -84,27 +84,19 @@ Los chainhooks heredados v1 permanecen de solo lectura en la interfaz de usuario - ### Configuración de mapa a v2 + ### Configuración de mapeo a v2 - Traduce las estructuras v1 a campos v2 antes de aprovisionar nuevos hooks. + Traduce estructuras v1 a campos v2 antes de aprovisionar nuevos hooks. - | v1 Concepto | v2 Objetivo | Notas | - |-------------|-------------|-------| - | `if_this.scope` I notice that you've provided the translation rules and setup, but the actual text to translate appears to be just a single vertical bar "|" character. - - Since this is just a formatting character and not text content, the translation would be: - - | `filters.events[].type` | Reemplazar `scope/action` combos con un solo tipo de evento. | - | `if_this.actions` | `type` | `transfer` mapea a `*_transfer`. | - | `then_that.http_post.url` I notice that you've provided the rules and instructions for translation, but the actual text to translate appears to be just a single vertical bar "|". - - Since there's no meaningful text content to translate from English to Spanish, I'll return the same character: - - | `action.url` | v2 maneja los secretos automáticamente. | - | `networks.mainnet` I don't see any text provided after "Text to translate:" in your message. Could you please provide the text you'd like me to translate from English to Spanish? `network: "mainnet"` | Crea un hook v2 por red. | + | v1 Concept | v2 Target | Notes | + |------------|-----------|-------| + | `if_this.scope` | `filters.events[].type` | Reemplazar `scope/action` combos con un solo tipo de evento. | + | `if_this.actions` | `type` | `transfer` se mapea a `*_transfer`. | + | `then_that.http_post.url` | `action.url` | v2 maneja los 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 de Conversión + #### Conversión de Ejemplo ```jsonc // v1 (Platform API) @@ -149,7 +141,7 @@ Los chainhooks heredados v1 permanecen de solo lectura en la interfaz de usuario - ### Crear v2 chainhooks + ### Crear chainhooks v2 Provision each chainhook with the SDK or REST API, mirroring the mapped filters. @@ -217,94 +209,20 @@ Los chainhooks heredados v1 permanecen de solo lectura en la interfaz de usuario -## Plantilla de Automatización - -Usa este script como punto de partida cuando tengas muchos chainhooks similares que mover. - - - - Script de migración masiva - - - ```typescript - import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; - - const PLATFORM_API = 'https://api.platform.hiro.so'; - const API_KEY = process.env.HIRO_API_KEY!; - const scopeMap: Record> = { - stx_event: { transfer: 'stx_transfer' }, - ft_event: { transfer: 'ft_transfer' }, - nft_event: { transfer: 'nft_transfer' }, - }; - - const v2Client = new ChainhooksClient({ - baseUrl: CHAINHOOKS_BASE_URL.mainnet, - apiKey: API_KEY, - }); - - const pickNetwork = (networks: any) => - networks.mainnet ? ['mainnet', networks.mainnet] : ['testnet', networks.testnet]; - - async function migrate() { - const res = await fetch(`${PLATFORM_API}/v1/ext/${API_KEY}/chainhooks`); - if (!res.ok) throw new Error(res.statusText); - const hooks = (await res.json()) as any[]; - - for (const hook of hooks) { - const [network, details] = pickNetwork(hook.networks); - const action = details.if_this.actions?.[0] ?? ''; - const eventType = scopeMap[details.if_this.scope]?.[action] ?? details.if_this.scope; - - const payload = { - version: '1', - name: hook.name, - chain: hook.chain, - network, - filters: { - events: [ - { - type: eventType, - ...(details.if_this.contract_identifier && { - contract_identifier: details.if_this.contract_identifier, - }), - }, - ], - }, - action: { type: 'http_post', url: details.then_that.http_post.url }, - options: { decode_clarity_values: true, enable_on_registration: true }, - }; - - try { - const created = await v2Client.registerChainhook(payload); - console.log(`Migrated ${hook.uuid} → ${created.uuid}`); - } catch (error) { - console.error(`Failed for ${hook.uuid}`, error); - } - } - } - - migrate(); - ``` - - - - ## Referencia de Traducción de Filtros ### Ámbitos comunes -| Alcance v1 | Acciones Típicas | v2 `type` | Extras | +| v1 | Acciones Típicas | v2 | Extras | |----------|-----------------|-----------|--------| -| `stx_event` I notice that you've provided the translation rules but the actual text to translate appears to be just a single vertical bar "|". - -| `transfer` I notice that you've provided the translation rules and instructions, but the actual text to translate appears to be just a single vertical bar "|" character. +| `stx_event` I notice that you've provided the translation rules and instructions, but the actual text to translate appears to be just a single vertical bar "|". -Since this is just a formatting character and not actual text content, the translation would be: - -| `stx_transfer` | Incluir `sender` o `recipient` filtros según sea necesario. | +| `transfer` | `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` I don't see any text provided to translate. Could you please share the text you'd like me to translate from English to Spanish? `nft_transfer` · `nft_mint` | Proporcionar `asset_identifier`. | +| `nft_event` | `transfer`, `mint` 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 vertical bar "|" shown. + +Could you please provide the actual text you'd like me to translate from English to Spanish? `nft_transfer` · `nft_mint` | Proporcionar `asset_identifier`. | ### Ejemplo @@ -330,5 +248,5 @@ Since this is just a formatting character and not actual text content, the trans :::next-steps * [Documentación del SDK](/tools/chainhook/chainhook-sdk): Documentación del SDK -* [Referencia de Filtros](/tools/chainhook/reference/filters): Referencia de filtros +* [Referencia de Filtros](/tools/chainhook/reference/filters): Referencia de Filtros ::: diff --git a/idioma.lock b/idioma.lock index 3958ad9e0..546a357e6 100644 --- a/idioma.lock +++ b/idioma.lock @@ -309,7 +309,7 @@ files: translations: es: true content/docs/en/tools/bitcoin-indexer/(indexer)/full-sync.mdx: - content: 62f80fcdec2c44b2136a101c9c6d3047 + content: 1b2d61c0e243295c2312a87328389cac translations: es: true content/docs/en/tools/bitcoin-indexer/(indexer)/configuration.mdx: @@ -1321,7 +1321,7 @@ files: translations: es: true content/docs/en/tools/chainhook/(overview)/migration.mdx: - content: c3c2fefd74654e640d2e0d5bb55c0ba5 + content: 8e4474f6ed7b00d94d48d8d41568c6cd translations: es: true content/docs/en/tools/chainhook/(overview)/faq.mdx: From ba444622ec7b34c1204800c6db8029c2ff75b8fe Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 15:18:27 -0600 Subject: [PATCH 12/16] update cards to use beta badges --- app/[locale]/(home)/_pages/page.en.tsx | 1 + app/[locale]/(home)/tools/_pages/page.en.tsx | 3 + components/card.tsx | 60 ++++++++++++++++---- 3 files changed, 54 insertions(+), 10 deletions(-) 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." /> ; 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}

From 5694a6d1018a8d151c16e3b9a6a2f3870df40c41 Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 15:47:43 -0600 Subject: [PATCH 13/16] add custom sidebar labels --- components/layouts/docs.tsx | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/components/layouts/docs.tsx b/components/layouts/docs.tsx index b494dfba3..6cbafaf35 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,14 @@ export function SidebarItem({ item, children }: { item: PageTree.Node; children: } if (item.type === 'separator') { + const { label, badge } = parseSeparatorMeta(item.name); return ( -

{item.name}

+
+

{label || item.name}

+ {badge ? ( + {badge.toUpperCase()} + ) : null} +
); } From f567c64edc09c976955b7b5d20fd50f7be09281b Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 20:20:36 -0600 Subject: [PATCH 14/16] update docskit components --- components/docskit/code-group.tsx | 2 +- components/docskit/code.client.tsx | 46 +++++++---- components/docskit/copy-button.tsx | 5 +- components/layouts/docs.tsx | 3 +- content/docs/en/resources/guides/index.mdx | 4 +- .../en/resources/snippets/build-a-stx-pc.mdx | 48 ------------ .../en/resources/snippets/build-an-ft-pc.mdx | 34 --------- .../en/resources/snippets/build-an-nft-pc.mdx | 31 -------- .../snippets/build-an-unsigned-tx.mdx | 51 ------------- .../snippets/check-for-duplicates.mdx | 40 ---------- .../snippets/convert-btc-to-stx-address.mdx | 47 ------------ .../snippets/convert-string-to-principal.mdx | 53 ------------- .../snippets/create-a-random-burn-address.mdx | 38 ---------- .../snippets/create-a-sponsored-tx.mdx | 69 ----------------- .../snippets/create-sha256-hash-clarity.mdx | 34 --------- .../snippets/create-sha256-hash-stacks-js.mdx | 46 ----------- .../resources/snippets/deploy-a-contract.mdx | 55 -------------- ...e-principal-addresses-between-networks.mdx | 65 ---------------- .../derive-stacks-address-from-keys.mdx | 40 ---------- .../fetch-testnet-bitcoin-on-regtest.mdx | 46 ----------- .../snippets/filter-items-from-a-list.mdx | 37 --------- .../snippets/generate-a-secret-key.mdx | 32 -------- .../resources/snippets/generate-a-wallet.mdx | 35 --------- .../snippets/generate-random-number.mdx | 47 ------------ .../get-account-details-from-wallet.mdx | 53 ------------- ...er-function-to-restrict-contract-calls.mdx | 36 --------- content/docs/en/resources/snippets/index.mdx | 76 ------------------- .../integrate-api-keys-using-stacksjs.mdx | 38 ---------- content/docs/en/resources/snippets/meta.json | 38 ---------- .../snippets/return-an-entry-from-a-map.mdx | 47 ------------ .../snippets/transfer-a-sip10-token.mdx | 69 ----------------- .../en/resources/snippets/transfer-stx.mdx | 50 ------------ content/docs/en/resources/templates/index.mdx | 8 -- content/docs/en/resources/templates/meta.json | 5 -- .../tools/chainhook/(chainhook-sdk)/meta.json | 2 +- .../en/tools/chainhook/(overview)/faq.mdx | 15 +++- .../tools/chainhook/(overview)/migration.mdx | 4 +- content/docs/en/tools/chainhook/index.mdx | 10 +-- content/docs/en/tools/chainhook/meta.json | 2 +- .../en/tools/contract-monitoring/index.mdx | 2 - .../es/resources/snippets/build-a-stx-pc.mdx | 47 ------------ .../es/resources/snippets/build-an-ft-pc.mdx | 33 -------- .../es/resources/snippets/build-an-nft-pc.mdx | 30 -------- .../snippets/build-an-unsigned-tx.mdx | 50 ------------ .../snippets/check-for-duplicates.mdx | 39 ---------- .../snippets/convert-btc-to-stx-address.mdx | 46 ----------- .../snippets/convert-string-to-principal.mdx | 52 ------------- .../snippets/create-a-random-burn-address.mdx | 37 --------- .../snippets/create-a-sponsored-tx.mdx | 68 ----------------- .../snippets/create-sha256-hash-clarity.mdx | 33 -------- .../snippets/create-sha256-hash-stacks-js.mdx | 45 ----------- .../resources/snippets/deploy-a-contract.mdx | 54 ------------- ...e-principal-addresses-between-networks.mdx | 64 ---------------- .../derive-stacks-address-from-keys.mdx | 39 ---------- .../fetch-testnet-bitcoin-on-regtest.mdx | 45 ----------- .../snippets/filter-items-from-a-list.mdx | 36 --------- .../snippets/generate-a-secret-key.mdx | 31 -------- .../resources/snippets/generate-a-wallet.mdx | 34 --------- .../snippets/generate-random-number.mdx | 46 ----------- .../get-account-details-from-wallet.mdx | 52 ------------- ...er-function-to-restrict-contract-calls.mdx | 35 --------- content/docs/es/resources/snippets/index.mdx | 75 ------------------ .../integrate-api-keys-using-stacksjs.mdx | 37 --------- content/docs/es/resources/snippets/meta.json | 38 ---------- .../snippets/return-an-entry-from-a-map.mdx | 46 ----------- .../snippets/transfer-a-sip10-token.mdx | 68 ----------------- .../es/resources/snippets/transfer-stx.mdx | 49 ------------ content/docs/es/resources/templates/index.mdx | 6 -- content/docs/es/resources/templates/meta.json | 5 -- content/docs/es/tools/chainhook/meta.json | 2 +- 70 files changed, 59 insertions(+), 2546 deletions(-) delete mode 100644 content/docs/en/resources/snippets/build-a-stx-pc.mdx delete mode 100644 content/docs/en/resources/snippets/build-an-ft-pc.mdx delete mode 100644 content/docs/en/resources/snippets/build-an-nft-pc.mdx delete mode 100644 content/docs/en/resources/snippets/build-an-unsigned-tx.mdx delete mode 100644 content/docs/en/resources/snippets/check-for-duplicates.mdx delete mode 100644 content/docs/en/resources/snippets/convert-btc-to-stx-address.mdx delete mode 100644 content/docs/en/resources/snippets/convert-string-to-principal.mdx delete mode 100644 content/docs/en/resources/snippets/create-a-random-burn-address.mdx delete mode 100644 content/docs/en/resources/snippets/create-a-sponsored-tx.mdx delete mode 100644 content/docs/en/resources/snippets/create-sha256-hash-clarity.mdx delete mode 100644 content/docs/en/resources/snippets/create-sha256-hash-stacks-js.mdx delete mode 100644 content/docs/en/resources/snippets/deploy-a-contract.mdx delete mode 100644 content/docs/en/resources/snippets/derive-principal-addresses-between-networks.mdx delete mode 100644 content/docs/en/resources/snippets/derive-stacks-address-from-keys.mdx delete mode 100644 content/docs/en/resources/snippets/fetch-testnet-bitcoin-on-regtest.mdx delete mode 100644 content/docs/en/resources/snippets/filter-items-from-a-list.mdx delete mode 100644 content/docs/en/resources/snippets/generate-a-secret-key.mdx delete mode 100644 content/docs/en/resources/snippets/generate-a-wallet.mdx delete mode 100644 content/docs/en/resources/snippets/generate-random-number.mdx delete mode 100644 content/docs/en/resources/snippets/get-account-details-from-wallet.mdx delete mode 100644 content/docs/en/resources/snippets/helper-function-to-restrict-contract-calls.mdx delete mode 100644 content/docs/en/resources/snippets/index.mdx delete mode 100644 content/docs/en/resources/snippets/integrate-api-keys-using-stacksjs.mdx delete mode 100644 content/docs/en/resources/snippets/meta.json delete mode 100644 content/docs/en/resources/snippets/return-an-entry-from-a-map.mdx delete mode 100644 content/docs/en/resources/snippets/transfer-a-sip10-token.mdx delete mode 100644 content/docs/en/resources/snippets/transfer-stx.mdx delete mode 100644 content/docs/en/resources/templates/index.mdx delete mode 100644 content/docs/en/resources/templates/meta.json delete mode 100644 content/docs/es/resources/snippets/build-a-stx-pc.mdx delete mode 100644 content/docs/es/resources/snippets/build-an-ft-pc.mdx delete mode 100644 content/docs/es/resources/snippets/build-an-nft-pc.mdx delete mode 100644 content/docs/es/resources/snippets/build-an-unsigned-tx.mdx delete mode 100644 content/docs/es/resources/snippets/check-for-duplicates.mdx delete mode 100644 content/docs/es/resources/snippets/convert-btc-to-stx-address.mdx delete mode 100644 content/docs/es/resources/snippets/convert-string-to-principal.mdx delete mode 100644 content/docs/es/resources/snippets/create-a-random-burn-address.mdx delete mode 100644 content/docs/es/resources/snippets/create-a-sponsored-tx.mdx delete mode 100644 content/docs/es/resources/snippets/create-sha256-hash-clarity.mdx delete mode 100644 content/docs/es/resources/snippets/create-sha256-hash-stacks-js.mdx delete mode 100644 content/docs/es/resources/snippets/deploy-a-contract.mdx delete mode 100644 content/docs/es/resources/snippets/derive-principal-addresses-between-networks.mdx delete mode 100644 content/docs/es/resources/snippets/derive-stacks-address-from-keys.mdx delete mode 100644 content/docs/es/resources/snippets/fetch-testnet-bitcoin-on-regtest.mdx delete mode 100644 content/docs/es/resources/snippets/filter-items-from-a-list.mdx delete mode 100644 content/docs/es/resources/snippets/generate-a-secret-key.mdx delete mode 100644 content/docs/es/resources/snippets/generate-a-wallet.mdx delete mode 100644 content/docs/es/resources/snippets/generate-random-number.mdx delete mode 100644 content/docs/es/resources/snippets/get-account-details-from-wallet.mdx delete mode 100644 content/docs/es/resources/snippets/helper-function-to-restrict-contract-calls.mdx delete mode 100644 content/docs/es/resources/snippets/index.mdx delete mode 100644 content/docs/es/resources/snippets/integrate-api-keys-using-stacksjs.mdx delete mode 100644 content/docs/es/resources/snippets/meta.json delete mode 100644 content/docs/es/resources/snippets/return-an-entry-from-a-map.mdx delete mode 100644 content/docs/es/resources/snippets/transfer-a-sip10-token.mdx delete mode 100644 content/docs/es/resources/snippets/transfer-stx.mdx delete mode 100644 content/docs/es/resources/templates/index.mdx delete mode 100644 content/docs/es/resources/templates/meta.json 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/layouts/docs.tsx b/components/layouts/docs.tsx index 6cbafaf35..aa29c4418 100644 --- a/components/layouts/docs.tsx +++ b/components/layouts/docs.tsx @@ -331,7 +331,8 @@ export function SidebarItem({ item, children }: { item: PageTree.Node; children: } if (item.type === 'separator') { - const { label, badge } = parseSeparatorMeta(item.name); + const separatorName = typeof item.name === 'string' ? item.name : undefined; + const { label, badge } = parseSeparatorMeta(separatorName); return (

{label || item.name}

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/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/chainhook/(chainhook-sdk)/meta.json b/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json index 241c650f1..592f45017 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/meta.json @@ -1,7 +1,7 @@ { "title": "Chainhook SDK", "pages": [ - "---Chainhook SDK---", + "---Chainhook SDK|new---", "introduction", "register-enable", "list-fetch", diff --git a/content/docs/en/tools/chainhook/(overview)/faq.mdx b/content/docs/en/tools/chainhook/(overview)/faq.mdx index 3d7fe6d8c..84f05e181 100644 --- a/content/docs/en/tools/chainhook/(overview)/faq.mdx +++ b/content/docs/en/tools/chainhook/(overview)/faq.mdx @@ -9,7 +9,7 @@ description: Frequently asked questions about 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 [support@hiro.so](mailto:support@hiro.so). +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). @@ -41,7 +41,7 @@ Chainhooks will be charged using a credit model for each delivery, with users ab 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 or via the API during and after the Beta period will run on v2.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. @@ -52,13 +52,20 @@ Learn how to migrate your v1 chainhooks to v2 in the [Migration Guide](/tools/ch - If v2.0 and v1.0 will be live at the same time, how do we access both? + 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 @@ -299,7 +306,7 @@ ngrok http 3000 - **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**: [support@hiro.so](mailto:support@hiro.so) +- **Email Support**: [beta@hiro.so](mailto:beta@hiro.so) - **GitHub Issues**: [hirosystems/chainhook](https://github.com/hirosystems/chainhook/issues) --- diff --git a/content/docs/en/tools/chainhook/(overview)/migration.mdx b/content/docs/en/tools/chainhook/(overview)/migration.mdx index b35fd3191..c2e7293e9 100644 --- a/content/docs/en/tools/chainhook/(overview)/migration.mdx +++ b/content/docs/en/tools/chainhook/(overview)/migration.mdx @@ -1,6 +1,6 @@ --- -title: Migrating from v1 to v2 -description: Guide for migrating legacy v1 chainhooks to Chainhook 2.0 Beta +title: Migration guide +description: Guide for migrating legacy chainhooks to v2 --- :::callout diff --git a/content/docs/en/tools/chainhook/index.mdx b/content/docs/en/tools/chainhook/index.mdx index fad7a6920..dfa889110 100644 --- a/content/docs/en/tools/chainhook/index.mdx +++ b/content/docs/en/tools/chainhook/index.mdx @@ -8,20 +8,18 @@ llm: false :::callout type: warn ### Chainhook 2.0 Beta -Chainhook 2.0 is currently in beta. During this period, we're focused on learning about reliability, performance, and developer experience. If you encounter issues or have feedback, please reach out to [support@hiro.so](mailto:support@hiro.so). +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). ::: ## Overview -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. +Chainhook is a configurable webhook service that lets you filter the Stacks blockchain for specific events and stream the data to your application. 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 -To explore Chainhook features with AI, copy and paste [llms.txt](/tools/chainhook/llms.txt) into your LLM of choice. - ## Key Features - **Reorg-aware indexing** - Automatically handles blockchain forks and reorganizations @@ -29,8 +27,8 @@ To explore Chainhook features with AI, copy and paste [llms.txt](/tools/chainhoo - **Historical evaluation** - Test chainhooks against past blocks for indexing or debugging :::next-steps -* [SDK Quickstart](/tools/chainhook/quickstart): Get started with the Chainhook SDK in 5 minutes -* [Platform Quickstart](/tools/chainhook/platform-quickstart): Create your first chainhook in the Platform UI +- [SDK Quickstart](/tools/chainhook/quickstart): Get started with the Chainhook SDK in 5 minutes +- [Platform Quickstart](/tools/chainhook/platform-quickstart): Create your first chainhook in the Platform UI ::: :::callout diff --git a/content/docs/en/tools/chainhook/meta.json b/content/docs/en/tools/chainhook/meta.json index 10c94440d..31a8590c4 100644 --- a/content/docs/en/tools/chainhook/meta.json +++ b/content/docs/en/tools/chainhook/meta.json @@ -1,5 +1,5 @@ { "title": "Chainhook", "root": true, - "pages": ["---Chainhook---", "index", "...(overview)", "...(chainhook-sdk)", "...reference"] + "pages": ["---Chainhook|beta---", "index", "...(overview)", "...(chainhook-sdk)", "...reference"] } 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/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/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---", From 950a01f6d7e3a4c11759dba71dc66db9002d2ef7 Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 20:20:50 -0600 Subject: [PATCH 15/16] update chainhook content --- .../en/resources/archive/download-guide.mdx | 2 +- .../chainhook/(chainhook-sdk)/edit-update.mdx | 64 +--- .../chainhook/(chainhook-sdk)/evaluate.mdx | 43 ++- .../chainhook/(chainhook-sdk)/list-fetch.mdx | 104 +----- .../chainhook/(chainhook-sdk)/manage-keys.mdx | 27 +- .../(chainhook-sdk)/register-enable.mdx | 145 +------ .../en/tools/chainhook/(overview)/faq.mdx | 2 +- .../tools/chainhook/(overview)/migration.mdx | 258 +++++++------ content/docs/en/tools/chainhook/index.mdx | 8 +- .../en/tools/chainhook/reference/filters.mdx | 63 +++- .../en/tools/chainhook/reference/options.mdx | 11 +- .../chainhook/reference/payload-anatomy.mdx | 8 +- .../es/resources/archive/download-guide.mdx | 353 +++--------------- content/docs/es/resources/guides/index.mdx | 14 +- .../chainhook/(chainhook-sdk)/edit-update.mdx | 76 +--- .../chainhook/(chainhook-sdk)/evaluate.mdx | 75 ++-- .../chainhook/(chainhook-sdk)/list-fetch.mdx | 106 +----- .../chainhook/(chainhook-sdk)/manage-keys.mdx | 39 +- .../(chainhook-sdk)/register-enable.mdx | 159 +------- .../es/tools/chainhook/(overview)/faq.mdx | 94 ++--- .../tools/chainhook/(overview)/migration.mdx | 337 ++++++++--------- content/docs/es/tools/chainhook/index.mdx | 22 +- .../es/tools/chainhook/reference/filters.mdx | 95 +++-- .../es/tools/chainhook/reference/options.mdx | 76 ++-- .../chainhook/reference/payload-anatomy.mdx | 32 +- .../es/tools/contract-monitoring/index.mdx | 18 +- 26 files changed, 738 insertions(+), 1493 deletions(-) diff --git a/content/docs/en/resources/archive/download-guide.mdx b/content/docs/en/resources/archive/download-guide.mdx index 4e09e4433..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/tools/chainhook/(chainhook-sdk)/edit-update.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx index cea831da1..18708cdd4 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx @@ -1,5 +1,5 @@ --- -title: Edit & Update +title: Edit chainhooks description: Modify existing chainhooks using the SDK --- @@ -11,9 +11,9 @@ The Platform UI does not currently support editing chainhooks. You must use the ## updateChainhook -### Mutable vs Immutable Fields +### Mutable vs Immutable fields -| Mutable (Can Update) | Immutable (Cannot Update) | +| Mutable | Immutable | |---------------------|---------------------------| | `name` | `chain` | | `filters` | `network` | @@ -38,19 +38,13 @@ await client.updateChainhook('chainhook-uuid', { }); ``` -## Common Update Patterns +### Add event filter (while preserving existing events) -| Update Type | Example | -|-------------|---------| -| **Webhook URL** | `{ action: { type: 'http_post', url: 'https://new-server.com/webhooks' } }` | -| **Name** | `{ name: 'production-ft-tracker' }` | -| **Filters** | `{ filters: { events: [{ type: 'nft_transfer' }] } }` | - -### Add Event Filter (Preserving Existing) +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 -const current = await client.getChainhook('chainhook-uuid'); - +// ✅ Good: Fetch first +const current = await client.getChainhook(uuid); await client.updateChainhook('chainhook-uuid', { filters: { events: [ @@ -59,6 +53,11 @@ await client.updateChainhook('chainhook-uuid', { ], }, }); + +// ❌ Bad: Will overwrite any existing events +await client.updateChainhook(uuid, { + filters: { events: { type: 'contract_call', contract_identifier: 'SP...XYZ.counter' } }, +}); ``` ### Update Multiple Fields @@ -70,50 +69,11 @@ await client.updateChainhook('chainhook-uuid', { action: { type: 'http_post', url: 'https://new-url.com/webhooks' }, options: { decode_clarity_values: true }, }); -``` - -## cURL Example - -### Update Chainhook - -```bash -curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ - "name": "Updated chainhook name", - "action": { "type": "http_post", "url": "https://new-server.com/webhooks" } - }' -``` - -### Response - -- **Success**: HTTP `204 No Content` -- **Verify**: Fetch the chainhook to confirm changes -```typescript -await client.updateChainhook('chainhook-uuid', { name: 'New name' }); const updated = await client.getChainhook('chainhook-uuid'); console.log('Updated:', updated.definition.name); ``` -### Example: Safe Additive Update - -```typescript -// ✅ Good: Fetch first -const current = await client.getChainhook(uuid); -await client.updateChainhook(uuid, { - filters: { - events: [...current.definition.filters.events, newEvent], - }, -}); - -// ❌ Bad: Might overwrite -await client.updateChainhook(uuid, { - filters: { events: [newEvent] }, -}); -``` - :::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 index 4ccde2ae5..d92f8b1f5 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/evaluate.mdx +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/evaluate.mdx @@ -1,9 +1,11 @@ --- -title: Evaluate Chainhook -description: Test chainhooks against specific past blocks +title: Evaluate a specific block +description: Run your chainhooks against specific blocks for testing, debugging, and historical indexing. --- -Test your chainhook configuration against specific past 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 @@ -16,7 +18,9 @@ Test your chainhook configuration against specific past blocks for testing, debu ### Example -```typescript + + +```ts !! with-block-height.ts import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; const client = new ChainhooksClient({ @@ -25,22 +29,29 @@ const client = new ChainhooksClient({ }); await client.evaluateChainhook('chainhook-uuid', { + // !mark block_height: 100000, }); ``` -Returns HTTP `204 No Content`. If filters match, webhook payload is sent to your `action.url`. +```ts !! with-block-hash.ts +import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; -### cURL +const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, +}); -```bash -curl -X POST \ - "https://api.testnet.hiro.so/chainhooks/me/chainhook-uuid/evaluate" \ - -H "x-api-key: $HIRO_API_KEY" \ - -H "content-type: application/json" \ - -d '{ "block_height": 100000 }' +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 | @@ -49,14 +60,6 @@ curl -X POST \ | **Backfill** | Index historical data | Process past blocks after creating chainhook | | **Re-process** | Fix webhook handler issues | Re-evaluate after fixing bugs | -## Best Practices - -| Practice | Example | -|----------|---------| -| **Rate limiting** | Add 100ms delay between requests | -| **Test first** | Evaluate 2-3 sample blocks before bulk processing | -| **Use height** | Easier to iterate than block hashes | - :::next-steps - [Register & Enable](/tools/chainhook/register-enable): Create chainhooks to evaluate diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx index 7a5ea6f95..cd9c609e6 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx @@ -1,17 +1,13 @@ --- -title: List & Fetch +title: Fetch chainhooks description: Retrieve chainhook information using the SDK --- -# List & Fetch - -Learn how to list and fetch chainhooks using the Chainhook SDK. - ## getChainhooks Retrieve a paginated list of all your chainhooks. -### TypeScript +### Example ```typescript import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; @@ -30,57 +26,14 @@ console.log('Limit:', chainhooks.limit); console.log('Offset:', chainhooks.offset); ``` -### With Pagination Options +### With options ```typescript -// Get specific page with custom limit +// Get specific page with 50 chainhooks starting from position 100 const chainhooks = await client.getChainhooks({ limit: 50, offset: 100, }); - -// This fetches 50 chainhooks starting from position 100 -``` - -### cURL - -```bash -curl -sS "https://api.testnet.hiro.so/chainhooks/me?limit=20&offset=0" \ - -H "x-api-key: $HIRO_API_KEY" -``` - -### Response - -```json -{ - "limit": 20, - "offset": 0, - "total": 45, - "results": [ - { - "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", - "definition": { - "version": "1", - "name": "my-chainhook", - "chain": "stacks", - "network": "testnet", - "filters": { ... }, - "action": { ... } - }, - "status": { - "enabled": true, - "type": "active", - "last_evaluated_at": 1696432154, - "last_evaluated_block_height": 150000, - "last_occurrence_at": 1696432100, - "last_occurrence_block_height": 149980, - "evaluated_block_count": 1000, - "occurrence_count": 42 - } - }, - // ... more chainhooks - ] -} ``` --- @@ -89,59 +42,12 @@ curl -sS "https://api.testnet.hiro.so/chainhooks/me?limit=20&offset=0" \ Retrieve a specific chainhook by UUID. -### TypeScript +### Example ```typescript const chainhook = await client.getChainhook('be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185'); ``` -### cURL - -```bash -curl -sS "https://api.testnet.hiro.so/chainhooks/me/" \ - -H "x-api-key: $HIRO_API_KEY" -``` - -### Response - -```json -{ - "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", - "definition": { - "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 - } - }, - "status": { - "enabled": true, - "type": "active", - "last_evaluated_at": 1696432154, - "last_evaluated_block_height": 150000, - "last_occurrence_at": 1696432100, - "last_occurrence_block_height": 149980, - "evaluated_block_count": 1000, - "occurrence_count": 42 - } -} -``` - :::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 index ae729e00b..1eb9777c6 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx @@ -13,47 +13,46 @@ description: Rotate consumer secrets and validate every Chainhook delivery ## Prerequisites :::prerequisites -- Hiro API key stored as `CHAINHOOKS_API_KEY`. -- Chainhook UUID you want to protect. -- Node.js runtime (the example uses Fastify). +- Hiro API key +- Node.js (server example uses Fastify). ::: ## Validating webhook requests with a consumer secret -Chainhooks attach an `Authorization: Bearer ` header to every webhook attempt, giving you a simple shared-secret handshake. +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 `await client.rotateConsumerSecret(chainhookUuid)` (or the `/chainhooks/{uuid}/secret` API) whenever you need a new token. -2. Persist the returned `secret` in your secret manager and reload it at process start or via a short refresh loop. -3. Reject webhook deliveries whose `Authorization` header does not equal `Bearer `. +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 `. -### Rotate/create consumer secret +### Create/rotate consumer secret -```ts +```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.CHAINHOOKS_API_KEY!, + apiKey: process.env.HIRO_API_KEY!, }); -let consumerSecret: string = await client.rotateConsumerSecret(chainhookUuid).secret; +// Store this value securely and use it to validate webhook requests +const secret = await client.rotateConsumerSecret(chainhookUuid).secret; ``` ### Example Fastify server -```ts +```ts -n import Fastify from 'fastify'; const server = Fastify(); server.post('/webhook', async (request, reply) => { - if (!consumerSecret) { + if (!secret) { reply.code(503).send({ error: 'consumer secret unavailable' }); return; } const authHeader = request.headers.authorization; - if (authHeader !== `Bearer ${consumerSecret}`) { + if (authHeader !== `Bearer ${secret}`) { reply.code(401).send({ error: 'invalid consumer secret' }); return; } diff --git a/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx b/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx index 5ae15bffb..583222ce7 100644 --- a/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx +++ b/content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx @@ -1,6 +1,6 @@ --- -title: Register & Enable -description: Create and activate chainhooks using the SDK +title: Create chainhooks +description: Create and activate chainhooks using the Chainhook SDK --- # Register & Enable @@ -11,9 +11,9 @@ Learn how to create and activate chainhooks using the Chainhook SDK. Creates a new chainhook configuration. By default, new chainhooks are created in a disabled state unless `enable_on_registration` is set to `true`. -### TypeScript +### Example -```typescript +```ts -nc import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; const client = new ChainhooksClient({ @@ -41,7 +41,7 @@ const chainhook = await client.registerChainhook({ }, options: { decode_clarity_values: true, - enable_on_registration: true, // Enable immediately + enable_on_registration: true, }, }); @@ -49,86 +49,15 @@ console.log('Chainhook UUID:', chainhook.uuid); console.log('Enabled:', chainhook.status.enabled); // true ``` -### cURL - -```bash -curl -sS -X POST "https://api.testnet.hiro.so/chainhooks/me" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ - "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 - } - }' -``` - -### Response - -```json -{ - "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", - "definition": { - "version": "1", - "name": "my-chainhook", - "chain": "stacks", - "network": "testnet", - "filters": { ... }, - "action": { ... }, - "options": { ... } - }, - "status": { - "enabled": true, - "type": "active" - } -} -``` - -### Enable Later - -If you don't set `enable_on_registration`, the chainhook will be created but disabled: - -```typescript -const chainhook = await client.registerChainhook({ - version: '1', - name: 'my-chainhook', - chain: 'stacks', - network: 'testnet', - filters: { /* ... */ }, - action: { /* ... */ }, - // No options.enable_on_registration -}); - -console.log('Enabled:', chainhook.status.enabled); // false - -// Enable it later -await client.enableChainhook(chainhook.uuid, 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 pause a chainhook without deleting it. +Enable or disable a single chainhook by UUID. This allows you to enable chainhooks after registration or pause without deleting it. -### TypeScript +### Examples ```typescript // Enable a chainhook @@ -138,26 +67,6 @@ await client.enableChainhook('chainhook-uuid', true); await client.enableChainhook('chainhook-uuid', false); ``` -### cURL - -Enable: -```bash -curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me//enabled" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ "enabled": true }' -``` - -Disable: -```bash -curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me//enabled" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ "enabled": false }' -``` - -### Response - Returns HTTP `204 No Content` on success. --- @@ -174,6 +83,7 @@ Enable specific chainhooks by their UUIDs (maximum 200): await client.bulkEnableChainhooks({ enabled: true, filters: { + // !mark(1:5) uuids: [ 'uuid-1', 'uuid-2', @@ -191,6 +101,7 @@ Enable all chainhooks that POST to a specific URL: await client.bulkEnableChainhooks({ enabled: true, filters: { + // !mark webhook_url: 'https://example.com/webhooks', }, }); @@ -201,9 +112,11 @@ await client.bulkEnableChainhooks({ Enable all chainhooks with a specific status: ```typescript + await client.bulkEnableChainhooks({ enabled: true, filters: { + // !mark statuses: ['inactive'], }, }); @@ -217,6 +130,7 @@ Use multiple filters together: await client.bulkEnableChainhooks({ enabled: false, // Disable matching chainhooks filters: { + // !mark(1:2) webhook_url: 'https://old-server.com/webhooks', statuses: ['active'], }, @@ -225,39 +139,6 @@ await client.bulkEnableChainhooks({ This will disable all active chainhooks that POST to the old webhook URL. -### cURL - -```bash -curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/enabled" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ - "enabled": true, - "filters": { - "uuids": ["uuid-1", "uuid-2"], - "webhook_url": "https://example.com/webhooks" - } - }' -``` - -### Response - -```json -{ - "updated": 5, - "results": [ - { - "uuid": "uuid-1", - "enabled": true - }, - { - "uuid": "uuid-2", - "enabled": true - } - ] -} -``` - :::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/(overview)/faq.mdx b/content/docs/en/tools/chainhook/(overview)/faq.mdx index 84f05e181..372f3d626 100644 --- a/content/docs/en/tools/chainhook/(overview)/faq.mdx +++ b/content/docs/en/tools/chainhook/(overview)/faq.mdx @@ -5,7 +5,7 @@ description: Frequently asked questions about Chainhook 2.0 Beta ## Chainhook 2.0 Beta - + What is the goal/purpose of the Chainhook 2.0 Beta? diff --git a/content/docs/en/tools/chainhook/(overview)/migration.mdx b/content/docs/en/tools/chainhook/(overview)/migration.mdx index c2e7293e9..d19cb52e5 100644 --- a/content/docs/en/tools/chainhook/(overview)/migration.mdx +++ b/content/docs/en/tools/chainhook/(overview)/migration.mdx @@ -19,70 +19,32 @@ Legacy v1 chainhooks remain read-only in the Platform UI; you manage them throug ## Prerequisites :::prerequisites -- API access to both the Platform API and Chainhook REST API (same `HIRO_API_KEY`). -- Local SDK (`@hirosystems/chainhooks-client`) or `curl` for REST calls. -- Environment variable `HIRO_API_KEY` set for the CLI and code samples. +- Hiro API key for access to both the Platform and Chainhook API. ::: -### Inventory v1 chainhooks -Use the Platform API to fetch every chainhook that still fires in production. +### Get a list of v1 chainhooks +Use the Platform API to fetch the chainhooks you want to migrate. -#### CLI -```bash -curl -sS "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks" \ - -H "content-type: application/json" -``` - -#### TypeScript -```typescript -export async function listV1Chainhooks() { - const response = await fetch( - `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks`, - { headers: { 'content-type': 'application/json' } } - ); - - if (!response.ok) throw new Error(response.statusText); - return (await response.json()) as any[]; -} - -const chainhooks = await listV1Chainhooks(); -console.log(`Found ${chainhooks.length} v1 chainhooks`); -``` - +```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' + } +}); - -### Inspect a chainhook -Pull the full definition for each UUID so you can convert custom filters and metadata. - -#### CLI -```bash -curl -sS \ - "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks/$CHAINHOOK_UUID" \ - -H "content-type: application/json" +const chainhooks = await response.json(); ``` -#### TypeScript -```typescript -export async function getV1Chainhook(uuid: string) { - const response = await fetch( - `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks/${uuid}`, - { headers: { 'content-type': 'application/json' } } - ); - - if (!response.ok) throw new Error(response.statusText); - return await response.json(); -} -``` -### Map configuration to v2 -Translate v1 structures to v2 fields before provisioning new hooks. +### 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 Concept | v2 Target | Notes | +| 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`. | @@ -90,9 +52,11 @@ Translate v1 structures to v2 fields before provisioning new hooks. | `networks.mainnet` | `network: "mainnet"` | Create one v2 hook per network. | | `authorization_header` | Webhook secret management | Use `rotateConsumerSecret()` after registration. | -#### Example Conversion -```jsonc -// v1 (Platform API) +#### Example + + + +```json !! Legacy { "name": "stx-transfers", "networks": { @@ -104,59 +68,98 @@ Translate v1 structures to v2 fields before provisioning new hooks. } ``` -```typescript -// v2 (SDK) -import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; - -const client = new ChainhooksClient({ - baseUrl: CHAINHOOKS_BASE_URL.mainnet, - apiKey: process.env.HIRO_API_KEY!, -}); - -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', +```json !! v2 +{ + "version": "1", + "name": "stx-transfers", + "chain": "stacks", + "network": "mainnet", + "filters": { + "events": [{ "type": "stx_transfer" }] }, - options: { - decode_clarity_values: true, - enable_on_registration: true, + "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 -Provision each chainhook with the SDK or REST API, mirroring the mapped filters. - -#### REST -```bash -curl -sS -X POST "https://api.mainnet.hiro.so/chainhooks/v1/me/" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d @v2-chainhook.json +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), +}); ``` -#### Chainhook SDK -```typescript +```ts !! sdk.ts const client = new ChainhooksClient({ - baseUrl: CHAINHOOKS_BASE_URL[config.network], + baseUrl: CHAINHOOKS_BASE_URL.mainnet, apiKey: process.env.HIRO_API_KEY!, }); -const chainhook = await client.registerChainhook(config); +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 retire v1 +### Validate and cleanup legacy chainhooks Stream events through both versions, confirm delivery, then clean up the legacy definitions. :::callout @@ -165,36 +168,50 @@ type: info Keep both v1 and v2 hooks active until you verify delivery parity. ::: -#### Enablement Checks -```typescript -const chainhook = await client.getChainhook(v2Uuid); +#### 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); ``` -#### Delete with cURL -```bash -curl -sS -X DELETE \ - "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks/$CHAINHOOK_UUID" \ - -H "content-type: application/json" +```ts !! sdk.ts +const chainhook = await client.getChainhook(uuid); +console.log(chainhook.status.enabled); ``` -#### Delete with SDK -```typescript -export async function deleteV1Chainhook(uuid: string) { - 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' } } - ); + - if (!response.ok) throw new Error(response.statusText); - return await response.json(); -} +#### 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 Translation Reference +## Filter reference ### Common scopes | v1 | Typical Actions | v2 | Extras | @@ -204,26 +221,7 @@ export async function deleteV1Chainhook(uuid: string) { | `ft_event` | `transfer` | `ft_transfer` | Specify `asset_identifier`. | | `nft_event` | `transfer`, `mint` | `nft_transfer` · `nft_mint` | Provide `asset_identifier`. | -### Example -```jsonc -// v1 filter -{ - "scope": "stx_event", - "actions": ["transfer"] -} -``` - -```jsonc -// v2 filter -{ - "events": [ - { - "type": "stx_transfer", - "recipient": "SP3FBR2AGKQX0..." - } - ] -} -``` +For more details, check out the [Filters](/tools/chainhook/reference/filters) reference page. :::next-steps - [SDK Documentation](/tools/chainhook/chainhook-sdk): SDK Documentation diff --git a/content/docs/en/tools/chainhook/index.mdx b/content/docs/en/tools/chainhook/index.mdx index dfa889110..6f3944a67 100644 --- a/content/docs/en/tools/chainhook/index.mdx +++ b/content/docs/en/tools/chainhook/index.mdx @@ -1,7 +1,7 @@ --- 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 --- @@ -13,7 +13,7 @@ Chainhook 2.0 is currently in beta. If you encounter issues or have feedback, pl ## Overview -Chainhook is a configurable webhook service that lets you filter the Stacks blockchain for specific events and stream the data to your application. +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. With Chainhook 2.0, you can manage chainhooks through: - **[Chainhook SDK](/tools/chainhook/chainhook-sdk)** - TypeScript/JavaScript client for programmatic management @@ -27,8 +27,8 @@ With Chainhook 2.0, you can manage chainhooks through: - **Historical evaluation** - Test chainhooks against past blocks for indexing or debugging :::next-steps -- [SDK Quickstart](/tools/chainhook/quickstart): Get started with the Chainhook SDK in 5 minutes -- [Platform Quickstart](/tools/chainhook/platform-quickstart): Create your first chainhook in the Platform UI +- [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 diff --git a/content/docs/en/tools/chainhook/reference/filters.mdx b/content/docs/en/tools/chainhook/reference/filters.mdx index 768447ca5..2275d39ae 100644 --- a/content/docs/en/tools/chainhook/reference/filters.mdx +++ b/content/docs/en/tools/chainhook/reference/filters.mdx @@ -1,29 +1,29 @@ --- -title: Filter Reference +title: Filters description: Complete reference for all Chainhook filter types --- -# Filter Reference +Filters define which blockchain events will trigger your chainhook. -Filters define which blockchain events will trigger your chainhook—use the matrix below to pick the right entry for `filters.events[]`. +Here is a complete reference for all Chainhook filter types: | Filter | When to use | |--------|-------------| -| `ft_event` | FT: catch *every* SIP-010 transfer, mint, or burn across assets. | -| `ft_transfer` | FT: follow a single asset such as USDC; optionally add `sender`/`receiver` for wallet-level triggers. | -| `ft_mint` | FT: track supply expansions or bridge inflows for one asset (set `asset_identifier`). | -| `ft_burn` | FT: track redemptions or supply contractions (set `asset_identifier`). | -| `nft_event` | NFT: monitor every transfer, mint, or burn for all collections. | -| `nft_transfer` | NFT: follow a SIP-009 collection; add `sender`, `receiver`, or `value` for owner/token targeting. | -| `nft_mint` | NFT: watch every new mint for a collection (set `asset_identifier`). | -| `nft_burn` | NFT: catch burns or redemptions (set `asset_identifier`). | -| `stx_event` | STX: capture all native transfers, mints, or burns. | -| `stx_transfer` | STX: trace every transfer; add `sender` or `receiver` to spotlight specific principals. | -| `contract_deploy` | Contracts: react to new contracts entering the network. | -| `contract_call` | Contracts: observe every invocation; narrow with `contract_identifier` and `function_name`. | -| `contract_log` | Contracts: catch `print`/log output from a contract (set `contract_identifier`). | -| `coinbase` | System: watch miner rewards hitting the chain. | -| `tenure_change` | System: track Proof-of-Transfer tenure updates; add `cause` (`"block_found"` or `"extended"`) for specificity. | +| `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) @@ -44,6 +44,7 @@ Match FT transfers for a specific asset: ```json { "type": "ft_transfer", + // !mark "asset_identifier": "SP...ABC.ft::usdc" } ``` @@ -54,6 +55,7 @@ Filter by sender: { "type": "ft_transfer", "asset_identifier": "SP...ABC.ft::usdc", + // !mark "sender": "SP...FROM" } ``` @@ -64,6 +66,7 @@ Filter by receiver: { "type": "ft_transfer", "asset_identifier": "SP...ABC.ft::usdc", + // !mark "receiver": "SP...TO" } ``` @@ -111,6 +114,7 @@ Match NFT transfers for a specific collection: ```json { "type": "nft_transfer", + // !mark "asset_identifier": "SP...COLL.nft::collectible" } ``` @@ -121,6 +125,7 @@ Filter by sender: { "type": "nft_transfer", "asset_identifier": "SP...COLL.nft::collectible", + // !mark "sender": "SP...FROM" } ``` @@ -131,6 +136,7 @@ Filter by receiver: { "type": "nft_transfer", "asset_identifier": "SP...COLL.nft::collectible", + // !mark "receiver": "SP...TO" } ``` @@ -141,6 +147,7 @@ Filter by specific token ID: { "type": "nft_transfer", "asset_identifier": "SP...COLL.nft::collectible", + // !mark "value": "u123" } ``` @@ -196,6 +203,7 @@ Filter by sender: ```json { "type": "stx_transfer", + // !mark "sender": "SP...SENDER" } ``` @@ -205,6 +213,7 @@ Filter by receiver: ```json { "type": "stx_transfer", + // !mark "receiver": "SP...RECEIVER" } ``` @@ -228,6 +237,7 @@ Filter by deployer: ```json { "type": "contract_deploy", + // !mark "sender": "SP...DEPLOYER" } ``` @@ -247,6 +257,7 @@ Match calls to a specific contract: ```json { "type": "contract_call", + // !mark "contract_identifier": "SP...XYZ.counter" } ``` @@ -257,6 +268,7 @@ Match calls to a specific function: { "type": "contract_call", "contract_identifier": "SP...XYZ.counter", + // !mark "function_name": "increment" } ``` @@ -268,17 +280,27 @@ Filter by caller: "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" } ``` @@ -289,6 +311,7 @@ Filter by transaction sender: { "type": "contract_log", "contract_identifier": "SP...XYZ.counter", + // !mark "sender": "SP...SENDER" } ``` @@ -322,6 +345,7 @@ Match tenure changes by cause (block found): ```json { "type": "tenure_change", + // !mark[/"block_found"/] "cause": "block_found" } ``` @@ -331,6 +355,7 @@ Match tenure changes by cause (extended): ```json { "type": "tenure_change", + // !mark[/"extended"/] "cause": "extended" } ``` @@ -359,8 +384,6 @@ You can combine multiple filters in the `filters.events` array. A chainhook will } ``` -This chainhook will trigger on either DIKO transfers **or** calls to the `increment` function. - :::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/options.mdx b/content/docs/en/tools/chainhook/reference/options.mdx index 9a0dcbbca..f10449244 100644 --- a/content/docs/en/tools/chainhook/reference/options.mdx +++ b/content/docs/en/tools/chainhook/reference/options.mdx @@ -1,5 +1,5 @@ --- -title: Options Reference +title: Options description: Complete reference for all Chainhook configuration options --- @@ -41,15 +41,6 @@ Include human-readable `repr` (and inferred types) alongside `hex` for arguments } ``` -**Example output when enabled**: -```json -{ - "hex": "0x01000000000000000000000002690ed9fe", - "repr": "u10352515582", - "type": "uint" -} -``` - --- ### include_contract_abi diff --git a/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx b/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx index dcf7ea5c1..b7354ca41 100644 --- a/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx +++ b/content/docs/en/tools/chainhook/reference/payload-anatomy.mdx @@ -1,14 +1,14 @@ --- -title: Payload Anatomy +title: Payload anatomy description: Understanding the structure of Chainhook webhook payloads --- ## Payload Overview -A Chainhook payload consists of two main sections: +A Chainhook payload consists of two main sections: **event** and **chainhook**: -1. **event** - Contains the blockchain data (blocks, transactions, operations) -2. **chainhook** - Metadata about the chainhook that triggered +- `event` contains the blockchain data (blocks, transactions, operations) +- `chainhook` contains metadata about the chainhook that triggered --- diff --git a/content/docs/es/resources/archive/download-guide.mdx b/content/docs/es/resources/archive/download-guide.mdx index 503fa07b4..a926bdca5 100644 --- a/content/docs/es/resources/archive/download-guide.mdx +++ b/content/docs/es/resources/archive/download-guide.mdx @@ -1,216 +1,52 @@ --- title: Cómo descargar archivos de Hiro -description: Guía completa para descargar archivos grandes del archivo de Hiro con consejos prácticos para evitar errores. +description: Guía completa para descargar archivos de archivo grandes de manera confiable con consejos de resolución de problemas. --- - ## Descripción general -Los archivos de Hiro Archive son conjuntos de datos masivos (desde varios GB hasta varios *cientos* de GB) alojados en Google Cloud Storage. Las descargas pueden cortarse por límites de red o tiempos de espera, así que necesitas estrategias fiables y comandos que reanuden el trabajo cuando algo falla. - -## Tamaños y requisitos de archivos +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. -Antes de iniciar cualquier descarga, confirma estos puntos básicos: +## Tamaños de archivos y requisitos -- **Espacio en disco**: Desde 10 GB (APIs) hasta cientos de GB para datos de blockchain. -- **Ancho de banda**: Algunas descargas pueden tardar horas; planifica horarios de baja congestión. -- **Almacenamiento para extracción**: Al descomprimir, los archivos crecen 2‑3×. Reserva espacio adicional. +Antes de descargar, asegúrate de tener suficiente: -```ts -// Verifica espacio libre en macOS o Linux -$ df -h | grep archive -# Muestra el porcentaje de uso y te ayuda a decidir si necesitas otro volumen -``` +* **Espacio en disco**: Los archivos van desde 10GB (APIs) hasta varios cientos de GB+ (datos de blockchain) +* **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) +### Método 1: wget con reanudación (Recomendado para la mayoría de usuarios) -El comando `wget` con la bandera `-c` reanuda cualquier descarga interrumpida y mantiene una barra de progreso legible. +El `wget` comando con el `-c` flag permite reanudar descargas interrumpidas: :::callout type: info + ### Usuarios de macOS -Instala la utilidad con `brew install wget` si tu sistema no la incluye. Como alternativa, usa el método `curl` mostrado más abajo (viene preinstalado). + +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 $ wget -c https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz $ wget -c --progress=bar:force:noscroll https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz -# La barra forzada evita que el log crezca sin control durante descargas largas -``` - -**Ventajas** -- Retoma descargas automáticamente. -- Disponible en la mayoría de sistemas Unix. -- Sintaxis corta y fácil de automatizar. - -**Desventajas** -- Descarga en un solo hilo. -- Puede sufrir reinicios si tu ISP corta conexiones largas. - -### Método 2: curl con reintentos automáticos - -`curl` ofrece control granular sobre reintentos, tiempos de espera y nombres de archivo. - -```terminal -$ curl --continue-at - --retry 10 --retry-delay 5 --retry-max-time 0 \ - --progress-bar \ - --output mainnet-stacks-blockchain-latest.tar.gz \ - https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz -# --continue-at - retoma el archivo parcial; --retry maneja caídas temporales -``` - -**Ventajas** -- Reintentos configurables con retardo exponencial. -- Permite personalizar cabeceras y autenticación si la necesitas. -- Funciona en macOS sin instalaciones adicionales. - -**Desventajas** -- Sintaxis más larga. -- Necesitas scripts adicionales para mostrar progreso detallado. - -### Método 3: gcloud storage cp (más rápido, requiere autenticación) - -La CLI de Google Cloud usa transferencias paralelas cuando descargas desde Google Cloud Storage. - -```terminal -$ gcloud auth login # Abre el navegador y vincula tu cuenta de Google -$ gcloud storage cp \ - gs://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz . -# Descarga al directorio actual usando varias conexiones paralelas -``` - -Si tu espacio en disco es limitado, transmite directamente al proceso de extracción: - -```terminal -$ gcloud storage cp \ - gs://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz - \ - | tar -xz -C /ruta/de/destino -# Evita archivos temporales grandes, pero desactiva la paralelización del tar ``` -**Ventajas** -- 2‑3× más rápido gracias a descargas paralelas. -- Maneja reintentos y reanudación por ti. -- Permite streaming a otros comandos (`tar`, `pv`, etc.). - -**Desventajas** -- Necesita `gcloud` y autenticación. -- Algunas redes empresariales bloquean el flujo OAuth. - -### Método 4: Gestores de descarga (ej. JDownloader) - -Si prefieres una GUI o necesitas dividir la descarga en múltiples conexiones, usa un gestor. - -```ts -// Flujo básico con JDownloader -const url = 'https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz'; -queue.add(url); // Copia y pega el enlace -queue.setChunks(8); // Ajusta los hilos paralelos -queue.start(); // Observa las métricas de velocidad y tiempo restante -``` - -**Ventajas** -- Interfaz visual con métricas de velocidad. -- Soporta reanudación con un clic. -- Configuras conexiones paralelas sin escribir scripts. - -**Desventajas** -- Requiere Java y más memoria. -- No siempre funciona bien en entornos sin GUI. - -## Verificación y extracción - -Después de descargar, verifica que el archivo no esté corrupto y descomprímelo de forma segura. - -```terminal -$ wget https://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.sha256 -$ shasum -a 256 mainnet-stacks-blockchain-latest.tar.gz -# Compara el hash mostrado con el archivo .sha256 descargado -``` - -```terminal -$ tar -zxvf mainnet-stacks-blockchain-latest.tar.gz -C /data/stacks -# Usa -C para extraer directamente en el volumen final y evitar copias extra -``` - -:::callout -type: info -### Extracciones pesadas -El archivo `marf.sqlite.blobs` puede tardar bastante en extraerse. No interrumpas `tar` a menos que falle; podrías terminar con datos incompletos. -::: - -## Consejos de rendimiento - -1. **Prefiere `gcloud` si tienes credenciales**: obtienes descargas paralelas y mejores reintentos. -2. **Programa las descargas en horarios valle**: menos congestión significa menos reinicios. -3. **Usa conexiones por cable**: el Wi-Fi introduce microcortes que dañan descargas largas. -4. **Monitorea el espacio libre**: los archivos extraídos ocupan más del doble. -5. **Transmite al extraer** cuando el espacio sea limitado (`gcloud ... - | tar -xz`). - -```ts -// Recordatorio rápido para automatizar descargas nocturnas -cron.schedule('0 1 * * *', () => { - execFile('wget', ['-c', ARCHIVE_URL], console.log); -}); -// Corre en servidores Linux y lanza la descarga todos los días a la 1 AM -``` - -## Preguntas frecuentes +**Ventajas:** - - - ¿Por qué las descargas fallan repetidamente? - - Los archivos grandes en la nube pueden sufrir límites de velocidad o desconexiones. Usa `wget -c`, `curl --continue-at -` o `gcloud storage cp` para reanudar automáticamente. - - - - ¿Qué método debo elegir? - - `gcloud storage cp` es el más rápido si tienes acceso. `wget -c` es la opción más sencilla. Un gestor como JDownloader sirve cuando prefieres interfaz gráfica. - - - - ¿Cuánto demora la descarga? - - Depende del archivo y de tu ancho de banda. El archivo principal de blockchain (cientos de GB) puede tardar de 6 a 24 horas en conexiones domésticas. - - - - ¿Puedo reanudar una descarga fallida? - - Sí. Usa `wget -c`, `curl --continue-at -` o `gcloud storage cp` con el mismo destino para continuar donde te quedaste. - - - - ¿Por qué gcloud es más rápido? - - La CLI abre varias conexiones en paralelo y está optimizada para Google Cloud Storage, lo que reduce la latencia y maneja los reintentos sin intervención manual. - - - - ¿Debo verificar los archivos? - - Siempre compara el hash SHA256 publicado con el resultado de `shasum -a 256` para asegurarte de que los datos no se corrompieron. - - - +* Reanuda descargas interrumpidas automáticamente +* Integrado en la mayoría de sistemas Unix +* Fácil de usar **Desventajas:** * Descargas de un solo hilo -* Es posible que aún experimente restablecimientos de conexión +* Aún puede experimentar reinicios de conexión ### Método 2: curl con reintentos -Utilizar `curl` con reintentos automáticos para descargas robustas. The `--continue-at -` bandera reanuda descargas parciales, mientras que `--output` title: Mi Canción -description: Descripción de la canción -sidebarTitle: --- Detalles --- -metadata: - composer: Compositor - artists: --- Intérpretes --- - album: Álbum +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 \ @@ -222,35 +58,30 @@ $ curl --continue-at - --retry 10 --retry-delay 5 --retry-max-time 0 \ **Ventajas:** * Mecanismo de reintento automático -* Capacidad de reanudación con `-C -` +* Reanudar capacidad con `-C -` * Más opciones de configuració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 en paralelo. Primero autenticarse con `gcloud auth login`, entonces descarga el archivo al disco o transmite 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 en el directorio actual +#### Descargar archivo al directorio actual ```terminal $ gcloud storage cp gs://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz . ``` -#### O transmitir directamente a extracción (ahorra espacio en disco pero más lento debido a la descarga secuencial) +#### O transmitir directamente a la extracción (ahorra espacio en disco pero es más lento debido a la descarga secuencial) ```terminal $ gcloud storage cp gs://archive.hiro.so/mainnet/stacks-blockchain/mainnet-stacks-blockchain-latest.tar.gz - | tar -xz ``` -**Ventajas: - -title: Clarinet -description: --- Clarinet is a key framework for writing maintainable, scalable and performant applications on the Ethereum blockchain. --- -sidebarTitle: Stacks -sidebarText: --- Clarity is a smart contract language designed for security. ---** +**Ventajas:** * Descargas significativamente más rápidas (mejora de velocidad de 2-3x) -* Construido en transferencias paralelas -* Manejo automático de reintento +* 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:** @@ -258,34 +89,34 @@ sidebarText: --- Clarity is a smart contract language designed for security. --- * Requiere autenticación de cuenta de Google * Instalación de software adicional necesaria -### Método 4: Descargadores de archivos (JDownloader) +### Método 4: Gestores de descarga (JDownloader) -Para usuarios que prefieren aplicaciones GUI o necesitan una gestión avanzada de descargas: +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. Copia la URL del archivo en JDownloader -3. Configura conexiones paralelas para descargas más rápidas +3. Configurar conexiones paralelas para descargas más rápidas **Ventajas:** * Interfaz gráfica * Descarga paralela -* Avanzados mecanismos de reintento +* 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 -### Comprobante de disponibilidad +### Disponibilidad de suma de verificación SHA256 checksum files are available for **todos los archivos** para verificar la integridad de la descarga. ::: -1. **Descarga 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 ``` @@ -305,139 +136,57 @@ type: info ### Extracción de archivos grandes -The - -title: Clarity Cheatsheet - -description: A quick reference for using Clarity on the Stacks blockchain. - -\--- - -\## Quick Start - -Get started with Clarity in just a few steps: - -1\. Install the Clarinet CLI -2\. Create a new Clarity project -3\. Write your first smart contract -4\. Deploy and test your contract - -\--- - -\## Clarity Basics - -Clarity is a domain-specific language (DSL) for writing smart contracts on the Stacks blockchain. It is a statically-typed, decidable language with a focus on safety and predictability. - -Some key features of Clarity: - -\- \*\*Explicitly Typed\*\*: All values in Clarity have an explicit type, which is checked at compile-time. -\- \*\*Decidable\*\*: Clarity programs are guaranteed to complete in a finite number of steps. -\- \*\*Deterministic\*\*: Clarity programs always produce the same output given the same inputs. -\- \*\*Secure\*\*: Clarity has a focus on security, with no runtime exceptions and no undefined behavior. - -\--- - -sidebarTitle: Additional Resources - -\- \[Clarity Documentation]\(https://docs.stacks.co/learn/clarity/) -\- \[Clarinet CLI]\(https://github.com/hirosystems/clarinet) -\- \[Clarity by Example]\(https://clarity-by-example.org/) `marf.sqlite.blobs` El archivo puede ser muy grande y puede tardar mucho tiempo en extraerlo. Asegúrese de tener suficiente espacio en el 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. **Usar gcloud para las descargas más rápidas** - requiere autenticación pero proporciona mejoras de velocidad significativas -2. **Descargar durante horas fuera de pico** - normalmente por la noche tarde o temprano por la mañana -3. **Use conexiones con cable** - evitar Wi-Fi para descargas grandes cuando sea posible -4. **Monitor el espacio en disco** - los archivos extraídos pueden ser 2-3 veces más grandes que los archivos comprimidos -5. **Consider la extracción por streaming** con gcloud para ahorrar espacio en el disco - -FAQ - -\--- -title: Quick Answers to Common Questions -\--- - -\- \*\*title:\*\* Quick Answers to Common Questions -\- \*\*sidebarTitle:\*\* FAQ -\- \*\*description:\*\* Get quick answers to frequently asked questions about Clarity, Stacks, and developing on Clarinet. +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 -\### What is Clarity? -Clarity is a smart contract language and platform for building decentralized applications (dApps) on the Bitcoin-based Stacks blockchain. - -\### What is Stacks? -Stacks is a layer-1 blockchain network that enables smart contracts and decentralized apps on Bitcoin. Stacks connects to the Bitcoin network but has its own native token, STX. - -\### What is Clarinet? -Clarinet is a local development environment and testing framework for building Clarity smart contracts. It allows developers to quickly write, test, and deploy their Clarity contracts. - -\--- ----- - - + ¿Por qué siguen fallando las descargas? - Descargas de archivos grandes desde almacenamiento en la nube pueden ser interrumpidas debido a problemas de red, limitación de tarifas, 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`. - - ¿Cuál método de descarga debo usar? + + ¿Qué método de descarga debo usar? - Para las velocidades más rápidas, use - - Clarinet - - \---, Stacks, Clarity - - title: Clarinet Deployment - description: Deploy the Clarinet stack - sidebarTitle: Clarinet `gcloud storage cp` título: Estilo de diseño clarinete - descripción: --- un gran estilo de diseño de interfaz de usuario basado en Clarinet - sidebarTitle: Clarinet UI - - \--- - - Clarinet UI es un conjunto de herramientas de diseño e implementación que ayudan a los equipos a construir aplicaciones web con un estilo de interfaz de usuario coherente y moderno. - - \--- - - Características: - - \- Sólida base de componentes de interfaz de usuario listos para usar - \- Diseño y documentación de alta calidad - \- Totalmente compatible con Stacks y Clarity - \- Escalable y fácil de personalizar - \- Soporte continuo y actualizaciones regulares `wget -c`. Para confiabilidad sin autenticación, intenta un administrador 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? - 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 mainnet (varios cientos de GB+) puede tardar 6-24+ horas 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` reanudar 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é es gcloud más rápido? - Google Cloud CLI utiliza transferencias paralelas y protocolos optimizados cuando descarga 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/tools/chainhook/(chainhook-sdk)/edit-update.mdx b/content/docs/es/tools/chainhook/(chainhook-sdk)/edit-update.mdx index 7c5cdf56a..b0706108a 100644 --- a/content/docs/es/tools/chainhook/(chainhook-sdk)/edit-update.mdx +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/edit-update.mdx @@ -1,25 +1,21 @@ --- -title: Editar y Actualizar +title: Editar chainhooks description: Modificar chainhooks existentes usando el SDK --- -Modifica chainhooks existentes incluyendo filtros, acciones y opciones. La interfaz de usuario de la plataforma no admite edición - usa el SDK en su lugar. +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 no admite actualmente la edición de chainhooks. Debes usar el SDK o API para realizar actualizaciones. +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 +### Campos mutables vs inmutables -| Mutable (Puede Actualizar) | Immutable (No Puede Actualizar) | +| Mutable | Immutable | |---------------------|---------------------------| | `name` | `chain` | -| `filters` I notice that you've provided the translation rules and setup, but the actual text to translate appears to be just a single vertical bar "|" character. - -Since this is just a formatting character and not text content that needs translation, the translation would be: - -| `network` | +| `filters` | `network` | | `action` | | | `options` | | @@ -41,19 +37,13 @@ await client.updateChainhook('chainhook-uuid', { }); ``` -## Patrones de Actualización Comunes +### Agregar filtro de eventos (conservando los eventos existentes) -| Update Type | Example | -|-------------|---------| -| **URL del webhook** | `{ action: { type: 'http_post', url: 'https://new-server.com/webhooks' } }` | -| **Nombre** | `{ name: 'production-ft-tracker' }` | -| **Filtros** | `{ filters: { events: [{ type: 'nft_transfer' }] } }` | - -### Agregar Filtro de Evento (Conservando el Existente) +Para agregar un nuevo filtro de eventos a un chainhook existente, puedes obtener la definición actual, modificarla y luego actualizarla. ```typescript -const current = await client.getChainhook('chainhook-uuid'); - +// ✅ Good: Fetch first +const current = await client.getChainhook(uuid); await client.updateChainhook('chainhook-uuid', { filters: { events: [ @@ -62,6 +52,11 @@ await client.updateChainhook('chainhook-uuid', { ], }, }); + +// ❌ Bad: Will overwrite any existing events +await client.updateChainhook(uuid, { + filters: { events: { type: 'contract_call', contract_identifier: 'SP...XYZ.counter' } }, +}); ``` ### Actualizar Múltiples Campos @@ -73,51 +68,12 @@ await client.updateChainhook('chainhook-uuid', { action: { type: 'http_post', url: 'https://new-url.com/webhooks' }, options: { decode_clarity_values: true }, }); -``` - -## Ejemplo de cURL - -### Actualizar Chainhook - -```bash -curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ - "name": "Updated chainhook name", - "action": { "type": "http_post", "url": "https://new-server.com/webhooks" } - }' -``` - -### Respuesta -* **Éxito**: HTTP `204 No Content` -* **Verificar**: Obtener el chainhook para confirmar cambios - -```typescript -await client.updateChainhook('chainhook-uuid', { name: 'New name' }); const updated = await client.getChainhook('chainhook-uuid'); console.log('Updated:', updated.definition.name); ``` -### Ejemplo: Actualización Aditiva Segura - -```typescript -// ✅ Good: Fetch first -const current = await client.getChainhook(uuid); -await client.updateChainhook(uuid, { - filters: { - events: [...current.definition.filters.events, newEvent], - }, -}); - -// ❌ Bad: Might overwrite -await client.updateChainhook(uuid, { - filters: { events: [newEvent] }, -}); -``` - :::next-steps * [Lista y Obtener](/tools/chainhook/list-fetch): Recuperar información de chainhook antes de actualizar -* [Referencia de Filtros](/tools/chainhook/reference/filters): Explora todas las opciones de filtro +* [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 index d94e4a28d..fc4701b23 100644 --- a/content/docs/es/tools/chainhook/(chainhook-sdk)/evaluate.mdx +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/evaluate.mdx @@ -1,62 +1,65 @@ --- -title: Evaluar Chainhook -description: Probar chainhooks contra bloques pasados específicos +title: Evaluar un bloque específico +description: Ejecuta tus chainhooks contra bloques específicos para pruebas, depuración e indexación histórica. --- -Prueba tu configuración de chainhook contra bloques pasados 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 -| Method | Parameter | Example | +| Método | Parámetro | Ejemplo | |--------|-----------|---------| -| **Por altura** | `block_height` I don't see any text provided to translate. Please provide the text you'd like me to translate from English to Spanish, and I'll follow all the critical rules you've specified. `{ block_height: 100000 }` | -| **Por hash** | `index_block_hash` | `{ index_block_hash: '0xa204...' }` | +| **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 -```typescript -import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; + + ```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!, + }); -const client = new ChainhooksClient({ - baseUrl: CHAINHOOKS_BASE_URL.testnet, - apiKey: process.env.HIRO_API_KEY!, -}); + await client.evaluateChainhook('chainhook-uuid', { + // !mark + block_height: 100000, + }); + ``` -await client.evaluateChainhook('chainhook-uuid', { - block_height: 100000, -}); -``` + ```ts !! with-block-hash.ts + import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; -Returns HTTP `204 No Content`. Si los filtros coinciden, la carga útil del webhook se envía a tu `action.url`. + const client = new ChainhooksClient({ + baseUrl: CHAINHOOKS_BASE_URL.testnet, + apiKey: process.env.HIRO_API_KEY!, + }); -### cURL + await client.evaluateChainhook('chainhook-uuid', { + // !mark + index_block_hash: '0xa204...', + }); + ``` + -```bash -curl -X POST \ - "https://api.testnet.hiro.so/chainhooks/me/chainhook-uuid/evaluate" \ - -H "x-api-key: $HIRO_API_KEY" \ - -H "content-type: application/json" \ - -d '{ "block_height": 100000 }' -``` +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 | |----------|-------------|---------| -| **Depurar** | Investigar eventos perdidos | Evaluar bloque específico que debería haber activado | +| **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** | Corregir problemas del manejador de webhook | Re-evaluar después de corregir errores | - -## Mejores Prácticas - -| Práctica | Ejemplo | -|----------|---------| -| **Limitación de velocidad** | Agregar retraso de 100ms entre solicitudes | -| **Prueba primero** | Evaluar 2-3 bloques de muestra antes del procesamiento masivo | -| **Usar altura** | Más fácil de iterar que los hashes de bloques | +| **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): Configura qué eventos coincidir +* [Referencia de Filtros](/tools/chainhook/reference/filters): Configurar qué eventos coinciden ::: diff --git a/content/docs/es/tools/chainhook/(chainhook-sdk)/list-fetch.mdx b/content/docs/es/tools/chainhook/(chainhook-sdk)/list-fetch.mdx index 959061ee5..c1f617b7b 100644 --- a/content/docs/es/tools/chainhook/(chainhook-sdk)/list-fetch.mdx +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/list-fetch.mdx @@ -1,16 +1,12 @@ --- -title: Lista y Obtención +title: Obtener chainhooks description: Recuperar información de chainhook usando el SDK --- -# Lista y Obtener - -Aprende cómo listar y obtener chainhooks usando el SDK de Chainhook. - ## getChainhooks -Recuperar una lista paginada de todos tus chainhooks. +Recupera una lista paginada de todos tus chainhooks. -### TypeScript +### Ejemplo ```typescript import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; @@ -29,57 +25,14 @@ console.log('Limit:', chainhooks.limit); console.log('Offset:', chainhooks.offset); ``` -### Con Opciones de Paginación +### Con opciones ```typescript -// Get specific page with custom limit +// Get specific page with 50 chainhooks starting from position 100 const chainhooks = await client.getChainhooks({ limit: 50, offset: 100, }); - -// This fetches 50 chainhooks starting from position 100 -``` - -### cURL - -```bash -curl -sS "https://api.testnet.hiro.so/chainhooks/me?limit=20&offset=0" \ - -H "x-api-key: $HIRO_API_KEY" -``` - -### Respuesta - -```json -{ - "limit": 20, - "offset": 0, - "total": 45, - "results": [ - { - "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", - "definition": { - "version": "1", - "name": "my-chainhook", - "chain": "stacks", - "network": "testnet", - "filters": { ... }, - "action": { ... } - }, - "status": { - "enabled": true, - "type": "active", - "last_evaluated_at": 1696432154, - "last_evaluated_block_height": 150000, - "last_occurrence_at": 1696432100, - "last_occurrence_block_height": 149980, - "evaluated_block_count": 1000, - "occurrence_count": 42 - } - }, - // ... more chainhooks - ] -} ``` *** @@ -88,59 +41,12 @@ curl -sS "https://api.testnet.hiro.so/chainhooks/me?limit=20&offset=0" \ Retrieve a specific chainhook by UUID. -### TypeScript +### Ejemplo ```typescript const chainhook = await client.getChainhook('be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185'); ``` -### cURL - -```bash -curl -sS "https://api.testnet.hiro.so/chainhooks/me/" \ - -H "x-api-key: $HIRO_API_KEY" -``` - -### Respuesta - -```json -{ - "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", - "definition": { - "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 - } - }, - "status": { - "enabled": true, - "type": "active", - "last_evaluated_at": 1696432154, - "last_evaluated_block_height": 150000, - "last_occurrence_at": 1696432100, - "last_occurrence_block_height": 149980, - "evaluated_block_count": 1000, - "occurrence_count": 42 - } -} -``` - :::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 index 5cf3c8102..29116d2ed 100644 --- a/content/docs/es/tools/chainhook/(chainhook-sdk)/manage-keys.mdx +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/manage-keys.mdx @@ -1,58 +1,57 @@ --- -title: Gestionar Claves -description: Rotar secretos de consumidor y validar cada entrega de Chainhook +title: Administrar Claves +description: Rotar secretos del consumidor y validar cada entrega de Chainhook --- -## Lo que aprenderás +## Qué aprenderás :::objectives -* Crear/rotar un secreto de consumidor Chainhook. -* Valida las solicitudes de webhook verificando el `Authorization` header. +* Crear/rotar un secreto de consumidor de Chainhook. +* Validar solicitudes de webhook verificando la `Authorization` encabezado. ::: -## Requisitos previos +## Prerrequisitos :::prerequisites -* Clave API de Hiro almacenada como `CHAINHOOKS_API_KEY`. -* Chainhook UUID you want to protect. -* Runtime de Node.js (el ejemplo usa Fastify). +* Clave API de Hiro +* Node.js (el ejemplo del servidor usa Fastify). ::: ## Validación de solicitudes de webhook con un secreto de consumidor -Los chainhooks adjuntan un `Authorization: Bearer ` encabezado a cada intento de webhook, proporcionándote un simple protocolo de intercambio de secreto compartido. +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. Rotar el secreto con `await client.rotateConsumerSecret(chainhookUuid)` (o el `/chainhooks/{uuid}/secret` API) cada vez que necesites un nuevo token. -2. Persistir lo devuelto `secret` en tu administrador de secretos y recárgalo al inicio del proceso o a través de un bucle de actualización corto. -3. Rechazar las entregas de webhook cuyas `Authorization` el encabezado no es igual a `Bearer `. +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 `. -### Rotar/crear secreto del consumidor +### Crear/rotar secreto del consumidor -```ts +```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.CHAINHOOKS_API_KEY!, + apiKey: process.env.HIRO_API_KEY!, }); -let consumerSecret: string = await client.rotateConsumerSecret(chainhookUuid).secret; +// Store this value securely and use it to validate webhook requests +const secret = await client.rotateConsumerSecret(chainhookUuid).secret; ``` ### Servidor Fastify de ejemplo -```ts +```ts -n import Fastify from 'fastify'; const server = Fastify(); server.post('/webhook', async (request, reply) => { - if (!consumerSecret) { + if (!secret) { reply.code(503).send({ error: 'consumer secret unavailable' }); return; } const authHeader = request.headers.authorization; - if (authHeader !== `Bearer ${consumerSecret}`) { + if (authHeader !== `Bearer ${secret}`) { reply.code(401).send({ error: 'invalid consumer secret' }); return; } diff --git a/content/docs/es/tools/chainhook/(chainhook-sdk)/register-enable.mdx b/content/docs/es/tools/chainhook/(chainhook-sdk)/register-enable.mdx index b7efea2fc..166f8aeb8 100644 --- a/content/docs/es/tools/chainhook/(chainhook-sdk)/register-enable.mdx +++ b/content/docs/es/tools/chainhook/(chainhook-sdk)/register-enable.mdx @@ -1,18 +1,18 @@ --- -title: Registrar y Habilitar -description: Crear y activar chainhooks usando el SDK +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. -## registrarChainhook +## registerChainhook -Crea una nueva configuración de chainhook. Por defecto, los nuevos chainhooks se crean en estado deshabilitado a menos que `enable_on_registration` se establece en `true`. +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`. -### TypeScript +### Ejemplo -```typescript +```ts -nc import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; const client = new ChainhooksClient({ @@ -40,7 +40,7 @@ const chainhook = await client.registerChainhook({ }, options: { decode_clarity_values: true, - enable_on_registration: true, // Enable immediately + enable_on_registration: true, }, }); @@ -48,86 +48,15 @@ console.log('Chainhook UUID:', chainhook.uuid); console.log('Enabled:', chainhook.status.enabled); // true ``` -### cURL - -```bash -curl -sS -X POST "https://api.testnet.hiro.so/chainhooks/me" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ - "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 - } - }' -``` - -### Respuesta - -```json -{ - "uuid": "be4ab3ed-b606-4fe0-97c4-6c0b1ac9b185", - "definition": { - "version": "1", - "name": "my-chainhook", - "chain": "stacks", - "network": "testnet", - "filters": { ... }, - "action": { ... }, - "options": { ... } - }, - "status": { - "enabled": true, - "type": "active" - } -} -``` - -### Habilitar más tarde - -Si no estableces `enable_on_registration`, el chainhook será creado pero deshabilitado: - -```typescript -const chainhook = await client.registerChainhook({ - version: '1', - name: 'my-chainhook', - chain: 'stacks', - network: 'testnet', - filters: { /* ... */ }, - action: { /* ... */ }, - // No options.enable_on_registration -}); - -console.log('Enabled:', chainhook.status.enabled); // false - -// Enable it later -await client.enableChainhook(chainhook.uuid, 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 pause a chainhook without deleting it. +Enable or disable a single chainhook by UUID. This allows you to enable chainhooks after registration or pause without deleting it. -### TypeScript +### Ejemplos ```typescript // Enable a chainhook @@ -137,44 +66,23 @@ await client.enableChainhook('chainhook-uuid', true); await client.enableChainhook('chainhook-uuid', false); ``` -### cURL - -Habilitar: - -```bash -curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me//enabled" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ "enabled": true }' -``` - -Desactivar: - -```bash -curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me//enabled" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ "enabled": false }' -``` - -### Respuesta - Returns HTTP `204 No Content` en caso de éxito. *** ## bulkEnableChainhooks -Habilitar o deshabilitar múltiples chainhooks a la vez usando filtros. Esto es útil para gestionar muchos chainhooks de forma programática. +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 UUID (máximo 200): +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', @@ -192,6 +100,7 @@ Enable all chainhooks that POST to a specific URL: await client.bulkEnableChainhooks({ enabled: true, filters: { + // !mark webhook_url: 'https://example.com/webhooks', }, }); @@ -202,9 +111,11 @@ await client.bulkEnableChainhooks({ Habilitar todos los chainhooks con un estado específico: ```typescript + await client.bulkEnableChainhooks({ enabled: true, filters: { + // !mark statuses: ['inactive'], }, }); @@ -218,6 +129,7 @@ Usa múltiples filtros juntos: await client.bulkEnableChainhooks({ enabled: false, // Disable matching chainhooks filters: { + // !mark(1:2) webhook_url: 'https://old-server.com/webhooks', statuses: ['active'], }, @@ -226,40 +138,7 @@ await client.bulkEnableChainhooks({ This will disable all active chainhooks that POST to the old webhook URL. -### cURL - -```bash -curl -sS -X PATCH "https://api.testnet.hiro.so/chainhooks/me/enabled" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d '{ - "enabled": true, - "filters": { - "uuids": ["uuid-1", "uuid-2"], - "webhook_url": "https://example.com/webhooks" - } - }' -``` - -### Respuesta - -```json -{ - "updated": 5, - "results": [ - { - "uuid": "uuid-1", - "enabled": true - }, - { - "uuid": "uuid-2", - "enabled": true - } - ] -} -``` - :::next-steps -* [Evaluar](/tools/chainhook/evaluate): Prueba chainhooks contra bloques pasados -* [Referencia de filtros](/tools/chainhook/reference/filters): Explora todas las opciones de filtro +* [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/(overview)/faq.mdx b/content/docs/es/tools/chainhook/(overview)/faq.mdx index 69111f31f..c1b9ea4bd 100644 --- a/content/docs/es/tools/chainhook/(overview)/faq.mdx +++ b/content/docs/es/tools/chainhook/(overview)/faq.mdx @@ -1,27 +1,27 @@ --- -title: FAQ +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 contacta a [support@hiro.so](mailto\:support@hiro.so). + 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? + ¿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? @@ -29,11 +29,11 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta - - ¿Cómo se determinarán los precios de Chainhooks después de la Beta? + + ¿Cómo se fijarán los precios de los Chainhooks después de la Beta? - Los Chainhooks se cobrarán usando un modelo de créditos por cada entrega, con usuarios capaces de seleccionar diferentes límites de créditos dependiendo de su uso. Para los usuarios de Chainhooks 2.0 Beta, sus límites post-beta serán inicialmente determinados por su nivel de suscripción existente de Hiro. + 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. @@ -42,10 +42,10 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta - ¿Qué pasará con los Chainhooks heredados existentes que se ejecutan en v1.0? + ¿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 o a través de la API durante y después del período Beta se ejecutarán en v2.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. @@ -56,12 +56,20 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta - Si v2.0 y v1.0 van a estar activas al mismo tiempo, ¿cómo accedemos a ambas? + 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 seguirán entregando eventos, pero no puedes modificarlos a través de la interfaz de usuario de la Plataforma. + 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. Ver el [Guía de Migración](/tools/chainhook/migration) para un tutorial completo. + 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). @@ -73,31 +81,31 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta ¿Puedo editar mis v2 Chainhooks? - La Plataforma actualmente no admite la edición de chainhooks existentes. Debe usar el [SDK de Chainhook](/tools/chainhook/chainhook-sdk) o [API de Chainhook](/apis/chainhook-api) para editar tus 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 frente al SDK? + ¿Cuándo debo usar la Plataforma vs el SDK?
-

Usa la Plataforma cuando:

+

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
  • -
  • Comenzar rápidamente
  • +
  • Comenzando rápidamente
-

Utiliza el SDK/API cuando:

+

Usa el SDK/API cuando:

  • Necesitas editar chainhooks existentes
  • Gestión programática de chainhook
  • Automatizando operaciones de chainhook
  • -
  • Integración en pipelines de CI/CD
  • +
  • Integrando en pipelines de CI/CD
  • Gestión de muchos chainhooks a escala
@@ -105,7 +113,7 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta
-## Empezando +## Comenzando @@ -114,13 +122,13 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta
    -
  1. Visita platform.hiro.so
  2. +
  3. Visitar platform.hiro.so
  4. Inicia sesión o crea una cuenta
  5. Navega a la sección de Claves API
  6. -
  7. Genera o copia tu clave de API
  8. +
  9. Genera o copia tu clave API
-

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

+

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

@@ -131,7 +139,7 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta
    -
  • Mainnet - La blockchain de producción Stacks con valor económico real
  • +
  • Red principal - La blockchain de producción Stacks con valor económico real
  • Testnet - Una red de prueba para desarrollo y pruebas
@@ -156,7 +164,7 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta ## Solución de problemas - + Mi webhook no está recibiendo eventos. ¿Qué debería verificar? @@ -181,21 +189,21 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta -

4. Verificar logs del endpoint del webhook

+

4. Verificar los logs del endpoint del webhook

  • Ensure your endpoint returns HTTP 200
  • -
  • Busca mensajes de error en los registros de tu aplicación
  • +
  • Busca mensajes de error en los logs de tu aplicación
- - Estoy recibiendo errores 401 No autorizado + + Estoy obteniendo errores 401 No autorizado
@@ -226,7 +234,7 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta
-

Utiliza el método evaluateChainhook() para probar tu configuración contra bloques específicos antes de habilitarla:

+

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

           
@@ -282,29 +290,29 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta
   
 
 
-## Preguntas Comunes
+## 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 cadena.
+      ¡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 reorgs y envía ambos rollback y aplicar eventos. Siempre procesa primero los eventos de rollback para mantener la consistencia de los datos.

+

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? @@ -332,11 +340,11 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta ¿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 el manejo de errores y monitoreo adecuados para aplicaciones de producción. + 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? @@ -361,9 +369,11 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta ## Dónde Obtener Ayuda -* **Discord**: Únete al **#chainhook** canal encendido [Discord](https://stacks.chat/) bajo las Herramientas para Desarrolladores de Hiro -* **Horario de Oficina Semanal**: Todos los jueves a las 11am ET ([agregar al calendario](https://www.addevent.com/event/oL21905919)) -* **Soporte por Email**: [support@hiro.so](mailto\:support@hiro.so) +* **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) *** @@ -372,5 +382,5 @@ description: Preguntas frecuentes sobre Chainhook 2.0 Beta :::next-steps * [Guía de Migración](/tools/chainhook/migration): Migrar de v1 a v2 -* [SDK Inicio Rápido](/tools/chainhook/quickstart): Comience con el SDK +* [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 index c0ab65dab..9fa6e681b 100644 --- a/content/docs/es/tools/chainhook/(overview)/migration.mdx +++ b/content/docs/es/tools/chainhook/(overview)/migration.mdx @@ -1,17 +1,17 @@ --- -title: Migración de v1 a v2 -description: Guía para migrar chainhooks v1 heredados a Chainhook 2.0 Beta +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 interfaz de usuario de la plataforma; los gestionas a través del [API de Plataforma](/apis/platform-api/reference/chainhooks/list). +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 -* Captura y analiza todos los chainhooks v1 existentes. +* 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. ::: @@ -19,154 +19,147 @@ Los chainhooks v1 heredados permanecen de solo lectura en la interfaz de usuario ## Requisitos previos :::prerequisites -* API access to both the Platform API and Chainhook REST API (same `HIRO_API_KEY`). -* SDK Local (`@hirosystems/chainhooks-client`) o `curl` for REST calls. -* Variable de entorno `HIRO_API_KEY` establecido para la CLI y ejemplos de código. +* Clave API de Hiro para acceso tanto a la Plataforma como a la API de Chainhook. ::: - ### Inventario v1 chainhooks + ### Obtener una lista de chainhooks v1 - Utiliza la API de la Plataforma para obtener cada chainhook que aún se ejecuta en producción. + Usa la API de la Plataforma para obtener los chainhooks que quieres migrar. - #### CLI - - ```bash - curl -sS "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks" \ - -H "content-type: application/json" - ``` - - #### TypeScript - - ```typescript - export async function listV1Chainhooks() { - const response = await fetch( - `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks`, - { headers: { 'content-type': 'application/json' } } - ); - - if (!response.ok) throw new Error(response.statusText); - return (await response.json()) as any[]; - } + ```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 listV1Chainhooks(); - console.log(`Found ${chainhooks.length} v1 chainhooks`); + const chainhooks = await response.json(); ``` - ### Inspeccionar un chainhook - - Pull the full definition for each UUID so you can convert custom filters and metadata. - - #### CLI + ### Comenzar el mapeo al nuevo formato v2 - ```bash - curl -sS \ - "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks/$CHAINHOOK_UUID" \ - -H "content-type: application/json" - ``` - - #### TypeScript + Traduce las estructuras v1 a formatos v2 antes de crear nuevos hooks. La siguiente tabla muestra el mapeo entre las estructuras v1 y v2: - ```typescript - export async function getV1Chainhook(uuid: string) { - const response = await fetch( - `https://api.platform.hiro.so/v1/ext/${process.env.HIRO_API_KEY}/chainhooks/${uuid}`, - { headers: { 'content-type': 'application/json' } } - ); - - if (!response.ok) throw new Error(response.statusText); - return await response.json(); - } - ``` - - - - ### Configuración de mapeo a 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 "|". - Traduce estructuras v1 a campos v2 antes de aprovisionar nuevos hooks. + | `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 "|". - | v1 Concept | v2 Target | Notes | - |------------|-----------|-------| - | `if_this.scope` | `filters.events[].type` | Reemplazar `scope/action` combos con un solo tipo de evento. | - | `if_this.actions` | `type` | `transfer` se mapea a `*_transfer`. | - | `then_that.http_post.url` | `action.url` | v2 maneja los secretos automáticamente. | + 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. | - #### Conversión de Ejemplo - - ```jsonc - // v1 (Platform API) - { - "name": "stx-transfers", - "networks": { - "mainnet": { - "if_this": { "scope": "stx_event", "actions": ["transfer"] }, - "then_that": { "http_post": { "url": "https://example.com/webhooks" } } + #### Ejemplo + + + ```json !! Legacy + { + "name": "stx-transfers", + "networks": { + "mainnet": { + "if_this": { "scope": "stx_event", "actions": ["transfer"] }, + "then_that": { "http_post": { "url": "https://example.com/webhooks" } } + } } } - } - ``` - - ```typescript - // v2 (SDK) - import { ChainhooksClient, CHAINHOOKS_BASE_URL } from '@hirosystems/chainhooks-client'; - - const client = new ChainhooksClient({ - baseUrl: CHAINHOOKS_BASE_URL.mainnet, - apiKey: process.env.HIRO_API_KEY!, - }); + ``` + + ```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 + } + } + ``` + - 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, - }, - }); - ``` + Para más detalles sobre los cambios de formato, consulta el [Filtros](/tools/chainhook/reference/filters) guía de referencia. ### Crear chainhooks v2 - Provision each chainhook with the SDK or REST API, mirroring the mapped filters. - - #### REST - - ```bash - curl -sS -X POST "https://api.mainnet.hiro.so/chainhooks/v1/me/" \ - -H "content-type: application/json" \ - -H "x-api-key: $HIRO_API_KEY" \ - -d @v2-chainhook.json - ``` - - #### SDK de Chainhook - - ```typescript - const client = new ChainhooksClient({ - baseUrl: CHAINHOOKS_BASE_URL[config.network], - apiKey: process.env.HIRO_API_KEY!, - }); - const chainhook = await client.registerChainhook(config); - ``` + 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 retirar v1 + ### Validar y limpiar chainhooks heredados Transmite eventos a través de ambas versiones, confirma la entrega, luego limpia las definiciones heredadas. @@ -175,76 +168,62 @@ Los chainhooks v1 heredados permanecen de solo lectura en la interfaz de usuario ### Mejores prácticas - Mantén tanto los hooks v1 como v2 activos hasta que verifiques la paridad de entrega. + Mantén ambos hooks v1 y v2 activos hasta que verifiques la paridad de entrega. ::: - #### Verificaciones de Habilitación - - ```typescript - const chainhook = await client.getChainhook(v2Uuid); - console.log(chainhook.status.enabled); - ``` - - #### Eliminar con cURL - - ```bash - curl -sS -X DELETE \ - "https://api.platform.hiro.so/v1/ext/$HIRO_API_KEY/chainhooks/$CHAINHOOK_UUID" \ - -H "content-type: application/json" - ``` - - #### Eliminar con SDK - - ```typescript - export async function deleteV1Chainhook(uuid: string) { - 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' } } - ); + #### 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' + } + } + ); - if (!response.ok) throw new Error(response.statusText); - return await response.json(); - } + await response.json(); ``` -## Referencia de Traducción de Filtros +## Referencia de filtros -### Ámbitos comunes +### Alcances comunes | v1 | Acciones Típicas | v2 | Extras | |----------|-----------------|-----------|--------| -| `stx_event` I notice that you've provided the translation rules and instructions, but the actual text to translate appears to be just a single vertical bar "|". +| `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 "|". -| `transfer` | `stx_transfer` | Incluir `sender` o `recipient` filtros según sea necesario. | +| `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` 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 vertical bar "|" shown. - -Could you please provide the actual text you'd like me to translate from English to Spanish? `nft_transfer` · `nft_mint` | Proporcionar `asset_identifier`. | - -### Ejemplo - -```jsonc -// v1 filter -{ - "scope": "stx_event", - "actions": ["transfer"] -} -``` - -```jsonc -// v2 filter -{ - "events": [ - { - "type": "stx_transfer", - "recipient": "SP3FBR2AGKQX0..." - } - ] -} -``` +| `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 diff --git a/content/docs/es/tools/chainhook/index.mdx b/content/docs/es/tools/chainhook/index.mdx index 7a3ff1219..76297bb56 100644 --- a/content/docs/es/tools/chainhook/index.mdx +++ b/content/docs/es/tools/chainhook/index.mdx @@ -1,7 +1,7 @@ --- title: Chainhook -sidebarTitle: Visión general -description: Chainhook es un indexador consciente de reorganización 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 --- :::callout @@ -9,30 +9,28 @@ type: warn ### Chainhook 2.0 Beta -Chainhook 2.0 se encuentra actualmente en versión beta. Durante este período, nos enfocamos en aprender sobre confiabilidad, rendimiento y experiencia del desarrollador. Si encuentras problemas o tienes comentarios, por favor comunícate con [support@hiro.so](mailto\:support@hiro.so). +Chainhook 2.0 está actualmente en beta. Si encuentras problemas o tienes comentarios, por favor contacta a [beta@hiro.so](mailto\:beta@hiro.so). ::: ## Descripción general -Chainhook es un indexador consciente de reorganizaciones que te permite construir flujos de eventos personalizados desde las blockchains de Bitcoin y Stacks. A diferencia de los indexadores tradicionales, Chainhook maneja automáticamente las reorganizaciones de blockchain, asegurando que tus datos se mantengan precisos sin reindexación manual. +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. Con Chainhook 2.0, puedes gestionar chainhooks a través de: -* **[Chainhook SDK](/tools/chainhook/chainhook-sdk)** - Cliente TypeScript/JavaScript para gestión programática +* **[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 -Para explorar las funciones de Chainhook con IA, copia y pega [llms.txt](/tools/chainhook/llms.txt) en tu LLM de elección. +## Características Clave -## Características principales - -* **Indexación consciente de reorganización** - Maneja automáticamente las bifurcaciones y reorganizaciones de blockchain +* **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 del SDK](/tools/chainhook/quickstart): Comienza con el SDK de Chainhook en 5 minutos -- [Inicio Rápido de la Plataforma](/tools/chainhook/platform-quickstart): Crea tu primer chainhook en la interfaz de usuario de la plataforma +- [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 @@ -40,5 +38,5 @@ type: help ### ¿Necesitas ayuda con Chainhook? -Contáctanos en el #chainhook canal encendido [Discord](https://stacks.chat/) bajo la sección Herramientas de Desarrollador de Hiro. +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/reference/filters.mdx b/content/docs/es/tools/chainhook/reference/filters.mdx index 4fb1db550..c5c19bf0e 100644 --- a/content/docs/es/tools/chainhook/reference/filters.mdx +++ b/content/docs/es/tools/chainhook/reference/filters.mdx @@ -1,30 +1,30 @@ --- -title: Referencia de Filtros +title: Filtros description: Referencia completa para todos los tipos de filtros de Chainhook --- -# Referencia de Filtros +Los filtros definen qué eventos de blockchain activarán tu chainhook. -Los filtros definen qué eventos de blockchain activarán tu chainhook—usa la matriz de abajo para elegir la entrada correcta para `filters.events[]`. +Aquí está una referencia completa para todos los tipos de filtros de Chainhook: -| Filter | Cuándo usar | +| Filtro | Cuándo usar | |--------|-------------| -| `ft_event` | FT: capturar *cada* SIP-010 transferir, acuñar o quemar a través de activos. | -| `ft_transfer` | FT: follow a single asset such as USDC; optionally add `sender`/`receiver` para disparadores a nivel de billetera. | -| `ft_mint` | FT: rastrea expansiones de suministro o flujos de entrada de puente para un activo (establecer `asset_identifier`). | -| `ft_burn` | FT: rastrear canjes o contracciones de suministro (establecer `asset_identifier`). | -| `nft_event` | NFT: monitorea cada transferencia, acuñación o quema para todas las colecciones. | -| `nft_transfer` | NFT: seguir una colección SIP-009; agregar `sender`, `receiver`, o `value` para focalización de propietario/token. | -| `nft_mint` | NFT: observa cada nueva acuñación para una colección (establecer `asset_identifier`). | -| `nft_burn` | NFT: capturar quemadas o canjes (establecer `asset_identifier`). | -| `stx_event` | STX: capturar todas las transferencias nativas, acuñaciones o quemas. | -| `stx_transfer` | STX: rastrear cada transferencia; agregar `sender` o `receiver` para destacar directores específicos. | -| `contract_deploy` | Contratos: reaccionar a nuevos contratos que ingresan a la red. | -| `contract_call` | Contratos: observar cada invocación; reducir con `contract_identifier` y `function_name`. | -| `contract_log` | Contratos: capturar `print`/salida de log de un contrato (establecer `contract_identifier`). | -| `coinbase` | Sistema: observar las recompensas de los mineros llegando a la cadena. | -| `tenure_change` | Sistema: rastrear actualizaciones de tenencia de Proof-of-Transfer; agregar `cause` I don't see any text provided 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 Token Fungible (FT) +| `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 @@ -38,11 +38,12 @@ Coincidir con cualquier evento de token fungible (transferencia, quema o acuñac ### Transferencia FT -Coincidencia de transferencias FT para un activo específico: +Coincidir transferencias FT para un activo específico: ```json { "type": "ft_transfer", + // !mark "asset_identifier": "SP...ABC.ft::usdc" } ``` @@ -53,23 +54,25 @@ Filtrar por remitente: { "type": "ft_transfer", "asset_identifier": "SP...ABC.ft::usdc", + // !mark "sender": "SP...FROM" } ``` -Filtrar por destinatario: +Filtrar por receptor: ```json { "type": "ft_transfer", "asset_identifier": "SP...ABC.ft::usdc", + // !mark "receiver": "SP...TO" } ``` ### Acuñación de FT -Hacer coincidir eventos de acuñación FT: +Coincidir eventos de acuñación FT: ```json { @@ -80,7 +83,7 @@ Hacer coincidir eventos de acuñación FT: ### FT Burn -Coincidir eventos de quema FT: +Coincidir eventos de quema de FT: ```json { @@ -91,7 +94,7 @@ Coincidir eventos de quema FT: *** -## Eventos de Tokens No Fungibles (NFT) +## Eventos de Token No Fungible (NFT) ### Cualquier Evento NFT @@ -105,11 +108,12 @@ Coincide con cualquier evento de NFT (transferencia, quema o acuñación): ### Transferencia de NFT -Coincide transferencias de NFT para una colección específica: +Coincidir transferencias de NFT para una colección específica: ```json { "type": "nft_transfer", + // !mark "asset_identifier": "SP...COLL.nft::collectible" } ``` @@ -120,6 +124,7 @@ Filtrar por remitente: { "type": "nft_transfer", "asset_identifier": "SP...COLL.nft::collectible", + // !mark "sender": "SP...FROM" } ``` @@ -130,6 +135,7 @@ Filtrar por receptor: { "type": "nft_transfer", "asset_identifier": "SP...COLL.nft::collectible", + // !mark "receiver": "SP...TO" } ``` @@ -140,13 +146,14 @@ Filtrar por ID de token específico: { "type": "nft_transfer", "asset_identifier": "SP...COLL.nft::collectible", + // !mark "value": "u123" } ``` ### Acuñación de NFT -Coincidir eventos de acuñación de NFT: +Hacer coincidir eventos de acuñación de NFT: ```json { @@ -157,7 +164,7 @@ Coincidir eventos de acuñación de NFT: ### Quema de NFT -Hacer coincidir eventos de quema de NFT: +Coincidir eventos de quema de NFT: ```json { @@ -172,7 +179,7 @@ Hacer coincidir eventos de quema de NFT: ### Cualquier Evento STX -Coincide con cualquier evento STX (transferencia, quema o acuñación): +Coincidir con cualquier evento STX (transferencia, quema o acuñación): ```json { @@ -195,6 +202,7 @@ Filtrar por remitente: ```json { "type": "stx_transfer", + // !mark "sender": "SP...SENDER" } ``` @@ -204,13 +212,14 @@ Filtrar por receptor: ```json { "type": "stx_transfer", + // !mark "receiver": "SP...RECEIVER" } ``` *** -## Eventos de Contratos +## Eventos de Contrato ### Implementación de Contrato @@ -227,6 +236,7 @@ Filtrar por implementador: ```json { "type": "contract_deploy", + // !mark "sender": "SP...DEPLOYER" } ``` @@ -246,6 +256,7 @@ Coincidir llamadas a un contrato específico: ```json { "type": "contract_call", + // !mark "contract_identifier": "SP...XYZ.counter" } ``` @@ -256,6 +267,7 @@ Coincidir llamadas a una función específica: { "type": "contract_call", "contract_identifier": "SP...XYZ.counter", + // !mark "function_name": "increment" } ``` @@ -267,17 +279,27 @@ Filtrar por llamador: "type": "contract_call", "contract_identifier": "SP...XYZ.counter", "function_name": "increment", + // !mark "sender": "SP...CALLER" } ``` ### Registro de Contrato -Coincidir eventos de impresión del 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" } ``` @@ -288,6 +310,7 @@ Filtrar por remitente de transacción: { "type": "contract_log", "contract_identifier": "SP...XYZ.counter", + // !mark "sender": "SP...SENDER" } ``` @@ -321,15 +344,17 @@ Coincidir cambios de tenencia por causa (bloque encontrado): ```json { "type": "tenure_change", + // !mark[/"block_found"/] "cause": "block_found" } ``` -Cambios de tenencia coincidentes por causa (extendido): +Cambios de permanencia coincidentes por causa (extendido): ```json { "type": "tenure_change", + // !mark[/"extended"/] "cause": "extended" } ``` @@ -338,7 +363,7 @@ Cambios de tenencia coincidentes por causa (extendido): ## Combinando Filtros -Puedes combinar múltiples filtros en el `filters.events` arreglo. Un chainhook se activará si **cualquier** de los filtros coinciden: +Puedes combinar múltiples filtros en el `filters.events` array. Un chainhook se activará si **cualquier** de los filtros coinciden: ```json { @@ -358,8 +383,6 @@ Puedes combinar múltiples filtros en el `filters.events` arreglo. Un chainhook } ``` -This chainhook will trigger on either DIKO transfers **o** llamadas al `increment` función. - :::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 index 79d038e6d..3b31ba846 100644 --- a/content/docs/es/tools/chainhook/reference/options.mdx +++ b/content/docs/es/tools/chainhook/reference/options.mdx @@ -1,8 +1,8 @@ --- -title: Referencia de Opciones +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. El `options` el campo es opcional y puede omitirse o establecerse en `null`. +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. @@ -15,21 +15,21 @@ Todas las opciones booleanas tienen como valor predeterminado `false` si se omit | 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 despliegues | -| `include_contract_source_code` | booleano | `false` | Incluir código fuente en despliegues | +| `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 y Bitcoin) | +| `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` | booleano | `false` | Habilitar chainhook inmediatamente en la creación | -| `expire_after_evaluations` | integer | ninguno | Expira después de N bloques evaluados | +| `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 you've provided the translation rules but the actual text to translate appears to be just a single colon ":". +* **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` @@ -42,29 +42,11 @@ Incluir legible para humanos `repr` (y tipos inferidos) junto con `hex` para arg } ``` -**I don't see any text provided to translate. Please provide the specific text you'd like me to translate from English to Spanish, and I'll follow all the rules you've outlined: - -1\. Keep "---" exactly as "---" -2\. Don't translate field names like "title:", "description:", "sidebarTitle:" -3\. Only translate the text values after colons -4\. Preserve all Markdown/YAML formatting -5\. Keep "Clarinet", "Stacks", and "Clarity" untranslated - -Please share the text you need translated.**: - -```json -{ - "hex": "0x01000000000000000000000002690ed9fe", - "repr": "u10352515582", - "type": "uint" -} -``` - *** ### include\_contract\_abi -Incluir el ABI del contrato en las operaciones de implementación. Esto mejora la tipificación de argumentos al decodificar. +Incluir la ABI del contrato en las operaciones de despliegue. Esto mejora el tipado de argumentos al decodificar. * **Tipo**: `boolean` * **Predeterminado**: `false` @@ -81,12 +63,10 @@ Incluir el ABI del contrato en las operaciones de implementación. Esto mejora l ### include\_contract\_source\_code -Incluye el código fuente del contrato en las operaciones de implementación. +Incluir el código fuente del contrato en las operaciones de despliegue. * **Tipo**: `boolean` -* **Por defecto**I notice that the text you provided to translate appears to be just a single colon (:). Since there is no actual content to translate, I'll return it as-is: - - : `false` +* **Predeterminado**: `false` ```json { @@ -100,7 +80,7 @@ Incluye el código fuente del contrato en las operaciones de implementación. ### include\_post\_conditions -Incluir post-condiciones decodificadas en los metadatos de la transacción. +Incluir post-condiciones decodificadas en los metadatos de transacción. * **Tipo**: `boolean` * **Predeterminado**: `false` @@ -117,7 +97,7 @@ Incluir post-condiciones decodificadas en los metadatos de la transacción. ### include\_raw\_transactions -Incluir hex de transacción sin procesar en metadatos de transacción. +Incluir hex de transacción sin procesar en los metadatos de transacción. * **Tipo**: `boolean` * **Predeterminado**: `false` @@ -134,7 +114,7 @@ Incluir hex de transacción sin procesar en metadatos de transacción. ### include\_block\_metadata -Incluye metadatos de bloque tanto para bloques de Stacks como de Bitcoin, con costos de ejecución, conteo de transacciones, etc. +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` @@ -153,12 +133,12 @@ Incluye metadatos de bloque tanto para bloques de Stacks como de Bitcoin, con co Incluir información del firmante y firmas en los metadatos del bloque. -* **Tipo**I notice that you've provided the translation instructions but the actual text to translate appears to be just a single colon ":". +* **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 actual content to translate (just a colon), the translation remains: + Since there's no translatable content (just a colon), I'll return it as-is: : `boolean` -* **Por defecto**: `false` +* **Default**: `false` ```json { @@ -174,10 +154,14 @@ Incluir información del firmante y firmas en los metadatos del bloque. ### enable\_on\_registration -Habilitar el chainhook inmediatamente después del registro/creación. Por defecto, un nuevo chainhook está deshabilitado al crearse. +Habilitar el chainhook inmediatamente después del registro/creación. Por defecto, un nuevo chainhook está deshabilitado al momento de la creación. -* **Tipo**: `boolean` -* **Predeterminado**: `false` +* **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 { @@ -195,10 +179,10 @@ Cuando se establece en `true`, la respuesta incluirá `status.enabled = true` y ### expire\_after\_evaluations -Vencer automáticamente el chainhook después de evaluar este número de bloques. +Expirar automáticamente el chainhook después de evaluar esta cantidad de bloques. * **Tipo**: `integer` -* **Por defecto**: Ninguno (sin expiración) +* **Predeterminado**: Ninguno (sin vencimiento) ```json { @@ -214,10 +198,10 @@ Esta chainhook expirará después de evaluar 10,000 bloques. ### expire\_after\_occurrences -Expira automáticamente el chainhook después de este número de ocurrencias coincidentes. +Hacer expirar automáticamente el chainhook después de esta cantidad de ocurrencias coincidentes. * **Tipo**: `integer` -* **Predeterminado**: Ninguno (sin vencimiento) +* **Predeterminado**: Ninguno (sin expiración) ```json { @@ -232,6 +216,6 @@ Este chainhook expirará después de activarse 250 veces. *** :::next-steps -* [Referencia de Filtro](/tools/chainhook/reference/filters): Aprende sobre todos los filtros de eventos disponibles -* [Anatomía del Payload](/tools/chainhook/reference/payload-anatomy): Comprende la estructura de las cargas útiles de chainhook +* [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 index ccb9f9c10..56bc1fa50 100644 --- a/content/docs/es/tools/chainhook/reference/payload-anatomy.mdx +++ b/content/docs/es/tools/chainhook/reference/payload-anatomy.mdx @@ -1,13 +1,15 @@ --- -title: Anatomía del Payload -description: Entendiendo la estructura de las cargas útiles de webhook de Chainhook +title: Anatomía del payload +description: Comprendiendo la estructura de las cargas útiles de webhook de Chainhook --- -## Descripción General de la Carga Útil +## Resumen de Payload -Un payload de Chainhook consiste en dos secciones principales: +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: -1. **evento** - Contiene los datos de la blockchain (bloques, transacciones, operaciones) -2. **chainhook** - Metadatos sobre el chainhook que se activó +: + +* `event` contiene los datos de la blockchain (bloques, transacciones, operaciones) +* `chainhook` contiene metadatos sobre el chainhook que se activó *** @@ -34,7 +36,7 @@ Un payload de Chainhook consiste en dos secciones principales: ### Aplicar Array -El `apply` la matriz contiene bloques que se están agregando a la cadena canónica. Cada bloque incluye: +El `apply` array contiene bloques que se están agregando a la cadena canónica. Cada bloque incluye: ```json { @@ -51,19 +53,19 @@ El `apply` la matriz contiene bloques que se están agregando a la cadena canón } ``` -### Revertir Array +### 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 reversión. Tu aplicación debe revertir cualquier cambio de estado de los bloques reversados. +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 Transacción +## Estructura de Transacciones -Cada transacción en el `transactions` la matriz contiene: +Cada transacción en la `transactions` array contiene: ### Metadatos @@ -114,9 +116,9 @@ Identificador único para la transacción: *** -## Array de Operaciones +## Matriz de Operaciones -Cada transacción incluye un `operations` array que describe cambios de estado. Las operaciones se indexan secuencialmente. +Cada transacción incluye un `operations` matriz que describe los cambios de estado. Las operaciones están indexadas secuencialmente. ### Operación de Tarifa @@ -244,6 +246,6 @@ Declaraciones de impresión de contratos: ``` :::next-steps -* [Referencia de Filtros](/tools/chainhook/reference/filters): Configura qué eventos activan tu chainhook -* [Referencia de Opciones](/tools/chainhook/reference/options): Personalizar contenido del payload +* [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. ::: From 73dc4a9b4d9cc1a238666d218b92ef3fdd8a6d89 Mon Sep 17 00:00:00 2001 From: Ryan Waits Date: Mon, 10 Nov 2025 20:21:07 -0600 Subject: [PATCH 16/16] update translation lockfile --- idioma.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/idioma.lock b/idioma.lock index 546a357e6..532adef3f 100644 --- a/idioma.lock +++ b/idioma.lock @@ -5,7 +5,7 @@ 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: @@ -49,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: @@ -1273,11 +1273,11 @@ files: translations: es: true content/docs/en/resources/archive/download-guide.mdx: - content: 1374bfd8028e4ee7d503de95ed825465 + content: 830654f26dc77cad2d5a56a7ba9aa849 translations: es: true content/docs/en/tools/chainhook/index.mdx: - content: c5fc6d932dfee92e6c17ac3b84a7ef07 + content: a3b5d553d27a4b0b5165493834d651dd translations: es: true content/docs/en/apis/chainhook-api/usage.mdx: @@ -1289,15 +1289,15 @@ files: translations: es: true content/docs/en/tools/chainhook/reference/payload-anatomy.mdx: - content: ac17346943a1ea78d977f3dcc9d59e05 + content: 7e9a1ee0962e14246ab73cdcecd04bf3 translations: es: true content/docs/en/tools/chainhook/reference/options.mdx: - content: ed56067c12b0a843e91b7fbf3c9d5ff1 + content: 829b730a8218f88c859d3806cb70e415 translations: es: true content/docs/en/tools/chainhook/reference/filters.mdx: - content: 707b20a16c590f7d82d0146753c42514 + content: 463e7e09482d76d8d8a167356dd596d1 translations: es: true content/docs/en/tools/chainhook/(platform-usage)/view-chainhooks.mdx: @@ -1321,23 +1321,23 @@ files: translations: es: true content/docs/en/tools/chainhook/(overview)/migration.mdx: - content: 8e4474f6ed7b00d94d48d8d41568c6cd + content: 8cfd38ef16dea630e041140d7c6c6b4f translations: es: true content/docs/en/tools/chainhook/(overview)/faq.mdx: - content: e62210687f8a8b470e3db19736fe05f1 + content: 93f6fb71e4887d95524ed029da144726 translations: es: true content/docs/en/tools/chainhook/(chainhook-sdk)/register-enable.mdx: - content: 7ec61d2eb0b5a25314ce81bfd886ba25 + content: b861f167b666720118ac005efe9b6e33 translations: es: true content/docs/en/tools/chainhook/(chainhook-sdk)/manage-keys.mdx: - content: 38b7f6f5fd4a9ac0b134b01c7c02b18f + content: b960ab63639590bdb4540047c2797314 translations: es: true content/docs/en/tools/chainhook/(chainhook-sdk)/list-fetch.mdx: - content: 72572a7c158185675cc64b72570b7168 + content: ff642f5bbe3cc936dec794be10cadbd3 translations: es: true content/docs/en/tools/chainhook/(chainhook-sdk)/introduction.mdx: @@ -1345,11 +1345,11 @@ files: translations: es: true content/docs/en/tools/chainhook/(chainhook-sdk)/evaluate.mdx: - content: 0ae296e25033cbc80fa903c403ad1580 + content: 4d521a416e9d3781e90148842da2d5c8 translations: es: true content/docs/en/tools/chainhook/(chainhook-sdk)/edit-update.mdx: - content: 299dd17ff88a2943f167de34bd758645 + content: 8c334f53e455abe0c5f9e565c01ba8b6 translations: es: true content/docs/en/apis/chainhook-api/reference/info/status.mdx: