Skip to content
99 changes: 99 additions & 0 deletions dbt_subprojects/dex/macros/models/_project/agra_base_trades.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
{% macro agra_base_trades(
blockchain,
start_date = '2026-03-01'
)
%}

with orders as (
select
evt_block_time as block_time
, evt_block_number as block_number
, evt_tx_hash as tx_hash
, evt_index
, contract_address as project_contract_address
, offerer
, recipient
, orderHash as order_hash
, offer
, consideration
from {{ source('agra_multichain', 'settlement_evt_orderfulfilled') }}
where chain = '{{ blockchain }}'
{% if is_incremental() %}
and {{ incremental_predicate('evt_block_time') }}
{% else %}
and evt_block_time >= timestamp '{{ start_date }}'
{% endif %}
)

-- Order hashes settled via matchOrders. Such orders emit one OrderFulfilled per side, so the
-- taker's order leg (offerer = recipient) is the aggregate of the maker legs and is a duplicate.
, matched_hashes as (
select evt_tx_hash as tx_hash, oh as order_hash
from {{ source('agra_multichain', 'settlement_evt_ordersmatched') }}
cross join unnest(orderHashes) as t(oh)
where chain = '{{ blockchain }}'
{% if is_incremental() %}
and {{ incremental_predicate('evt_block_time') }}
{% else %}
and evt_block_time >= timestamp '{{ start_date }}'
{% endif %}
group by 1, 2
)

-- token the taker RECEIVES (offerer gives) -> token_bought
, offer_side as (
select
tx_hash
, evt_index
, from_hex(json_extract_scalar(o, '$.token')) as token
, cast(sum(cast(json_extract_scalar(o, '$.amount') as uint256)) as uint256) as amount
from orders
cross join unnest(offer) as t(o)
where json_extract_scalar(o, '$.itemType') = '1' -- ERC20 only
group by tx_hash, evt_index, from_hex(json_extract_scalar(o, '$.token'))
)

-- token the taker PAYS (offerer receives, incl. fees) -> token_sold
, consideration_side as (
select
tx_hash
, evt_index
, from_hex(json_extract_scalar(c, '$.token')) as token
, cast(sum(cast(json_extract_scalar(c, '$.amount') as uint256)) as uint256) as amount
from orders
cross join unnest(consideration) as t(c)
where json_extract_scalar(c, '$.itemType') = '1' -- ERC20 only
group by tx_hash, evt_index, from_hex(json_extract_scalar(c, '$.token'))
)

select
'{{ blockchain }}' as blockchain
, 'agra' as project
, '1' as version
, cast(date_trunc('month', o.block_time) as date) as block_month
, cast(date_trunc('day', o.block_time) as date) as block_date
, o.block_time
, o.block_number
, ofr.amount as token_bought_amount_raw
, con.amount as token_sold_amount_raw
, ofr.token as token_bought_address
, con.token as token_sold_address
, o.recipient as taker
, o.offerer as maker
, o.project_contract_address
, o.tx_hash
, o.evt_index
from orders o
inner join offer_side ofr
on ofr.tx_hash = o.tx_hash
and ofr.evt_index = o.evt_index
inner join consideration_side con
on con.tx_hash = o.tx_hash
and con.evt_index = o.evt_index
Comment on lines +125 to +127

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve item identity when joining Seaport legs

When an Agra OrderFulfilled contains more than one ERC20 offer or consideration item, offer_side and consideration_side each produce one row per token, but this join only uses tx_hash and evt_index. A single event then expands into every offer-token × consideration-token combination while the model is keyed and tested only by tx_hash, evt_index, so multi-asset fills will either fail the unique test/merge or be arbitrarily de-duped downstream in dex_base_trades_macro.

Useful? React with 👍 / 👎.

@cxheng315 cxheng315 Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't happen for Agra. It's a CLOB — every order matches exactly one ERC20 against the quote token (a/b or c/b). A single order paying two tokens for one (a + c / b) isn't a valid trade; it could only come from a contract failure. Confirmed across all production data so far (247 events: 235 ethereum, 12 hyperevm) — every OrderFulfilled has exactly one distinct ERC20 per side.

