Project structure

The structure of the DipDup project package is the following:

├── graphql
├── handlers
│   ├──
│   ├──
│   └──
├── hooks
│   ├──
│   ├──
│   ├──
│   ├──
│   └──
├── sql
│   ├── on_reindex
│   ├── on_restart
│   ├── on_index_rollback
│   └── on_synchronized
└── types
    └── tzbtc
        ├── parameter
        │   ├──
        │   ├──
        │   └──
graphqlGraphQL queries for Hasura (*.graphql)
handlersUser-defined callbacks to process matched operations and big map diffs
hooksUser-defined callbacks to run manually or by schedule
models.pyTortoise ORM models
sqlSQL scripts to run from callbacks (*.sql)
typesCodegened Pydantic typeclasses for contract storage/parameter

DipDup will generate all the necessary directories and files inside the project's root on init command. These include contract type definitions and callback stubs to be implemented by the developer.

Type classes

DipDup receives all smart contract data (transaction parameters, resulting storage, big_map updates) in normalized form (read more about how TzKT handles Michelson expressions) but still as raw JSON. DipDup uses contract type information to generate data classes, which allow developers to work with strictly typed data.

DipDup generates Pydantic models out of JSONSchema. You might want to install additional plugins (PyCharm, mypy) for convenient work with this library.

The following models are created at init:

  • operation indexes: storage type for all contracts met in handler patterns plus parameter type for all destination+entrypoint pairs.
  • big_map indexes: key and storage types for all big map paths in handler configs.

Nested packages

Callback modules don't have to be in top-level hooks/handlers directories. Add one or multiple dots to the callback name to define nested packages:

package: indexer

After running the init command, you'll get the following directory tree (shortened for readability):

├── hooks
│   ├── foo
│   │   ├──
│   │   └──
│   └──
└── sql
    └── foo
        └── bar
            └── .keep

The same rules apply to handler callbacks. Note that the callback field must be a valid Python package name - lowercase letters, underscores, and dots.