EOSIO Contract API
The aim of this project is to provide an framework to fill and query state and history for specific contracts on eosio based blockchains.
This project uses the eosio State History Plugin as data source and PostgreSQL to store / query the data. Per block transactions guarantee that the database is consistent at any time.
Requirements
- NodeJS >= 16.0
- PostgreSQL >= 13.0
- Redis >= 5.0
- Nodeos >= 1.8.0 (only tested with 2.0) The state history plugin needs to be enabled and the options:
trace-history = true
,chain-state-history = true
Suggestions
- Hasura GraphQL Engine >= 1.3 (if you want to allow GraphQL queries)
- PGAdmin 4 (Interface to manage the postgres database)
Configuration
The config folder contains 3 different configuration files
connections.config.json
This file contains Postgres / Redis / Nodeos connection data for the used chain.
Notes
- Redis: Can be used for multiple chains without further action
- PostgreSQL: Each chain needs it own postgres database (can use the same postgres instance), but multiple readers of the same chain can use the same database if they are non conflicting
- Nodeos: nodeos should habe a full state history for the range you are trying to index
{
"postgres": {
"host": "127.0.0.1",
"port": 5432,
"user": "username",
"password": "changeme",
"database": "api-wax-mainnet-atomic-1"
},
"redis": {
"host": "127.0.0.1",
"port": 6379
},
"chain": {
"name": "wax-mainnet",
"chain_id": "1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4",
"http": "http://127.0.0.1:8888",
"ship": "ws://127.0.0.1:8080"
}
}
readers.config.json
This file is used to configure the filler
[
// Multiple Readers can be defined and each one will run in a seperated thread
{
"name": "atomic-1", // Name of the reader. Should be unique per chain and should not change after it was started
"start_block": 0, // start at a specific block. If ready was already started, this can only be higher than the last indexed block
"stop_block": 0, // stop at a specific block
"irreversible_only": false, // If you need data for a lot of contracts and do not need live data, this option is faster
"ship_prefetch_blocks": 50, // How much unconfirmed blocks ship will send
"ship_min_block_confirmation": 30, // After how much blocks the reader will confirm the blocks
"ship_ds_queue_size": 20, // how much blocks the reader should preserialize the action / table data
"ds_ship_threads": 4, // How much threads should be used to deserialize traces and table deltas
"db_group_blocks": 10, // In catchup mode, the reader will group this amount of bl
"contracts": [
// AtomicAssets handler which provides data for the AtomicAssets NFT standard
{
"handler": "atomicassets",
"args": {
"atomicassets_account": "atomicassets", // Account where the contract is deployed
"store_logs": true, // store logs
"store_transfers": true // store the transfer history
}
}
]
}
]
server.config.json
{
"provider_name": "pink.network", // Provider which is show in the endpoint documentation
"provider_url": "https://pink.network",
"server_addr": "0.0.0.0", // Server address to bind to
"server_name": "wax.api.atomicassets.io", // Server name which is shown in the documentation
"server_port": 9000, // Server Port
"cache_life": 2, // GET endpoints are cached for this amount of time (in seconds)
"trust_proxy": true, // Enable if you use a reverse proxy to have correct rate limiting by ip
"rate_limit": {
"interval": 60, // Interval to reset the counter (in seconds)
"requests": 240 // How much requests can be made in the defined interval
},
"ip_whitelist": [], // These IPs are not rate limited or receive cached requests
"slow_query_threshold": 7500, // If specific queries take longer than this threshold a warning is created
"max_query_time_ms": 10000, // max execution time for a database query
"max_db_connections": 50, // max number of concurrent db connections / db queries
"namespaces": [
// atomicassets namespace which provides an API for basic functionalities
{
"name": "atomicassets",
"path": "/atomicassets", // Each API endpoint will start with this path
"args": {
"atomicassets_account": "atomicassets" // Account where the contract is deployed
}
}
]
}
Installation
This project consists of two separated processes which need to be started and stopped independently:
- The API which will provide the socket and REST endpoints (or whatever is used)
- The filler which will read the data from the blockchain and fills the database
The filler needs to be started before the API when running it for the first time:
Prerequisites:
-
PostgreSQL
- Create a database and user which is allowed to read and write on that db
-
EOSIO node
- State History Plugin enabled with options
trace-history = true
,chain-state-history = true
- Fully synced for the block range you want to process
- Open socket and http api
- State History Plugin enabled with options
-
Copy and modify example configs with the correct connection params
There are two suggested ways to run the project: Docker if you want to containerize the application or PM2 if you want to run it on system level
Docker
git clone && cd eosio-contract-api
- There is an example docker compose file provided
docker-compose up -d
Start
docker-compose start eosio-contract-api-filler
docker-compose start eosio-contract-api-server
Stop
docker-compose stop eosio-contract-api-filler
docker-compose stop eosio-contract-api-server
PM2
git clone && cd eosio-contract-api
yarn install
yarn global add pm2
Start
pm2 start ecosystems.config.json --only eosio-contract-api-filler
pm2 start ecosystems.config.json --only eosio-contract-api-server
Stop
pm2 stop eosio-contract-api-filler
pm2 stop eosio-contract-api-server
Currently Supported Contracts
Readers (used to fill the database)
Readers are used to fill the database for a specific contract.
atomicassets
{
"handler": "atomicassets",
"args": {
"atomicassets_account": "atomicassets" // account where the atomicassets contract is deployed
"store_transfers": true // store the transfer history
"store_logs": true // store data structure logs
}
}
atomicmarket
This reader requires a atomicassets and a delphioracle reader with the same contract as specified here
{
"handler": "atomicmarket",
"args": {
"atomicassets_account": "atomicassets" // account where the atomicassets contract is deployed
"atomicmarket_account": "atomicmarket" // account where the atomicmarket contract is deployed
"store_logs": true // Store logs of sales / auctions
}
}
delphioracle
{
"handler": "delphioracle",
"args": {
"delphioracle_account": "delphioracle" // account where the delphioracle contract is deployed
}
}
Namespace (API endpoints)
A namespace provides an API for a specific contract or use case and is based on data a reader provides
atomicassets
{
"handler": "atomicassets",
"args": {
"atomicassets_account": "atomicassets" // account where the atomicassets contract is deployed
"connected_reader": "atomic-1" // reader to which the API connects for live data
}
}
atomicmarket
{
"handler": "atomicmarket",
"args": {
"atomicmarket_account": "atomicmarket", // account where the atomicmarket contract is deployed
"connected_reader": "atomic-1" // reader to which the API connects for live data
}
}