Skip to content

Commit 3f29ede

Browse files
Merge pull request #2 from balancer/@gustavo/feat/liquidity
add files to connect already existing labels to dbt
2 parents 7bae66b + c3987f7 commit 3f29ede

200 files changed

Lines changed: 18855 additions & 20 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
---
2+
name: dune-dbt-integration
3+
description: Troubleshoot and configure Dune + dbt integration. Use when setting up profiles.yml, fixing 401/access denied errors, running dbt against Dune, or querying dbt models in the Dune UI.
4+
alwaysApply: false
5+
---
6+
7+
# Dune dbt Integration
8+
9+
Key learnings for integrating dbt with Dune's Trino connector.
10+
11+
## profiles.yml — Authentication
12+
13+
**Dune uses LDAP, not JWT.** The official template differs from some docs:
14+
15+
```yaml
16+
# ✅ Correct (from dune-dbt-template)
17+
method: ldap
18+
user: dune # Fixed — do not change
19+
password: "{{ env_var('DUNE_API_KEY') }}"
20+
catalog: dune # Fixed — do not change
21+
host: trino.api.dune.com
22+
port: 443
23+
http_scheme: https
24+
cert: true
25+
session_properties:
26+
transformations: true
27+
```
28+
29+
**Do not use:** `method: jwt`, `jwt_token`, or `user: "{{ env_var('DUNE_TEAM_NAME') }}"`.
30+
31+
## Environment Variables
32+
33+
- **dbt does not auto-load `.env`** — run `source .env` before `uv run dbt run`.
34+
- Required: `DUNE_API_KEY`, `DUNE_TEAM_NAME`.
35+
- Optional: `DEV_SCHEMA_SUFFIX` for personal dev schemas.
36+
- Optional: `DUNE_SKIP_VIEW_PROPERTIES=true` — skips `hide_spells()` and `expose_spells()` post-hooks in prod when API key lacks `alter_view_properties` permission.
37+
38+
## Model Config — Dune Restrictions
39+
40+
- **Remove `file_format = 'delta'`** — Dune catalog does not support this property. Causes: `table property 'format' does not exist`.
41+
- **Never set `format` or `file_format`** in model configs.
42+
43+
## incremental_predicate Macro
44+
45+
Models using `incremental_predicate()` require these vars in `dbt_project.yml`:
46+
47+
```yaml
48+
vars:
49+
DBT_ENV_INCREMENTAL_TIME_UNIT: 'day'
50+
DBT_ENV_INCREMENTAL_TIME: '1'
51+
```
52+
53+
Otherwise: `Required var 'DBT_ENV_INCREMENTAL_TIME_UNIT' not found`.
54+
55+
## Schema Naming
56+
57+
| Target | Schema pattern | Example |
58+
|--------|----------------|---------|
59+
| dev | `{team}__tmp___{custom}` | `balancer__tmp___balancer_v2_ethereum` |
60+
| prod | `{team}__{custom}` or `{team}` | `balancer__balancer_v2_ethereum` |
61+
62+
Note: dev uses **three underscores** between `tmp` and the custom schema.
63+
64+
## Querying in Dune UI
65+
66+
Always use the `dune.` catalog prefix:
67+
68+
```sql
69+
-- Dev
70+
select * from dune.balancer__tmp___balancer_v2_ethereum.bpt_supply limit 100;
71+
72+
-- Prod
73+
select * from dune.balancer.bpt_supply limit 100;
74+
```
75+
76+
## Common Errors
77+
78+
| Error | Cause | Fix |
79+
|-------|-------|-----|
80+
| 401 Invalid authentication | Wrong auth method or API key | Use LDAP, `user: dune`, `password: DUNE_API_KEY` |
81+
| Env var not provided | .env not loaded | Run `source .env` before dbt |
82+
| table property 'format' does not exist | file_format in model | Remove `file_format = 'delta'` |
83+
| DBT_ENV_INCREMENTAL_TIME_UNIT not found | incremental_predicate macro | Add vars to dbt_project.yml |
84+
| access denied (prod) | See "Access Denied (Prod)" section below | |
85+
| Cannot execute procedure dune._internal.alter_view_properties | API key lacks permission for view metadata | Set `DUNE_SKIP_VIEW_PROPERTIES=true` in .env |
86+
87+
## Access Denied (Prod) — Support Recommendation
88+
89+
When support says: *"make sure in your profiles.yml your api key has permissions in your prod target"*, they refer to:
90+
91+
### 1. API key context
92+
93+
- **API key must be from the team account**, not personal.
94+
- If dev works, the key is team-level — this is not the issue.
95+
- Prod writes to `{team_name}`; personal keys only have access to `{user}__tmp_*`.
96+
97+
### 2. DUNE_TEAM_NAME exact match
98+
99+
- Prod schema (`{{ env_var('DUNE_TEAM_NAME') }}`) must be **exactly** the team handle on Dune.
100+
- Docs: *"Verify you're using the correct team namespace"* (Supported SQL Operations).
101+
- Case-sensitive; no spaces or extra characters.
102+
103+
**Check:** In the Dune UI, what is the exact team handle? Compare with `DUNE_TEAM_NAME` in `.env`.
104+
105+
### 3. Data Transformations enabled
106+
107+
- Docs: *"Dune Enterprise account with Data Transformations enabled"* (prerequisite).
108+
- *"Verify you're using the correct team namespace and have Data Transformations enabled."* (troubleshooting).
109+
110+
**Check:** Does the team's Enterprise plan have Data Transformations enabled?
111+
112+
### 4. profiles.yml — same key for dev and prod
113+
114+
- Dev and prod use the same `DUNE_API_KEY` (correct).
115+
- Only difference is schema: dev → `{team}__tmp_*`, prod → `{team}`.
116+
- `transformations: true` in both (required for writes).
117+
118+
### Checklist for access denied in prod
119+
120+
1. [ ] API key created under **team** context (if dev works, this is done)
121+
2. [ ] `DUNE_TEAM_NAME` = exact team handle on Dune
122+
3. [ ] Data Transformations enabled on team's Enterprise plan
123+
4. [ ] `transformations: true` in session_properties (dev and prod)
124+
5. [ ] Contact Dune support if all above pass — prod schema may require explicit provisioning
125+
126+
## Deploy Targets
127+
128+
```bash
129+
# Dev (default)
130+
uv run dbt run
131+
132+
# Prod
133+
uv run dbt run --target prod
134+
```
135+
136+
Prod may require additional permissions; dev typically works with a valid org API key.
137+
138+
## Verify API Key
139+
140+
```bash
141+
curl -X POST -H "X-DUNE-API-KEY: $DUNE_API_KEY" "https://api.dune.com/api/v1/usage"
142+
```
143+
144+
Success = JSON with `credits_used`, `billing_periods`. Use **POST**, not GET.
145+
146+
## Prerequisites
147+
148+
- Dune Enterprise account with **Data Transformations** enabled.
149+
- API key from the **team/org** (not personal) for team schemas.