And even if it did occur, a multi-asset fill isn't representable in dex.trades anyway (one token_bought_address / one token_sold_address per row, and no prices at the base layer to split a leg), so it's intentionally out of scope — consistent with the rest of the base-trades sector.

That said, I added a deterministic guard so a malformed order can never silently break the (tx_hash, evt_index) unique key or the incremental merge: each side collapses to one row per event via row_number() (QUALIFY isn't supported in DuneSQL/Trino). It's a verified no-op on all current data — old-vs-new output is byte-identical (209 = 209 rows, empty symmetric difference) — and the unique key stays the tripwire if an anomalous order ever appears.

left join matched_hashes m
on m.tx_hash = o.tx_hash
and m.order_hash = o.order_hash
-- drop the duplicated taker aggregate leg of matched orders; keep everything else
where m.order_hash is null or o.offerer <> o.recipient

{% endmacro %}
9 changes: 9 additions & 0 deletions dbt_subprojects/dex/models/_projects/agra/agra_trades.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{ config(
schema = 'agra',
alias = 'trades',
materialized = 'view'
, post_hook='{{ hide_spells() }}'
)
}}

{{ dex_project_trades('agra') }}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ version: 2

models:
- name: aerodrome_trades
- name: agra_trades
- name: airswap_trades
- name: apeswap_trades
- name: arbswap_trades
Expand Down
1 change: 1 addition & 0 deletions dbt_subprojects/dex/models/dex_info.sql
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,5 @@ FROM (VALUES
, ('bitget_dex_aggregator', 'Bitget Wallet Aggregator', 'Aggregator', 'BitgetWalletAggregator')
, ('tessera_v', 'Tessera-V', 'Direct', 'tessera_v')
, ('bulba', 'Bulba', 'Direct', 'bulba')
, ('agra', 'Agra', 'Direct', 'agra_gg')
) AS temp_table (project, name, marketplace_type, x_username)
19 changes: 19 additions & 0 deletions dbt_subprojects/dex/models/trades/ethereum/_schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,25 @@ models:
filter:
version: 3

- name: agra_ethereum_base_trades
meta:
blockchain: ethereum
sector: dex
project: agra
contributors: cxheng315
config:
tags: [ 'ethereum', 'dex', 'trades', 'agra' ]
description: "Agra ethereum base trades"
data_tests:
- dbt_utils.unique_combination_of_columns:
arguments:
combination_of_columns:
- tx_hash
- evt_index
- check_dex_base_trades_seed:
arguments:
seed_file: ref('agra_ethereum_base_trades_seed')


