Each DipDup project consists of a YAML config and a Python package of a specific structure. It could be placed anywhere, but needs to be importable. The package name is defined in the config file.
To generate all necessary directories and files according to config run the
init command. You should run it every time you significantly change the config file.
The structure of the resulting package is the following:
|📁 ||Contract ABIs used to generate typeclasses|
|📁 ||Environment-specific configs to merge with the root one|
|📁 ||Dockerfiles, compose files, and default env variables for each environment|
|📁 ||Custom GraphQL queries to expose with Hasura engine|
|📁 ||User-defined callbacks to process contract data|
|📁 ||Arbitrary Hasura metadata to apply during configuration|
|📁 ||User-defined callbacks to run manually or by schedule|
|📁 ||DipDup ORM models to store data in the database|
|📁 ||SQL scripts and queries to run manually or on specific events|
|📁 ||Automatically generated Pydantic dataclasses for contract types|
|Root DipDup config; can be expanded with env-specific files|
|A little helper, symlink to let you import the package from the root directory.|
|Python package metadata (introduced in PEP 518; see details)|
There are also preconfigured .dockerignore and .gitignore files; py.typed marker. Usually, you won't need to modify them.
DipDup uses contract type information to generate Pydantic models to work with strictly typed data. We call these models typeclasses. Modules in the
types directory are generated automatically from contract ABIs and JSONSchemas during init. You can modify them manually, but usually won't need to. Under the hood, the process is roughly the following:
- Contract ABIs are fetched from public sources or provided by the user.
- DipDup converts these ABIs to intermediate JSONSchemas.
- JSONSchemas converted to Pydantic models with datamodel-code-generator.
This approach allows working with complex contract types with nested structures and polymorphic variants.
Callbacks can be grouped into packages to organize the project structure. Add one or multiple dots to the callback name to define nested packages:
package: dipdup_indexer hooks: backup.restore: callback: backup.on_restore
After running the
init command, you'll get the following directory tree (shortened for brevity)
dipdup_indexer ├── hooks │ ├── backup │ │ └── on_restore.py └── sql └── backup └── on_restore
Handler callbacks can be grouped the same way. Note, that the callback name still needs to be a valid Python module path: only lowercase letters, underscores, and dots.