.env.example

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
DUNE_TEAM_NAME=balancer
2-
DUNE_API_KEY=""
2+
DUNE_API_KEY=""
3+
4+
# Set to 'true' to skip alter_view_properties in prod (workaround when API key lacks permission)
5+
# DUNE_SKIP_VIEW_PROPERTIES=true

dbt_project.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ flags:
1717
# These configurations specify where dbt should look for different types of files.
1818
# The `model-paths` config, for example, states that models in this project can be
1919
# found in the "models/" directory. You probably won't need to change these!
20-
model-paths: ["models"]
20+
model-paths: ["models", "sources"]
2121
analysis-paths: ["analyses"]
2222
test-paths: ["tests"]
2323
seed-paths: ["seeds"]
@@ -28,8 +28,13 @@ clean-targets: # directories to be removed by `dbt clean`
2828
- "target"
2929
- "dbt_packages"
3030

31+
# Required by incremental_predicate macro (used in transfers_bpt models)
32+
vars:
33+
DBT_ENV_INCREMENTAL_TIME_UNIT: 'day'
34+
DBT_ENV_INCREMENTAL_TIME: '1'
35+
3136
models:
32-
dbt_template:
37+
balancer:
3338
# Config indicated by + and applies to all files under models/templates/
3439
+materialized: view # fallback default, materialized should be overriden in model specific configs
3540
+view_security: invoker # required security setting for views
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{%- macro trino_properties(properties) -%}
2+
map_from_entries(ARRAY[
3+
{%- for key, value in properties.items() %}
4+
ROW('{{ key }}', '{{ value }}')
5+
{%- if not loop.last -%},{%- endif -%}
6+
{%- endfor %}
7+
])
8+
{%- endmacro -%}
9+
10+
{% macro expose_spells(blockchains, spell_type, spell_name, contributors) %}
11+
{%- set validated_contributors = tojson(fromjson(contributors | as_text)) -%}
12+
{%- if ("%s" % validated_contributors) == "null" -%}
13+
{%- do exceptions.raise_compiler_error("Invalid contributors '%s'. The list of contributors must be valid JSON." % contributors) -%}
14+
{%- endif -%}
15+
{%- if target.name == 'prod' and env_var('DUNE_SKIP_VIEW_PROPERTIES', 'false') != 'true' -%}
16+
{%- set properties = {
17+
'dune.public': 'true',
18+
'dune.data_explorer.blockchains': blockchains | as_text,
19+
'dune.data_explorer.category': 'abstraction',
20+
'dune.data_explorer.abstraction.type': spell_type,
21+
'dune.data_explorer.abstraction.name': spell_name,
22+
'dune.data_explorer.contributors': validated_contributors,
23+
'dune.vacuum': '{"enabled":true}'
24+
} -%}
25+
{%- if model.config.materialized == "view" -%}
26+
CALL {{ model.database }}._internal.alter_view_properties('{{ model.schema }}', '{{ model.alias }}',
27+
{{ trino_properties(properties) }}
28+
)
29+
{%- else -%}
30+
ALTER TABLE {{ this }}
31+
SET PROPERTIES extra_properties = {{ trino_properties(properties) }}
32+
{%- endif -%}
33+
{%- endif -%}
34+
{%- endmacro -%}
35+
36+
{% macro hide_spells() %}
37+
{%- if target.name == 'prod' and env_var('DUNE_SKIP_VIEW_PROPERTIES', 'false') != 'true' -%}
38+
{%- set properties = {
39+
'dune.public': 'false',
40+
'dune.data_explorer.category': 'abstraction',
41+
'dune.vacuum': '{"enabled":true}'
42+
} -%}
43+
{%- if model.config.materialized == "view" -%}
44+
CALL {{ model.database }}._internal.alter_view_properties('{{ model.schema }}', '{{ model.alias }}',
45+
{{ trino_properties(properties) }}
46+
)
47+
{%- else -%}
48+
ALTER TABLE {{ this }}
49+
SET PROPERTIES extra_properties = {{ trino_properties(properties) }}
50+
{%- endif -%}
51+
{%- endif -%}
52+
{%- endmacro -%}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{% macro incremental_predicate(column) -%}
2+
{{column}} >= date_trunc('{{var("DBT_ENV_INCREMENTAL_TIME_UNIT")}}', now() - interval '{{var('DBT_ENV_INCREMENTAL_TIME')}}' {{var('DBT_ENV_INCREMENTAL_TIME_UNIT')}})
3+
{%- endmacro -%}

macros/dune_dbt_overrides/get_custom_schema.sql

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,14 @@
88

99
{% macro generate_schema_name(custom_schema_name, node) -%}
1010

11-
{%- set dev_suffix = env_var('DEV_SCHEMA_SUFFIX', '') -%}
11+
{# Base schema is set in profiles.yml per Dune docs (team or team__tmp_*) #}
12+
{%- set base_schema = target.schema -%}
1213

13-
{%- if target.name == 'prod' -%}
14-
{# prod environment, writes to target schema #}
15-
{{ target.schema }}
16-
{%- elif target.name != 'prod' and dev_suffix != '' -%}
17-
{# dev environments, writes to target schema with dev suffix #}
18-
{{ target.schema }}__tmp_{{ dev_suffix | trim }}
14+
{# If a model supplies a custom schema, append it to avoid collisions #}
15+
{%- if custom_schema_name is not none -%}
16+
{{ base_schema }}__{{ custom_schema_name | trim }}
1917
{%- else -%}
20-
{# default to dev environment, no dev suffix #}
21-
{{ target.schema }}__tmp_
18+
{{ base_schema }}
2219
{%- endif -%}
2320

2421
{%- endmacro %}

0 commit comments

Comments
 (0)