- name: dex_ethereum_token_volumes_daily
data_tests:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
, ref('supernova_v3_ethereum_base_trades')
, ref('zeroex_ethereum_base_trades')
, ref('zeroex_settler_ethereum_base_trades')
, ref('agra_ethereum_base_trades')
] %}
WITH base AS (
{{ dex_base_trades_macro(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{{ config(
schema = 'agra_ethereum'
, alias = 'base_trades'
, materialized = 'incremental'
, file_format = 'delta'
, incremental_strategy = 'merge'
, unique_key = ['tx_hash', 'evt_index']
, incremental_predicates = [incremental_predicate('DBT_INTERNAL_DEST.block_time')]
)
}}

{{ agra_base_trades(blockchain = 'ethereum') }}
19 changes: 19 additions & 0 deletions dbt_subprojects/dex/models/trades/hyperevm/_schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,22 @@ models:
seed_file: ref('balancer_hyperevm_base_trades_seed')
filter:
version: 3

- name: agra_hyperevm_base_trades
meta:
blockchain: hyperevm
sector: dex
project: agra
contributors: cxheng315
config:
tags: [ 'hyperevm', 'dex', 'trades', 'agra' ]
description: "Agra HyperEVM base trades"
data_tests:
- dbt_utils.unique_combination_of_columns:
arguments:
combination_of_columns:
- tx_hash
- evt_index
- check_dex_base_trades_seed:
arguments:
seed_file: ref('agra_hyperevm_base_trades_seed')
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
, ref('hyperswap_v3_hyperevm_base_trades')
, ref('hybra_v3_hyperevm_base_trades')
, ref('balancer_v3_hyperevm_base_trades')
, ref('agra_hyperevm_base_trades')
] %}
{{ dex_base_trades_macro(
blockchain = 'hyperevm',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{{ config(
schema = 'agra_hyperevm'
, alias = 'base_trades'
, materialized = 'incremental'
, file_format = 'delta'
, incremental_strategy = 'merge'
, unique_key = ['tx_hash', 'evt_index']
, incremental_predicates = [incremental_predicate('DBT_INTERNAL_DEST.block_time')]
)
}}

{{ agra_base_trades(blockchain = 'hyperevm') }}
30 changes: 30 additions & 0 deletions dbt_subprojects/dex/seeds/trades/_schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6205,3 +6205,33 @@ seeds:
token_bought_amount_raw: uint256
token_sold_amount_raw: uint256
block_date: timestamp

- name: agra_ethereum_base_trades_seed
config:
column_types:
blockchain: varchar
project: varchar
version: varchar
tx_hash: varbinary
evt_index: uint256
block_number: uint256
token_bought_address: varbinary
token_sold_address: varbinary
token_bought_amount_raw: uint256
token_sold_amount_raw: uint256
block_date: timestamp

- name: agra_hyperevm_base_trades_seed
config:
column_types:
blockchain: varchar
project: varchar
version: varchar
tx_hash: varbinary
evt_index: uint256
block_number: uint256
token_bought_address: varbinary
token_sold_address: varbinary
token_bought_amount_raw: uint256
token_sold_amount_raw: uint256
block_date: timestamp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
blockchain,project,version,block_date,tx_hash,evt_index,token_bought_address,token_sold_address,block_number,token_bought_amount_raw,token_sold_amount_raw
ethereum,agra,1,2026-06-26,0xf65db18c06700ca14f938c08dc98298aafa9e44d64d3165cc78e95ae8892deee,161,0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48,0xc9499006a149c553d18171747ed19aa7c6dd19e2,25400895,2998470,3000000
ethereum,agra,1,2026-06-26,0xbc1f81e637e09c3b5b2eaff92a7b7090a37afad576099d36fb15510b09fb7e9e,67,0xc9499006a149c553d18171747ed19aa7c6dd19e2,0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48,25400893,2998470,2999999
ethereum,agra,1,2026-06-25,0xa9c838147c9c34c37fa5dbb34b32439980df845933101d35574c4c988bb7e411,324,0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48,0xbf4e3fbe8b60062a00c7a6b1d97d0d49c2971a19,25394695,101181000,100000000
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
blockchain,project,version,block_date,tx_hash,evt_index,token_bought_address,token_sold_address,block_number,token_bought_amount_raw,token_sold_amount_raw
hyperevm,agra,1,2026-03-13,0x788f742644007f6d4780d573e8fc4c13157d8d750ffb89d8aeb33cfb8e6121b4,0,0x5555555555555555555555555555555555555555,0x5748ae796ae46a4f1348a1693de4b50560485562,29637157,998583302299000000,981987710000000000
hyperevm,agra,1,2026-03-13,0x61fa7014a33d33de87fa076bd887e9db6fd91172263301f86b6e7b80a59c223b,4,0x5555555555555555555555555555555555555555,0x5748ae796ae46a4f1348a1693de4b50560485562,29633718,18316689857700000,18012300000000000
hyperevm,agra,1,2026-03-13,0x68f814e2bf69ff189e8f7ba372a350a96f7e7a7577807c5b6457cca3995e1de6,0,0x5555555555555555555555555555555555555555,0x5748ae796ae46a4f1348a1693de4b50560485562,29633687,998582310142300000,981987700000000000
5 changes: 5 additions & 0 deletions sources/_sector/dex/trades/ethereum/_sources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,8 @@ sources:
- name: pair_evt_swap
- name: algebrafactory_evt_custompool
- name: algebrapool_evt_swap

- name: agra_multichain
tables:
- name: settlement_evt_orderfulfilled
- name: settlement_evt_ordersmatched
Loading