Open Protocol Indexer, OPI, is the best-in-slot open-source indexing client for meta-protocols on Bitcoin. OPI uses a fork of ord 0.23.2 with minimal changes to maintain compatibility with base layer rules. Also, OPI is built with modularity in mind. All modules in OPI have been built with reorg protection.
Currently OPI has modules for BRC-20, Bitmap and SNS, we'll add new modules over time. Pull Requests are welcomed for other meta-protocols.
OPI-ord sits in the core of OPI. It indexes all json/text inscriptions and their first 2 transfers.
Transfer limit can be changed via INDEX_TX_LIMIT
variable in ord fork. This limit has been added since there are some UTXO's with a lot of inscription content and their movement floods transfers tables. Also, base indexing of most protocols only needs the first two transfers. BRC-20 becomes invalid after 2 hops, bitmap and SNS validity is calculated at inscription time.
BRC-20 Indexer is the first module of OPI. It follows the official protocol rules hosted here. BRC-20 Indexer saves all historical balance changes and all BRC-20 events.
In addition to indexing all events, it also calculates a block hash and cumulative hash of all events for easier db comparison. Here's the pseudocode for hash calculation:
## Calculation starts at block 767430 which is the first inscription block
EVENT_SEPARATOR = '|'
## max_supply, limit_per_mint, amount decimal count is the same as ticker's decimals (no trailing dot if decimals is 0)
## ticker_lowercase = lower(ticker)
## ticker_original is the ticker on inscription
for event in block_events:
if event is 'predeploy-inscribe':
block_str += 'predeploy-inscribe;<inscr_id>;<predeployer_pkscript>;<hash>;<block_height>' + EVENT_SEPARATOR
if event is 'deploy-inscribe':
block_str += 'deploy-inscribe;<inscr_id>;<deployer_pkscript>;<ticker_lowercase>;<ticker_original>;<max_supply>;<decimals>;<limit_per_mint>;<is_self_mint("true" or "false")>' + EVENT_SEPARATOR
if event is 'mint-inscribe':
block_str += 'mint-inscribe;<inscr_id>;<minter_pkscript>;<ticker_lowercase>;<ticker_original>;<amount>;<parent_id("" if null)>' + EVENT_SEPARATOR
if event is 'transfer-inscribe':
block_str += 'transfer-inscribe;<inscr_id>;<source_pkscript>;<ticker_lowercase>;<ticker_original>;<amount>' + EVENT_SEPARATOR
if event is 'transfer-transfer':
## if sent as fee, sent_pkscript is empty
block_str += 'transfer-transfer;<inscr_id>;<source_pkscript>;<sent_pkscript>;<ticker_lowercase>;<ticker_original>;<amount>' + EVENT_SEPARATOR
if event is 'brc20prog-deploy-inscribe':
block_str += 'brc20prog-deploy-inscribe;<inscr_id>;<source_pkscript>;<data>;<base64_data>' + EVENT_SEPARATOR
if event is 'brc20prog-deploy-transfer':
block_str += 'brc20prog-deploy-transfer;<inscr_id>;<source_pkscript>;<spent_pkscript>;<data>;<base64_data>;<byte_len>' + EVENT_SEPARATOR
if event is 'brc20prog-call-inscribe':
block_str += '<inscr_id>;<source_pkscript>;<contract_address>;<contract_inscription_id>;<data>;<base64_data>' + EVENT_SEPARATOR
if event is 'brc20prog-call-transfer':
block_str += '<inscr_id>;<source_pkscript>;<spent_pkscript>;<contract_address>;<contract_inscription_id>;<data>;<base64_data>' + EVENT_SEPARATOR
if event is 'brc20prog-transact-inscribe':
block_str += '<inscr_id>;<source_pkscript>;<data>;<base64_data>' + EVENT_SEPARATOR
if event is 'brc20prog-transact-transfer':
block_str += '<inscr_id>;<source_pkscript>;<spent_pkscript>;<data>;<base64_data>;<byte_len>' + EVENT_SEPARATOR
if event is 'brc20prog-withdraw-inscribe':
block_str += '<inscr_id>;<source_pkscript>;<ticker_lowercase>;<ticker_original>;<amount>' + EVENT_SEPARATOR
if event is 'brc20prog-withdraw-transfer':
block_str += '<inscr_id>;<source_pkscript>;<spent_pkscript>;<ticker_lowercase>;<ticker_original>;<amount>' + EVENT_SEPARATOR
if block_str.last is EVENT_SEPARATOR: block_str.remove_last()
block_hash = sha256_hex(block_str)
## for first block last_cumulative_hash is empty
cumulative_hash = sha256_hex(last_cumulative_hash + block_hash)
There is an optional block event hash reporting system pointed at https://api.opi.network/report_block. If you want to exclude your node from this, just change REPORT_TO_INDEXER
variable in brc20_index/.env
.
Also change REPORT_NAME
to differentiate your node from others.
BRC-20 API exposes activity on block (block events), balance of a wallet at the start of a given height, current balance of a wallet, block hash and cumulative hash at a given block and hash of all current balances.
Bitmap Indexer is the second module of OPI. It follows the official protocol rules hosted here. Bitmap Indexer saves all bitmap-number inscription-id pairs.
In addition to indexing all pairs, it also calculates a block hash and cumulative hash of all events for easier db comparison. Here's the pseudocode for hash calculation:
## Calculation starts at block 767430 which is the first inscription block
EVENT_SEPARATOR = '|'
for bitmap in new_bitmaps_in_block:
block_str += 'inscribe;<inscr_id>;<bitmap_number>' + EVENT_SEPARATOR
if block_str.last is EVENT_SEPARATOR: block_str.remove_last()
block_hash = sha256_hex(block_str)
## for first block last_cumulative_hash is empty
cumulative_hash = sha256_hex(last_cumulative_hash + block_hash)
Bitmap API exposes block hash and cumulative hash at a given block, hash of all bitmaps and inscription_id of a given bitmap.
SNS Indexer is the third module of OPI. It follows the official protocol rules hosted here. SNS Indexer saves all name, domain, inscription-id and namespace, inscription-id tuples.
In addition to indexing all tuples, it also calculates a block hash and cumulative hash of all events for easier db comparison. Here's the pseudocode for hash calculation:
## Calculation starts at block 767430 which is the first inscription block
EVENT_SEPARATOR = '|'
for event in new_events_in_block:
if event is 'name-registration':
## name is the full name, domain is the part afler dot
block_str += 'register;<inscr_id>;<name>;<domain>' + EVENT_SEPARATOR
elif event is 'namespace-registration':
block_str += 'ns_register;<inscr_id>;<namespace>' + EVENT_SEPARATOR
if block_str.last is EVENT_SEPARATOR: block_str.remove_last()
block_hash = sha256_hex(block_str)
## for first block last_cumulative_hash is empty
cumulative_hash = sha256_hex(last_cumulative_hash + block_hash)
SNS API exposes block hash and cumulative hash at a given block, hash of all registered names, id number and domain of a given name, id number and name tuples of a domain, and all registered namespaces endpoints.
For detailed installation guides:
- Ubuntu: installation guide
Modules use PostgreSQL as DB. Before running the indexer, setup a PostgreSQL DB (all modules can write into different databases as well as use a single database).
Build ord:
cd ord; cargo build --release;
Install node modules
cd modules/brc20_api; npm install;
cd ../bitmap_api; npm install;
Create a virtual environment and install python libraries
cd modules;
python3 -m venv .venv;
source .venv/bin/activate;
pip3 install -r requirements.txt;
Setup .env files and DBs
Run reset_init.py
in each module folder to initialise .env file, databases and set other necessary files.
First, run ordinals indexer to fill the inscription database:
Main Meta-Protocol Indexer / Ord and DB Server
cd ord/target/release;
./ord --data-dir . index run
Note
For ord to reach the bitcoin rpc server correctly, pass --bitcoin-rpc-url
, --bitcoin-rpc-username
and --bitcoin-rpc-password
parameters before index run
. To run on signet, add --signet
as well.
BRC-20 Indexer
If BRC20 Programmable Module is supported, set up and run brc20_prog server using the instructions at bestinslot-xyz/brc20-programmable-module#usage before running BRC-20 indexer.
cd modules/brc20_index;
cargo build --release;
./target/release/brc20-index;
BRC-20 API
cd modules/brc20_api;
node api.js;
Bitmap Indexer
cd modules/bitmap_index;
python3 bitmap_index.py;
Bitmap API
cd modules/bitmap_api;
node api.js;
SNS Indexer
cd modules/sns_index;
python3 sns_index.py;
SNS API
cd modules/sns_api;
node api.js;
- Stop all indexers and apis (preferably starting from main indexer but actually the order shouldn't matter)
- Update the repo (
git pull
) - Recompile ord (
cd ord; cargo build --release;
) - Re-run all indexers and apis