A comprehensive indexing service for the Studio Blockchain that provides API endpoints for blockchain explorers, wallets, and dApps.
The Studio Blockchain Indexer continuously processes blocks and transactions from the Studio Blockchain, storing them in a PostgreSQL database for efficient querying. It provides a rich set of REST API endpoints that enable developers to access blockchain data, token information, NFT metadata, and smart contract details.
- Real-time Blockchain Indexing: Continuously indexes blocks, transactions, and events
- Token Support: Tracks ERC-20, ERC-721, and ERC-1155 tokens
- NFT Metadata: Retrieves and stores NFT metadata and ownership information
- Smart Contract Verification: Verifies and stores smart contract source code and ABIs
- Transaction Decoding: Decodes transaction input data for verified contracts
- Comprehensive API: Provides REST endpoints for all indexed data
- Monitoring: Includes enhanced monitoring for system health and security
- Docker and Docker Compose (for Docker deployment)
- Node.js 20.x and PostgreSQL 15.x (for manual deployment)
- Git
-
Clone the repository:
git clone https://github.com/StudioPlatforms/studio-blockchain-indexer.git cd studio-blockchain-indexer
-
Configure environment variables (optional):
- The default configuration in
docker-compose.yml
works out of the box - Modify environment variables in
docker-compose.yml
if needed
- The default configuration in
-
Start the services:
docker-compose up -d
-
Monitor the logs:
docker-compose logs -f
-
Clone the repository:
git clone https://github.com/StudioPlatforms/studio-blockchain-indexer.git cd studio-blockchain-indexer
-
Install dependencies:
npm install
-
Create a PostgreSQL database:
createdb studio_indexer
-
Create a
.env
file based on.env.example
:cp .env.example .env # Edit .env with your configuration
-
Build the application:
npm run build
-
Start the indexer:
npm start
The indexer provides a comprehensive set of API endpoints for accessing blockchain data. All endpoints are relative to the base URL (default: http://localhost:3000
).
GET /health
- Check the health status of the indexer
GET /blocks
- Get latest blocksGET /blocks/:number
- Get block by numberGET /blocks/hash/:hash
- Get block by hash
GET /transactions
- Get latest transactionsGET /transactions/:hash
- Get transaction by hashGET /transactions/:hash/receipt
- Get transaction receiptGET /transactions/:hash/decoded
- Get decoded transaction dataGET /transactions/pending
- Get pending transactionsGET /address/:address/transactions
- Get transactions by address
GET /search?q=<query>
- Search for blocks, transactions, or addresses
GET /account/:address/balances
- Get native and token balances for an accountGET /address/:address/tokens
- Get tokens owned by an addressGET /address/:address/token-transfers
- Get token transfers for an address
GET /tokens/:tokenAddress
- Get token informationGET /tokens/:tokenAddress/holders
- Get token holdersGET /tokens/:tokenAddress/transfers
- Get token transfers
GET /address/:address/nfts
- Get NFTs owned by an addressGET /nfts/:tokenAddress/:tokenId
- Get NFT metadataGET /address/:address/nft-transfers
- Get NFT transfers for an addressGET /nfts/:tokenAddress
- Get NFT collection informationGET /nfts
- Get list of NFT collections
POST /contracts/verify
- Verify a single-file contractPOST /contracts/verify-multi
- Verify a multi-file contractGET /contracts/:address/verified
- Check if a contract is verifiedGET /contracts/:address/abi
- Get contract ABIGET /contracts/:address/source
- Get contract source codeGET /contracts/:address/verification
- Get contract verification detailsPOST /contracts/:address/interact
- Interact with a contractGET /contracts/verified
- Get list of verified contracts
GET /stats/tps
- Get transactions per secondGET /stats/holders
- Get total STO holdersGET /stats/validators/payout
- Get validators payoutGET /stats/contracts/count
- Get total contracts countGET /stats/contracts/erc20/count
- Get ERC20 contracts countGET /stats/contracts/nft/count
- Get NFT contracts count
The Studio Blockchain Indexer includes a powerful contract verification system that supports both single-file and multi-file contracts. This system allows developers to verify their smart contracts and make them available for exploration through the API.
- Bytecode Comparison: The system compiles the provided source code and compares the resulting bytecode with the on-chain bytecode.
- Metadata Extraction: The system extracts metadata from the bytecode to provide additional information about the contract.
- Storage: Upon successful verification, the source code, ABI, and other metadata are stored in the database.
To verify a single-file contract, use the /contracts/verify
endpoint:
// Example: Verifying a single-file contract
const response = await fetch('http://localhost:3000/contracts/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
address: '0x1234567890123456789012345678901234567890',
sourceCode: 'pragma solidity ^0.8.0; contract MyContract { ... }',
compilerVersion: '0.8.0',
contractName: 'MyContract',
optimizationUsed: true,
runs: 200,
constructorArguments: '', // Optional: hex-encoded constructor arguments without 0x prefix
libraries: {}, // Optional: mapping of library names to addresses
evmVersion: 'cancun' // Optional: EVM version (default: 'cancun')
})
});
const result = await response.json();
For contracts that span multiple files or use imports, use the /contracts/verify-multi
endpoint:
// Example: Verifying a multi-file contract
const response = await fetch('http://localhost:3000/contracts/verify-multi', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
address: '0x1234567890123456789012345678901234567890',
sourceFiles: {
'MyContract.sol': 'pragma solidity ^0.8.0; import "./Interfaces.sol"; contract MyContract { ... }',
'Interfaces.sol': 'pragma solidity ^0.8.0; interface IMyInterface { ... }',
'Libraries/Math.sol': 'pragma solidity ^0.8.0; library Math { ... }'
},
compilerVersion: '0.8.0',
contractName: 'MyContract',
optimizationUsed: true,
runs: 200,
constructorArguments: '', // Optional: hex-encoded constructor arguments without 0x prefix
libraries: {
'Math': '0x1234567890123456789012345678901234567890'
}, // Optional: mapping of library names to addresses
evmVersion: 'cancun' // Optional: EVM version (default: 'cancun')
})
});
const result = await response.json();
The verification system supports several ways to handle imports:
- Multi-File Verification: Include all imported files in the
sourceFiles
object. - Import Mappings: For standard libraries, you can provide import mappings:
// Example: Using import mappings
const response = await fetch('http://localhost:3000/contracts/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
// ... other verification parameters
importMappings: {
'@openzeppelin/contracts/': 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.4.0/',
'@uniswap/v3-core/': 'https://github.com/Uniswap/v3-core/blob/main/'
}
})
});
A successful verification response includes:
{
"success": true,
"message": "Contract verified successfully",
"address": "0x1234567890123456789012345678901234567890",
"abi": [...],
"metadata": "...",
"metadataHash": "0xa165627a7a72305820..."
}
For multi-file contracts, the response also includes the main file:
{
"success": true,
"message": "Contract verified successfully",
"address": "0x1234567890123456789012345678901234567890",
"abi": [...],
"metadata": "...",
"metadataHash": "0xa165627a7a72305820...",
"mainFile": "MyContract.sol"
}
Common verification errors include:
- Compiler Version Mismatch: The compiler version used for verification doesn't match the one used for deployment.
- Optimization Settings Mismatch: The optimization settings (enabled/disabled, runs) don't match.
- Missing Imports: Not all imported files are included in the
sourceFiles
object. - Constructor Arguments Mismatch: The provided constructor arguments don't match the ones used during deployment.
- Library Addresses Mismatch: The provided library addresses don't match the ones used during deployment.
To get verification details for a contract:
// Example: Getting verification details
const response = await fetch('http://localhost:3000/contracts/0x1234567890123456789012345678901234567890/verification');
const verificationDetails = await response.json();
The response includes:
{
"address": "0x1234567890123456789012345678901234567890",
"contractName": "MyContract",
"compilerVersion": "0.8.0",
"license": "MIT",
"optimizationUsed": true,
"runs": 200,
"evmVersion": "cancun",
"constructorArguments": "",
"libraries": {},
"verifiedAt": "2025-04-22T14:30:00Z",
"metadataHash": "0xa165627a7a72305820...",
"isMultiFile": true,
"sourceFiles": {
"MyContract.sol": "...",
"Interfaces.sol": "..."
},
"ownerAddress": "0x846c234adc6d8e74353c0c355b0c2b6a1e46634f",
"creatorAddress": "0x846c234adc6d8e74353c0c355b0c2b6a1e46634f",
"creationInfo": {
"creator": "0x846c234adc6d8e74353c0c355b0c2b6a1e46634f",
"blockNumber": "22166",
"timestamp": 1742059631,
"transactionHash": "0x3531c58c08bc1b4f0d94bf6ae2942f459f8918e1ccfd298b5d5f59b387ec913f"
}
}
The following environment variables can be configured:
DB_HOST
- PostgreSQL host (default:localhost
)DB_PORT
- PostgreSQL port (default:5432
)DB_NAME
- PostgreSQL database name (default:studio_indexer
)DB_USER
- PostgreSQL username (default:postgres
)DB_PASSWORD
- PostgreSQL password (default:postgres
)
RPC_URL
- Studio Blockchain RPC URL (default:https://mainnet.studio-blockchain.com
)CHAIN_ID
- Studio Blockchain chain ID (default:240241
)
PORT
- API server port (default:3000
)HOST
- API server host (default:0.0.0.0
)
START_BLOCK
- Block to start indexing from (default:0
)BATCH_SIZE
- Number of blocks to process in a batch (default:10
)CONFIRMATIONS
- Number of confirmations to wait before processing a block (default:12
)
LOG_LEVEL
- Logging level (default:info
)
src/
- Source codeservices/
- Core servicesapi/
- API endpointsblockchain/
- Blockchain interactiondatabase/
- Database operationsindexer.ts
- Indexer serviceverification/
- Contract verification
types/
- TypeScript type definitionsutils/
- Utility functionsconfig.ts
- Configurationindex.ts
- Entry point
migrations/
- Database migrationsscripts/
- Utility scriptsdocker-compose.yml
- Docker Compose configurationDockerfile
- Docker build configuration
npm run dev
This will start the indexer in development mode with hot reloading.
If you need to reset the indexer and start from scratch:
# For Docker deployment
docker-compose down -v
docker-compose up -d
# For manual deployment
npm run migrate down
npm run migrate up
The indexer includes an enhanced monitoring service that checks the health of the system and provides alerts for any issues. You can view the logs at:
# For Docker deployment
docker-compose logs -f enhanced-monitor
# For manual deployment
tail -f logs/enhanced-monitor.log
The Studio Blockchain Indexer provides a solid foundation for building blockchain explorers, wallets, and dApps. Here are some ways to build on top of it:
You can build a frontend application that consumes the API endpoints provided by the indexer. Here's an example of fetching the latest blocks:
// Example: Fetching the latest blocks
async function getLatestBlocks() {
const response = await fetch('http://localhost:3000/blocks?limit=10');
const blocks = await response.json();
// Process blocks
blocks.forEach(block => {
console.log(`Block ${block.number}: ${block.hash}`);
console.log(`Transactions: ${block.transactions.length}`);
console.log(`Timestamp: ${new Date(block.timestamp * 1000).toISOString()}`);
});
}
For verified contracts, you can decode transaction input data:
// Example: Decoding transaction input data
async function decodeTransaction(txHash) {
const response = await fetch(`http://localhost:3000/transactions/${txHash}/decoded`);
const decodedTx = await response.json();
if (decodedTx.decoded) {
console.log(`Function: ${decodedTx.decoded.functionName}`);
console.log(`Signature: ${decodedTx.decoded.functionSignature}`);
console.log('Parameters:');
decodedTx.decoded.params.forEach(param => {
console.log(` ${param.name} (${param.type}): ${param.value}`);
});
} else {
console.log('Contract is not verified or transaction is not a contract interaction');
}
}
Track token balances for an address:
// Example: Tracking token balances
async function getTokenBalances(address) {
const response = await fetch(`http://localhost:3000/account/${address}/balances`);
const balances = await response.json();
console.log(`Native Balance: ${balances.native} STO`);
console.log('Token Balances:');
balances.tokens.forEach(token => {
console.log(` ${token.name} (${token.symbol}): ${token.balance}`);
});
}
Track NFT ownership for an address:
// Example: Tracking NFT ownership
async function getNFTs(address) {
const response = await fetch(`http://localhost:3000/address/${address}/nfts`);
const nfts = await response.json();
console.log(`NFTs owned by ${address}:`);
nfts.forEach(nft => {
console.log(` Collection: ${nft.collectionName} (${nft.collectionAddress})`);
console.log(` Token ID: ${nft.tokenId}`);
console.log(` Name: ${nft.metadata.name}`);
console.log(` Image: ${nft.metadata.image}`);
});
}
If you need to index additional data or create custom endpoints, you can extend the indexer by adding new services and API endpoints. The modular architecture makes it easy to add new functionality.
You can implement webhooks and notifications by subscribing to specific events or addresses and triggering actions when new data is indexed.
This project is licensed under the MIT License - see the LICENSE file for details. Contact: [email protected] Lead developer: [email protected]