{"id":19291751,"url":"https://github.com/hirosystems/token-metadata-api","last_synced_at":"2025-04-22T06:31:43.999Z","repository":{"id":52377536,"uuid":"512028974","full_name":"hirosystems/token-metadata-api","owner":"hirosystems","description":"Stacks Token Metadata API","archived":false,"fork":false,"pushed_at":"2025-04-09T22:58:20.000Z","size":2124,"stargazers_count":9,"open_issues_count":17,"forks_count":2,"subscribers_count":3,"default_branch":"develop","last_synced_at":"2025-04-15T21:43:55.903Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hirosystems.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-07-08T21:20:55.000Z","updated_at":"2025-04-09T22:58:23.000Z","dependencies_parsed_at":"2023-12-27T23:48:11.453Z","dependency_job_id":"2733f948-0135-4677-8c35-ce6912e6895c","html_url":"https://github.com/hirosystems/token-metadata-api","commit_stats":null,"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hirosystems%2Ftoken-metadata-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hirosystems%2Ftoken-metadata-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hirosystems%2Ftoken-metadata-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hirosystems%2Ftoken-metadata-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hirosystems","download_url":"https://codeload.github.com/hirosystems/token-metadata-api/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250183243,"owners_count":21388682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T22:27:45.121Z","updated_at":"2025-04-22T06:31:43.651Z","avatar_url":"https://github.com/hirosystems.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n       /     /   ▶ Token Metadata API   \n      / --- /      Indexes metadata for all Fungible, Non-Fungible, and Semi-Fungible Tokens in the\n     /     /       Stacks blockchain and exposes it via JSON REST API endpoints.\n\n* [Features](#features)\n* [API reference](#api-reference)\n* [Client library](#client-library)\n* [Quick start](#quick-start)\n    * [System requirements](#system-requirements)\n    * [Running the service](#running-the-service)\n        * [Run modes](#run-modes)\n    * [Stopping the service](#stopping-the-service)\n    * [Using an image cache service](#using-an-image-cache-service)\n* [Service architecture](#service-architecture)\n    * [Job Queue](#job-queue)\n        * [Process Smart Contract Job](#process-smart-contract-job)\n        * [Process Token Job](#process-token-job)\n* [Bugs and Feature Requests](#bugs-and-feature-requests)\n* [Contribute](#contribute)\n* [Community](#community)\n\n## Features\n\n* Complete\n  [SIP-016](https://github.com/stacksgov/sips/blob/main/sips/sip-016/sip-016-token-metadata.md)\n  metadata ingestion for\n    * [SIP-009](https://github.com/stacksgov/sips/blob/main/sips/sip-009/sip-009-nft-standard.md)\n      Non-Fungible Tokens\n    * [SIP-010](https://github.com/stacksgov/sips/blob/main/sips/sip-010/sip-010-fungible-token-standard.md)\n      Fungible Tokens\n    * [SIP-013](https://github.com/stacksgov/sips/blob/main/sips/sip-013/sip-013-semi-fungible-token-standard.md)\n      Semi-Fungible Tokens\n* Automatic metadata refreshes via [SIP-019](https://github.com/stacksgov/sips/pull/72)\n  notifications\n* Metadata localization support\n* Metadata fetching via `http:`, `https:`, `data:` URIs. Also supported via customizable gateways:\n    * IPFS\n    * Arweave\n* Easy to use REST JSON endpoints with ETag caching\n* Prometheus metrics for job queue status, contract and token counts, API performance, etc.\n* Image cache/CDN support\n* Run modes for auto-scaling\n\n## API reference\n\nSee the [Token Metadata API Reference](https://docs.hiro.so/metadata/) for more information.\n\n## Client library\n\nA TypeScript client library is available for consuming the API. See [the official\npackage](https://www.npmjs.com/package/@hirosystems/token-metadata-api-client) for more information.\n\n## Quick start\n\n### System requirements\n\nThe Token Metadata Service is a microservice that has hard dependencies on other Stacks blockchain\ncomponents. Before you start, you'll need to have access to:\n\n1. A fully synchronized [Stacks node](https://github.com/stacks-network/stacks-blockchain)\n1. A fully synchronized instance of [Chainhook](https://github.com/hirosystems/chainhook)\n1. A local writeable Postgres database for token metadata storage\n1. (Optional) A Google Cloud Storage bucket for storing token images\n\n### Running the service\n\nClone the repo.\n\nCreate an `.env` file and specify the appropriate values to configure access to the Stacks API\ndatabase, the Token Metadata Service local database, and the Stacks node RPC interface. See\n[`env.ts`](https://github.com/hirosystems/token-metadata-api/blob/develop/src/env.ts) for all\navailable configuration options.\n\nBuild the app (NodeJS v18+ is required)\n```\nnpm install\nnpm run build\n```\n\nStart the service\n```\nnpm run start\n```\n\n#### Run modes\n\nTo better support auto-scaling server configurations, this service supports three run modes\nspecified by the `RUN_MODE` environment variable:\n\n* `default`: Runs all background jobs and the API server. Use this when you're running this service\n  only on one instance. This is the default mode.\n* `readonly`: Runs only the API server. Use this in an auto-scaled cluster when you have multiple\n  `readonly` instances and just one `writeonly` instance. This mode needs a `writeonly` instance to\n  continue populating the token metadata DB.\n* `writeonly`: Use one of these in an auto-scaled environment so you can continue consuming new\n  token contracts. Use in conjunction with multiple `readonly` instances as explained above.\n\n### Stopping the service\n\nWhen shutting down, you should always prefer to send the `SIGINT` signal instead of `SIGKILL` so\nthe service has time to finish any pending background work and all dependencies are gracefully\ndisconnected.\n\n### Using an image cache service\n\nThe Token Metadata API allows you to specify a Google Cloud Storage bucket to store the token images\nscanned by the metadata processor. This is recommended if you're looking to optimize your image speeds\nwhen using the API via a wallet, etc. See the `env.ts` file for more information on which environment\nvariables you need to provide.\n\n## Service architecture\n\n### Job Queue\n\nThe role of the\n[`JobQueue`](https://github.com/hirosystems/token-metadata-api/blob/develop/src/token-processor/queue/job-queue.ts)\nis to perform all the smart contract and token processing in the service.\n\nIt is a priority queue that organizes all necessary work for contract ingestion and token metadata\nprocessing. Every job processed by this queue corresponds to one row in the `jobs` DB table, which\nmarks its processing status and related objects to be worked on (smart contract or token).\n\nThis object essentially runs an infinite loop that follows these steps:\n1. Upon `start()`, it fetches a set number of job rows that are `'pending'` and loads their\n   corresponding `Job` objects into memory for processing, marking those rows now as `'queued'`.\n2. It executes each loaded job to completion concurrently. Depending on success or failure, the job\n   row is marked as either `'done'` or `'failed'`.\n3. Once all loaded jobs are done (and the queue is now empty), it goes back to step 1.\n\nThere are two env vars that can help you tune how the queue performs:\n* `ENV.JOB_QUEUE_SIZE_LIMIT`: The in-memory size of the queue, i.e. the number of pending jobs that\n   are loaded from the database while they wait for execution (see step 1 above).\n* `ENV.JOB_QUEUE_CONCURRENCY_LIMIT`: The number of jobs that will be ran simultaneously.\n\nThis queue runs continuously and can handle an unlimited number of jobs.\n\n#### Process Smart Contract Job\n\nThis job makes a contract call to the Stacks node in order to determine the total number of tokens\ndeclared by the given contract. Once determined, it creates and enqueues all of these tokens for\nmetadata ingestion.\n\n#### Process Token Job\n\nThis job fetches the metadata JSON object for a single token as well as other relevant properties\ndepending on the token type (symbol, decimals, etc.). Once fetched, it parses and ingests this data\nto save it into the local database for API endpoints to return.\n\nIf a `429` (Too Many Requests) status code is returned by a hostname used to fetch metadata, the\nservice will cease all further requests to it until a reasonable amount of time has passed or until\nthe time specified by the host in a `Retry-After` response header.\n\n## Bugs and feature requests\n\nIf you encounter a bug or have a feature request, we encourage you to follow the steps below:\n\n 1. **Search for existing issues:** Before submitting a new issue, please search [existing and closed issues](../../issues) to check if a similar problem or feature request has already been reported.\n 1. **Open a new issue:** If it hasn't been addressed, please [open a new issue](../../issues/new/choose). Choose the appropriate issue template and provide as much detail as possible, including steps to reproduce the bug or a clear description of the requested feature.\n 1. **Evaluation SLA:** Our team reads and evaluates all the issues and pull requests. We are avaliable Monday to Friday and we make a best effort to respond within 7 business days.\n\nPlease **do not** use the issue tracker for personal support requests or to ask for the status of a transaction. You'll find help at the [#support Discord channel](https://discord.gg/SK3DxdsP).\n\n\n## Contribute\n\nDevelopment of this product happens in the open on GitHub, and we are grateful to the community for contributing bugfixes and improvements. Read below to learn how you can take part in improving the product.\n\n### Code of Conduct\nPlease read our [Code of conduct](../../../.github/blob/main/CODE_OF_CONDUCT.md) since we expect project participants to adhere to it. \n\n### Contributing Guide\nRead our [contributing guide](.github/CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes.\n\n## Community\n\nJoin our community and stay connected with the latest updates and discussions:\n\n- [Join our Discord community chat](https://discord.gg/ZQR6cyZC) to engage with other users, ask questions, and participate in discussions.\n\n- [Visit hiro.so](https://www.hiro.so/) for updates and subcribing to the mailing list.\n\n- Follow [Hiro on Twitter.](https://twitter.com/hirosystems)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhirosystems%2Ftoken-metadata-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhirosystems%2Ftoken-metadata-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhirosystems%2Ftoken-metadata-api/lists"}