add nft and defi curation, with tests and docs

This commit is contained in:
Mike Stepanovic 2025-08-21 13:59:20 -06:00
parent cec1e5e57a
commit 550fcb16ac
41 changed files with 2679 additions and 15 deletions

View File

@ -17,30 +17,50 @@ There is more information on how to use dbt docs in the last section of this doc
**Click on the links below to jump to the documentation for each schema.**
### Core Fact Tables (`movement`.`CORE`.`<table_name>`)
- [fact_blocks](#!/model/model.movement_models.core__fact_blocks)
- [fact_changes](#!/model/model.movement_models.core__fact_changes)
- [fact_events](#!/model/model.movement_models.core__fact_events)
- [fact_transactions](#!/model/model.movement_models.core__fact_transactions)
- [fact_transactions_block_metadata](#!/model/model.movement_models.core__fact_transactions_block_metadata)
- [fact_transactions_state_checkpoint](#!/model/model.movement_models.core__fact_transactions_state_checkpoint)
- [fact_transfers](#!/model/model.movement_models.core__fact_transfers)
### Core Tables (`movement`.`CORE`.`<table_name>`)
**Dimension Tables:**
- [core.dim_labels](#!/model/model.movement_models.core__dim_labels)
**Fact Tables:**
- [core.fact_blocks](#!/model/model.movement_models.core__fact_blocks)
- [core.fact_changes](#!/model/model.movement_models.core__fact_changes)
- [core.fact_events](#!/model/model.movement_models.core__fact_events)
- [core.fact_transactions](#!/model/model.movement_models.core__fact_transactions)
- [core.fact_transactions_block_metadata](#!/model/model.movement_models.core__fact_transactions_block_metadata)
- [core.fact_transactions_state_checkpoint](#!/model/model.movement_models.core__fact_transactions_state_checkpoint)
- [core.fact_transfers](#!/model/model.movement_models.core__fact_transfers)
**Convenience Views:**
- [core.ez_transfers](#!/model/model.movement_models.core__ez_transfers)
### Price Tables (`movement`.`PRICE`.`<table_name>`)
**Dimension Tables:**
- [price.dim_asset_metadata](https://flipsidecrypto.github.io/stellar-models/#!/model/model.movement_models.price__dim_asset_metadata)
- [price.dim_asset_metadata](#!/model/model.movement_models.price__dim_asset_metadata)
**Fact Tables:**
- [price.fact_prices_ohlc_hourly](https://flipsidecrypto.github.io/stellar-models/#!/model/model.movement_models.price__fact_prices_ohlc_hourly)
- [price.fact_prices_ohlc_hourly](#!/model/model.movement_models.price__fact_prices_ohlc_hourly)
**Convenience Views:**
- [price.ez_prices_hourly](https://flipsidecrypto.github.io/stellar-models/#!/model/model.movement_models.price__ez_prices_hourly)
- [price.ez_asset_metadata](https://flipsidecrypto.github.io/stellar-models/#!/model/model.movement_models.price__ez_asset_metadata)
- [price.ez_prices_hourly](#!/model/model.movement_models.price__ez_prices_hourly)
- [price.ez_asset_metadata](#!/model/model.movement_models.price__ez_asset_metadata)
### Stats Tables (`stellar`.`STATS`.`<table_name>`)
### DeFi Tables (`movement`.`DEFI`.`<table_name>`)
- [stats.ez_core_metrics_hourly](https://flipsidecrypto.github.io/stellar-models/#!/model/model.movement_models.stats__ez_core_metrics_hourly)
**Fact Tables:**
- [defi.fact_bridge_activity](#!/model/model.movement_models.defi__fact_bridge_activity)
### NFT Tables (`movement`.`NFT`.`<table_name>`)
**Fact Tables:**
- [nft.fact_nft_mints](#!/model/model.movement_models.nft__fact_nft_mints)
- [nft.fact_nft_sales](#!/model/model.movement_models.nft__fact_nft_sales)
### Stats Tables (`movement`.`STATS`.`<table_name>`)
**Convenience Views:**
- [stats.ez_core_metrics_hourly](#!/model/model.movement_models.stats__ez_core_metrics_hourly)
The movement models are built a few different ways, but the core fact tables are built using three layers of sql models: **bronze, silver, and gold (or core).**

View File

@ -0,0 +1,5 @@
{% docs address_name %}
The human-readable name or label associated with an address, making it easier to identify the entity behind the address.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs amount_unadj %}
The raw, unadjusted amount of tokens or assets involved in the transaction, before any decimal adjustment.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs buyer_address %}
The address of the buyer in an NFT sale transaction.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs currency_address %}
The contract address of the currency or token used for payment in the transaction.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs direction %}
The direction of the bridge transaction, typically indicating whether assets are being deposited or withdrawn.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs event_name %}
The name of the specific event being processed or emitted by the smart contract.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs nft_address %}
The contract address of the NFT collection or smart contract.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs nft_count %}
The number of NFT tokens involved in the transaction, typically 1 for unique NFTs.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs nft_from_address %}
The address from which the NFT is being transferred or minted. For mint transactions, this is typically the zero address or null.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs nft_to_address %}
The address to which the NFT is being transferred or minted.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs platform_address %}
The contract address of the marketplace or platform facilitating the NFT transaction.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs platform_exchange_version %}
The version of the exchange or marketplace contract being used for the transaction.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs platform_name %}
The name of the marketplace or platform facilitating the NFT transaction.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs project_name %}
The name of the project, protocol, or application associated with the transaction or event.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs seller_address %}
The address of the seller in an NFT sale transaction.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs core__dim_labels %}
This table contains labeled addresses with associated metadata, providing human-readable names and categorization for blockchain addresses. This enables easier identification and analysis of different entities within the Movement ecosystem.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs defi__fact_bridge_activity %}
This table captures cross-chain bridge activity on the Movement network, tracking transfers of assets between different blockchain networks. It includes details about the source and destination chains, amounts transferred, and the platforms facilitating these bridge transactions.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs nft__fact_nft_mints %}
This table tracks NFT minting activities on the Movement network, capturing when new non-fungible tokens are created and minted to specific addresses. It includes details about the NFT collection, token IDs, and any associated pricing information.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs nft__fact_nft_sales %}
This table captures NFT sale transactions on the Movement network, tracking secondary market activity where NFTs are bought and sold between users. It includes marketplace information, buyer/seller details, and transaction pricing.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs token_version %}
The version of the token standard being used (e.g., v1, v2) for the NFT contract.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs tokenid %}
The unique identifier of the specific NFT token within the collection.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs total_price_raw %}
The raw total price paid for the NFT transaction, before any decimal adjustments.
{% enddocs %}

View File

@ -0,0 +1,5 @@
{% docs tx_sender %}
The address that sent or initiated the transaction.
{% enddocs %}

View File

@ -0,0 +1,36 @@
{{ config(
materialized = 'incremental',
unique_key = ['dim_labels_id'],
incremental_strategy = 'merge',
cluster_by = 'modified_timestamp::DATE',
merge_exclude_columns = ['inserted_timestamp'],
tags = ['core']
) }}
SELECT
'movement' AS blockchain,
creator,
address,
address_name,
label_type,
label_subtype,
project_name,
{{ dbt_utils.generate_surrogate_key(
[' address ']
) }} AS dim_labels_id,
SYSDATE() AS inserted_timestamp,
modified_timestamp
FROM
{{ ref('silver__labels') }}
{% if is_incremental() %}
WHERE
modified_timestamp >= (
SELECT
MAX(
modified_timestamp
)
FROM
{{ this }}
)
{% endif %}

View File

@ -1,5 +1,36 @@
version: 2
models:
- name: core__dim_labels
description: '{{ doc("core__dim_labels") }}'
tests:
- dbt_utils.recency:
datepart: hour
field: MODIFIED_TIMESTAMP
interval: 3
severity: error
tags: ['test_recency']
columns:
- name: blockchain
description: '{{ doc("blockchain") }}'
- name: creator
description: '{{ doc("creator") }}'
- name: address
description: '{{ doc("address") }}'
- name: address_name
description: '{{ doc("address_name") }}'
- name: label_type
description: '{{ doc("label_type") }}'
- name: label_subtype
description: '{{ doc("label_subtype") }}'
- name: project_name
description: '{{ doc("project_name") }}'
- name: dim_labels_id
description: '{{ doc("pk") }}'
- name: inserted_timestamp
description: '{{ doc("inserted_timestamp") }}'
- name: modified_timestamp
description: '{{ doc("modified_timestamp") }}'
- name: core__ez_transfers
description: '{{ doc("core__ez_transfers") }}'
tests:

View File

@ -0,0 +1,79 @@
{{ config(
materialized = 'incremental',
unique_key = 'fact_bridge_activity_id',
incremental_strategy = 'merge',
incremental_predicates = ["dynamic_range_predicate", "block_timestamp::DATE"],
merge_exclude_columns = ["inserted_timestamp"],
cluster_by = ['block_timestamp::DATE'],
post_hook = "ALTER TABLE {{ this }} ADD SEARCH OPTIMIZATION ON EQUALITY(tx_hash,version,tx_sender, sender, receiver);",
meta ={ 'database_tags':{ 'table':{ 'PURPOSE': 'BRIDGE' }} },
tags = ['noncore']
) }}
{% if execute %}
{% if is_incremental() %}
{% set query %}
CREATE
OR REPLACE temporary TABLE defi.bridge__mod_intermediate_tmp AS
SELECT
platform,
MAX(modified_timestamp) modified_timestamp
FROM
{{ this }}
GROUP BY
platform {% endset %}
{% do run_query(
query
) %}
{% set min_block_date_query %}
SELECT
MIN(block_timestamp)
FROM
{{ ref('silver__bridge_layerzero_transfers') }} A
LEFT JOIN defi.bridge__mod_intermediate_tmp b
ON A.platform = b.platform
WHERE
(
A.modified_timestamp >= b.modified_timestamp
OR b.modified_timestamp IS NULL
) {% endset %}
{% set min_bd = run_query(min_block_date_query) [0] [0] %}
{% if not min_bd or min_bd == 'None' %}
{% set min_bd = '2099-01-01' %}
{% endif %}
{% endif %}
{% endif %}
SELECT
block_number,
block_timestamp,
version,
tx_hash,
platform,
bridge_address,
event_name,
direction,
tx_sender,
sender,
receiver,
source_chain_id,
source_chain_name,
destination_chain_id,
destination_chain_name,
token_address,
amount_unadj,
event_index,
{{ dbt_utils.generate_surrogate_key(
['tx_hash', 'event_index']
) }} AS fact_bridge_activity_id,
SYSDATE() AS inserted_timestamp,
SYSDATE() AS modified_timestamp,
'{{ invocation_id }}' AS _invocation_id
FROM
{{ ref('silver__bridge_layerzero_transfers') }}
{% if is_incremental() %}
WHERE
block_timestamp :: DATE >= '{{min_bd}}'
{% endif %}

View File

@ -0,0 +1,54 @@
version: 2
models:
- name: defi__fact_bridge_activity
description: '{{ doc("defi__fact_bridge_activity") }}'
tests:
- dbt_utils.recency:
datepart: hour
field: MODIFIED_TIMESTAMP
interval: 3
severity: error
tags: ['test_recency']
columns:
- name: block_number
description: '{{ doc("block_number") }}'
- name: block_timestamp
description: '{{ doc("block_timestamp") }}'
- name: version
description: '{{ doc("version") }}'
- name: tx_hash
description: '{{ doc("tx_hash") }}'
- name: platform
description: '{{ doc("bridge_platform") }}'
- name: bridge_address
description: '{{ doc("bridge_address") }}'
- name: event_name
description: '{{ doc("event_name") }}'
- name: direction
description: '{{ doc("direction") }}'
- name: tx_sender
description: '{{ doc("tx_sender") }}'
- name: sender
description: '{{ doc("bridge_sender") }}'
- name: receiver
description: '{{ doc("bridge_receiver") }}'
- name: source_chain_id
description: '{{ doc("source_chain_id") }}'
- name: source_chain_name
description: '{{ doc("source_chain") }}'
- name: destination_chain_id
description: '{{ doc("destination_chain_id") }}'
- name: destination_chain_name
description: '{{ doc("destination_chain") }}'
- name: token_address
description: '{{ doc("bridge_token_address") }}'
- name: amount_unadj
description: '{{ doc("amount_unadj") }}'
- name: event_index
description: '{{ doc("event_index") }}'
- name: fact_bridge_activity_id
description: '{{ doc("pk") }}'
- name: inserted_timestamp
description: '{{ doc("inserted_timestamp") }}'
- name: modified_timestamp
description: '{{ doc("modified_timestamp") }}'

View File

@ -0,0 +1,95 @@
version: 2
models:
- name: nft__fact_nft_mints
description: '{{ doc("nft__fact_nft_mints") }}'
tests:
- dbt_utils.recency:
datepart: hour
field: MODIFIED_TIMESTAMP
interval: 3
severity: error
tags: ['test_recency']
columns:
- name: block_timestamp
description: '{{ doc("block_timestamp") }}'
- name: block_number
description: '{{ doc("block_number") }}'
- name: version
description: '{{ doc("version") }}'
- name: tx_hash
description: '{{ doc("tx_hash") }}'
- name: event_index
description: '{{ doc("event_index") }}'
- name: event_type
description: '{{ doc("event_type") }}'
- name: nft_from_address
description: '{{ doc("nft_from_address") }}'
- name: nft_to_address
description: '{{ doc("nft_to_address") }}'
- name: nft_address
description: '{{ doc("nft_address") }}'
- name: token_version
description: '{{ doc("token_version") }}'
- name: project_name
description: '{{ doc("project_name") }}'
- name: tokenid
description: '{{ doc("tokenid") }}'
- name: nft_count
description: '{{ doc("nft_count") }}'
- name: total_price_raw
description: '{{ doc("total_price_raw") }}'
- name: currency_address
description: '{{ doc("currency_address") }}'
- name: fact_nft_mints_id
description: '{{ doc("pk") }}'
- name: inserted_timestamp
description: '{{ doc("inserted_timestamp") }}'
- name: modified_timestamp
description: '{{ doc("modified_timestamp") }}'
- name: nft__fact_nft_sales
description: '{{ doc("nft__fact_nft_sales") }}'
tests:
- dbt_utils.recency:
datepart: hour
field: MODIFIED_TIMESTAMP
interval: 3
severity: error
tags: ['test_recency']
columns:
- name: block_timestamp
description: '{{ doc("block_timestamp") }}'
- name: block_number
description: '{{ doc("block_number") }}'
- name: version
description: '{{ doc("version") }}'
- name: tx_hash
description: '{{ doc("tx_hash") }}'
- name: event_index
description: '{{ doc("event_index") }}'
- name: event_type
description: '{{ doc("event_type") }}'
- name: buyer_address
description: '{{ doc("buyer_address") }}'
- name: seller_address
description: '{{ doc("seller_address") }}'
- name: nft_address
description: '{{ doc("nft_address") }}'
- name: token_version
description: '{{ doc("token_version") }}'
- name: platform_address
description: '{{ doc("platform_address") }}'
- name: project_name
description: '{{ doc("project_name") }}'
- name: platform_name
description: '{{ doc("platform_name") }}'
- name: platform_exchange_version
description: '{{ doc("platform_exchange_version") }}'
- name: total_price_raw
description: '{{ doc("total_price_raw") }}'
- name: fact_nft_sales_id
description: '{{ doc("pk") }}'
- name: inserted_timestamp
description: '{{ doc("inserted_timestamp") }}'
- name: modified_timestamp
description: '{{ doc("modified_timestamp") }}'

View File

@ -0,0 +1,43 @@
{{ config(
materialized = 'incremental',
unique_key = ['fact_nft_mints_id'],
incremental_strategy = 'merge',
cluster_by = ['modified_timestamp::DATE'],
merge_exclude_columns = ['inserted_timestamp'],
post_hook = "ALTER TABLE {{ this }} ADD SEARCH OPTIMIZATION ON EQUALITY(nft_address);",
tags = ['noncore']
) }}
SELECT
block_timestamp,
block_number,
version,
tx_hash,
event_index,
event_type,
nft_from_address,
nft_to_address,
nft_address,
token_version,
project_name,
tokenid,
nft_count,
total_price_raw,
currency_address,
{{ dbt_utils.generate_surrogate_key(
['tx_hash','event_index']
) }} AS fact_nft_mints_id,
SYSDATE() AS inserted_timestamp,
SYSDATE() AS modified_timestamp,
'{{ invocation_id }}' AS _invocation_id
FROM
{{ ref('silver__nft_mints_combined') }}
{% if is_incremental() %}
WHERE
modified_timestamp >= (
SELECT
MAX(modified_timestamp)
FROM
{{ this }}
)
{% endif %}

View File

@ -0,0 +1,40 @@
{{ config(
materialized = 'incremental',
unique_key = ['fact_nft_sales_id'],
incremental_strategy = 'merge',
cluster_by = ['modified_timestamp::DATE'],
merge_exclude_columns = ['inserted_timestamp'],
post_hook = "ALTER TABLE {{ this }} ADD SEARCH OPTIMIZATION ON EQUALITY(nft_address);",
tags = ['noncore']
) }}
SELECT
block_timestamp,
block_number,
version,
tx_hash,
event_index,
event_type,
buyer_address,
seller_address,
nft_address,
token_version,
platform_address,
project_name,
platform_name,
platform_exchange_version,
total_price_raw,
nft_sales_tradeport_id AS fact_nft_sales_id,
inserted_timestamp,
modified_timestamp
FROM
{{ ref('silver__nft_sales_tradeport') }}
{% if is_incremental() %}
WHERE
modified_timestamp >= (
SELECT
MAX(modified_timestamp)
FROM
{{ this }}
)
{% endif %}

View File

@ -0,0 +1,23 @@
{{ config(
materialized = 'view',
tags = ['core']
) }}
SELECT
system_created_at,
insert_date,
blockchain,
address,
creator,
label_type,
label_subtype,
address_name,
project_name,
modified_timestamp
FROM
{{ source(
'crosschain',
'dim_labels'
) }}
WHERE
blockchain = 'movement'

View File

@ -520,4 +520,62 @@ models:
- name: modified_timestamp
data_type: TIMESTAMP_NTZ
- name: _invocation_id
data_type: VARCHAR
data_type: VARCHAR
- name: silver__labels
description: Address labels and metadata from crosschain source filtered for movement blockchain
config:
contract:
enforced: true
columns:
- name: system_created_at
data_type: TIMESTAMP_NTZ
description: System timestamp when the label was created
tests:
- not_null:
tags: ['test_quality']
- name: insert_date
data_type: DATE
description: Date when the label was inserted
tests:
- not_null:
tags: ['test_quality']
- name: blockchain
data_type: VARCHAR
description: Blockchain name (movement)
tests:
- not_null:
tags: ['test_quality']
- accepted_values:
values: ['movement']
tags: ['test_quality']
- name: address
data_type: VARCHAR
description: Address being labeled
tests:
- not_null:
tags: ['test_quality']
- name: creator
data_type: VARCHAR
description: Creator of the address/contract
- name: label_type
data_type: VARCHAR
description: Type of label (e.g., cex, dex, defi)
tests:
- not_null:
tags: ['test_quality']
- name: label_subtype
data_type: VARCHAR
description: Subtype of the label for more granular categorization
- name: address_name
data_type: VARCHAR
description: Human-readable name for the address
- name: project_name
data_type: VARCHAR
description: Name of the project associated with the address
- name: modified_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp when the label was last modified
tests:
- not_null:
tags: ['test_quality']

View File

@ -0,0 +1,238 @@
{{ config(
materialized = 'incremental',
unique_key = "bridge_layerzero_transfers_id",
incremental_strategy = 'merge',
merge_exclude_columns = ["inserted_timestamp"],
cluster_by = ['block_timestamp::DATE','inserted_timestamp::DATE'],
post_hook = "ALTER TABLE {{ this }} ADD SEARCH OPTIMIZATION ON EQUALITY(tx_hash, version, sender, receiver);",
tags = ['noncore']
) }}
WITH evnts AS (
SELECT
block_number,
block_timestamp,
version,
tx_hash,
event_index,
payload_function,
event_address,
event_resource,
event_data,
inserted_timestamp
FROM
{{ ref('core__fact_events') }}
WHERE
-- Movement Network bridge events - LayerZero OFT and legacy bridge
(
-- LayerZero OFT events for MOVE, USDC.e, USDT.e, WETH.e, WBTC.e
(
event_address IN (
'0x7e4fd97ef92302eea9b10f74be1d96fb1f1511cf7ed28867b0144ca89c6ebc3c', -- MOVE
'0x4d2969d384e440db9f1a51391cfc261d1ec08ee1bdf7b9711a6c05d485a4110a', -- USDC.e
'0x38cdb3f0afabee56a3393793940d28214cba1f5781e13d5db18fa7079f60ab55', -- USDT.e
'0x3dfe1ac4574c7dbbe6f1c5ba862de88fc3e7d3cf8eba95ef1abf32b582889e6d', -- WETH.e
'0xbdf86868a32dbae96f2cd50ab05b4be43b92e84e793a4fc01b5b460cc38fdc14' -- WBTC.e
)
AND event_module = 'oft_core'
AND event_resource IN ('OftSent', 'OftReceived')
)
OR
-- LayerZero deposit events via bridge_receiver
(
event_address = '0x8110d118c4886c48979966b837bbd948f9a35660f36903926f51b93c1da5d1d'
AND event_module = 'bridge_receiver'
AND event_resource = 'LZDepositEvent'
)
OR
-- Legacy bridge events
(
event_address = '0xf3a5e4355c5ad7f9164da03754b1c90ed3c7f3a611f87d9c220a728e6d71d595'
AND event_module = 'bridge'
AND event_resource IN ('BridgeEvent', 'WithdrawEvent')
)
)
AND success
{% if is_incremental() %}
AND inserted_timestamp >= (
SELECT
MAX(inserted_timestamp)
FROM
{{ this }}
)
{% else %}
AND block_timestamp :: DATE >= '2024-11-01'
{% endif %}
),
txs AS (
SELECT
block_timestamp,
tx_hash,
sender,
payload :type_arguments [0] :: STRING AS token_address,
payload :arguments [1] :: STRING AS src_sender
FROM
{{ ref('core__fact_transactions') }}
WHERE
success
{% if is_incremental() %}
AND inserted_timestamp >= (
SELECT
MAX(inserted_timestamp)
FROM
{{ this }}
)
{% else %}
AND block_timestamp :: DATE >= '2024-11-01'
{% endif %}
),
chngs AS (
SELECT
block_timestamp,
tx_hash,
change_module,
CASE
WHEN change_module = 'coin' THEN change_data :coin :value
WHEN change_module = 'oft' THEN COALESCE(change_data :locked_coin :value, change_data :fungible_asset :value)
WHEN change_module = 'fungible_asset' THEN change_data :fungible_asset :value
END :: INT AS amount,
change_resource :: STRING AS token_address,
change_index
FROM
{{ ref('core__fact_changes') }}
WHERE
success
AND change_module IN (
'coin',
'oft',
'fungible_asset'
) {# AND amount IS NOT NULL #}
{% if is_incremental() %}
AND inserted_timestamp >= (
SELECT
MAX(inserted_timestamp)
FROM
{{ this }}
)
{% else %}
AND block_timestamp :: DATE >= '2024-11-01'
{% endif %}
),
chngs_2 AS (
SELECT
block_timestamp,
tx_hash,
token_address
FROM
chngs
WHERE
change_module = 'coin'
AND token_address LIKE 'CoinInfo%' qualify(ROW_NUMBER() over(PARTITION BY tx_hash
ORDER BY
change_index DESC) = 1)
)
SELECT
A.block_number,
A.block_timestamp,
A.version,
A.tx_hash,
'layerzero' AS platform,
A.event_address AS bridge_address,
A.event_resource AS event_name,
CASE
WHEN event_resource IN ('OftSent', 'WithdrawEvent') THEN 'outbound'
WHEN event_resource IN ('OftReceived', 'LZDepositEvent', 'BridgeEvent') THEN 'inbound'
ELSE 'unknown'
END AS direction,
b.sender AS tx_sender,
CASE
WHEN event_resource IN ('OftSent', 'WithdrawEvent') THEN b.sender
WHEN event_resource = 'OftReceived' THEN COALESCE(b.src_sender, event_data:from_address::STRING)
WHEN event_resource = 'LZDepositEvent' THEN REPLACE(event_data:sender::STRING, '000000000000000000000000', '')
WHEN event_resource = 'BridgeEvent' THEN event_data:from_address::STRING
ELSE b.sender
END AS sender,
CASE
WHEN event_resource = 'OftSent' THEN event_data:from_address::STRING
WHEN event_resource = 'OftReceived' THEN event_data:to_address::STRING
WHEN event_resource = 'LZDepositEvent' THEN NULL -- receiver is embedded in message
WHEN event_resource = 'BridgeEvent' THEN event_data:to_address::STRING
WHEN event_resource = 'WithdrawEvent' THEN event_data:to_address::STRING
ELSE COALESCE(
event_data:receiver::STRING,
REPLACE(event_data:dst_receiver::STRING, '000000000000000000000000', '')
)
END AS receiver,
CASE
WHEN direction = 'outbound' THEN 108
WHEN event_resource = 'OftReceived' THEN
CASE
WHEN event_data:src_eid::INT = 30101 THEN 1 -- LayerZero Ethereum mainnet EID
ELSE event_data:src_eid::INT
END
WHEN event_resource = 'LZDepositEvent' THEN
CASE
WHEN event_data:src_eid::INT = 30101 THEN 1 -- LayerZero Ethereum mainnet EID
ELSE event_data:src_eid::INT
END
WHEN event_resource = 'BridgeEvent' THEN event_data:source_chain::INT
ELSE COALESCE(event_data:src_chain_id::INT, event_data:src_eid::INT)
END AS source_chain_id,
CASE
WHEN source_chain_id = 1 THEN 'ethereum'
WHEN source_chain_id = 108 THEN 'movement'
ELSE 'unknown'
END AS source_chain_name,
CASE
WHEN direction = 'inbound' THEN 108
WHEN event_resource = 'OftSent' THEN
CASE
WHEN event_data:dst_eid::INT = 30101 THEN 1 -- LayerZero Ethereum mainnet EID
ELSE event_data:dst_eid::INT
END
WHEN event_resource = 'WithdrawEvent' THEN event_data:target_chain::INT
ELSE COALESCE(event_data:dst_chain_id::INT, event_data:dst_eid::INT)
END AS destination_chain_id,
CASE
WHEN destination_chain_id = 1 THEN 'ethereum'
WHEN destination_chain_id = 108 THEN 'movement'
ELSE 'unknown'
END AS destination_chain_name,
A.event_address AS token_address,
CASE
WHEN event_resource IN ('OftSent', 'OftReceived') THEN COALESCE(event_data:amount_sent_ld::INT, event_data:amount_received_ld::INT)
WHEN event_resource IN ('BridgeEvent', 'WithdrawEvent') THEN event_data:amount::INT
ELSE COALESCE(event_data:amount::INT, event_data:amount_ld::INT)
END AS amount_unadj,
A.event_index,
{{ dbt_utils.generate_surrogate_key(
['a.tx_hash','a.event_index']
) }} AS bridge_layerzero_transfers_id,
SYSDATE() AS inserted_timestamp,
SYSDATE() AS modified_timestamp,
'{{ invocation_id }}' AS _invocation_id
FROM
evnts A
JOIN txs b
ON A.tx_hash = b.tx_hash
AND A.block_timestamp :: DATE = b.block_timestamp :: DATE
LEFT JOIN chngs C
ON A.tx_hash = C.tx_hash
AND A.block_timestamp :: DATE = C.block_timestamp :: DATE
AND amount_unadj = C.amount
AND C.change_module = 'coin'
LEFT JOIN chngs d
ON A.tx_hash = d.tx_hash
AND A.block_timestamp :: DATE = d.block_timestamp :: DATE
AND d.change_module IN ('oft', 'fungible_asset')
AND d.amount IS NOT NULL
LEFT JOIN chngs_2 e
ON A.tx_hash = e.tx_hash
AND A.block_timestamp :: DATE = e.block_timestamp :: DATE
AND C.tx_hash IS NULL
AND d.tx_hash IS NULL
QUALIFY ROW_NUMBER() OVER (PARTITION BY a.tx_hash, a.event_index ORDER BY a.block_number DESC) = 1

View File

@ -0,0 +1,158 @@
version: 2
models:
- name: silver__bridge_layerzero_transfers
description: LayerZero bridge transfers and legacy bridge events for cross-chain asset transfers
config:
contract:
enforced: true
tests:
- dbt_utils.unique_combination_of_columns:
combination_of_columns:
- tx_hash
- event_index
tags: ['test_quality']
columns:
- name: block_number
data_type: NUMBER
description: Block number of the transaction
tests:
- not_null:
tags: ['test_quality']
- name: block_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp of the block
tests:
- not_null:
tags: ['test_quality']
- name: version
data_type: NUMBER
description: Transaction version number
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: tx_hash
data_type: VARCHAR
description: Transaction hash
tests:
- not_null:
tags: ['test_quality']
- name: platform
data_type: VARCHAR
description: Bridge platform name (layerzero)
tests:
- not_null:
tags: ['test_quality']
- accepted_values:
values: ['layerzero']
tags: ['test_quality']
- name: bridge_address
data_type: VARCHAR
description: Address of the bridge contract
tests:
- not_null:
tags: ['test_quality']
- name: event_name
data_type: VARCHAR
description: Name of the bridge event
tests:
- not_null:
tags: ['test_quality']
- name: direction
data_type: VARCHAR
description: Direction of the bridge transfer (inbound/outbound)
tests:
- not_null:
tags: ['test_quality']
- accepted_values:
values: ['inbound', 'outbound', 'unknown']
tags: ['test_quality']
- name: tx_sender
data_type: VARCHAR
description: Transaction sender address
tests:
- not_null:
tags: ['test_quality']
- name: sender
data_type: VARCHAR
description: Bridge transfer sender address
tests:
- not_null:
tags: ['test_quality']
- name: receiver
data_type: VARCHAR
description: Bridge transfer receiver address
- name: source_chain_id
data_type: NUMBER
description: Source chain ID
tests:
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: source_chain_name
data_type: VARCHAR
description: Source chain name
tests:
- not_null:
tags: ['test_quality']
- name: destination_chain_id
data_type: NUMBER
description: Destination chain ID
tests:
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: destination_chain_name
data_type: VARCHAR
description: Destination chain name
tests:
- not_null:
tags: ['test_quality']
- name: token_address
data_type: VARCHAR
description: Token contract address
tests:
- not_null:
tags: ['test_quality']
- name: amount_unadj
data_type: NUMBER
description: Unadjusted amount transferred
tests:
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: event_index
data_type: NUMBER
description: Index of the event within the transaction
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: bridge_layerzero_transfers_id
data_type: VARCHAR
description: Unique identifier for the bridge transfer record
tests:
- unique:
tags: ['test_quality']
- not_null:
tags: ['test_quality']
- name: inserted_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp when the record was inserted
tests:
- not_null:
tags: ['test_quality']
- name: modified_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp when the record was last modified
tests:
- not_null:
tags: ['test_quality']
- name: _invocation_id
data_type: VARCHAR
description: DBT invocation identifier

View File

@ -0,0 +1,127 @@
{{ config(
materialized = 'incremental',
unique_key = "nft_mints_combined_id",
incremental_strategy = 'merge',
cluster_by = ['block_timestamp::DATE','inserted_timestamp::DATE'],
merge_exclude_columns = ["inserted_timestamp"],
post_hook = "ALTER TABLE {{ this }} ADD SEARCH OPTIMIZATION ON EQUALITY(tx_hash, version, nft_from_address, nft_to_address, nft_address);",
tags = ['noncore']
) }}
{% if execute %}
{% if is_incremental() %}
{% set min_bts_query %}
SELECT
MIN(block_timestamp) :: DATE
FROM
{{ ref('silver__nft_mints_v2') }}
WHERE
_inserted_timestamp > (
SELECT
MAX(_inserted_timestamp) modified_timestamp
FROM
{{ this }}
) {% endset %}
{% set min_bts = run_query(min_bts_query) [0] [0] %}
{% if not min_bts or min_bts == 'None' %}
{% set min_bts = '2099-01-01' %}
{% endif %}
{% endif %}
{% endif %}
WITH base AS (
SELECT
*
FROM
{{ ref('silver__nft_mints_v1') }}
{% if is_incremental() %}
WHERE
_inserted_timestamp >= (
SELECT
MAX(_inserted_timestamp)
FROM
{{ this }}
)
{% endif %}
UNION ALL
SELECT
*
FROM
{{ ref('silver__nft_mints_v2') }}
{% if is_incremental() %}
WHERE
_inserted_timestamp >= (
SELECT
MAX(_inserted_timestamp)
FROM
{{ this }}
)
{% endif %}
),
xfers AS (
SELECT
tx_hash,
event_index,
token_address
FROM
{{ ref('silver__transfers') }}
WHERE
success
{% if is_incremental() %}
AND _inserted_timestamp >= (
SELECT
MAX(_inserted_timestamp)
FROM
{{ this }}
)
{% endif %}
UNION ALL
SELECT
tx_hash,
event_index,
metadata_address AS token_address
FROM
{{ ref('silver__transfers_fungible') }}
WHERE
success
{% if is_incremental() %}
AND block_timestamp :: DATE >= '{{ min_bts }}'
{% endif %}
)
SELECT
mints.block_timestamp,
mints.block_number,
mints.version,
mints.tx_hash,
mints.event_index,
mints.event_type,
mints.nft_address,
mints.project_name,
mints.nft_from_address,
mints.nft_to_address,
mints.tokenid,
mints.token_version,
mints.nft_count,
mints.price_raw AS total_price_raw,
transfers.token_address AS currency_address,
{{ dbt_utils.generate_surrogate_key(
['mints.tx_hash','mints.event_index']
) }} AS nft_mints_combined_id,
SYSDATE() AS inserted_timestamp,
SYSDATE() AS modified_timestamp,
'{{ invocation_id }}' AS _invocation_id
FROM
base mints
JOIN xfers transfers
ON mints.tx_hash = transfers.tx_hash qualify ROW_NUMBER() over (
PARTITION BY mints.tx_hash,
mints.event_index
ORDER BY
transfers.event_index DESC
) = 1

View File

@ -0,0 +1,361 @@
{{ config(
materialized = 'incremental',
unique_key = "nft_mints_v1_id",
incremental_strategy = 'merge',
cluster_by = ['block_timestamp::DATE','inserted_timestamp::DATE'],
merge_exclude_columns = ["inserted_timestamp"],
tags = ['noncore']
) }}
WITH evnts AS (
SELECT
block_number,
block_timestamp,
version,
tx_hash,
event_index,
payload_function,
sender,
event_address,
event_resource,
event_data,
event_module,
event_type,
inserted_timestamp
FROM
{{ ref('core__fact_events') }}
WHERE
success
AND event_type IN (
'0x3::token::MintTokenEvent',
'0x3::token::DepositEvent',
'0x1::coin::WithdrawEvent'
)
{% if is_incremental() %}
AND inserted_timestamp >= GREATEST(
(
SELECT
MAX(inserted_timestamp)
FROM
{{ this }}
),
SYSDATE() :: DATE - 3
)
{% endif %}
),
v1_mint_events_raw AS (
SELECT
*,
COUNT(tx_hash) over (
PARTITION BY tx_hash
ORDER BY
tx_hash
) AS mint_count,
'v1' AS token_version,
LAG(
event_index,
1,
0
) over (
PARTITION BY tx_hash
ORDER BY
event_index
) AS prev_event_index,
LEAD(
event_index,
1,
0
) over (
PARTITION BY tx_hash
ORDER BY
event_index
) AS next_event_index_raw
FROM
evnts
WHERE
event_type = '0x3::token::MintTokenEvent'
),
deposit_v1_token_tx AS (
SELECT
*
FROM
evnts
WHERE
tx_hash IN (
SELECT
tx_hash
FROM
v1_mint_events_raw
)
AND event_type = '0x3::token::DepositEvent'
),
coin_withdraw_events_v2 AS (
SELECT
*,
MIN(event_index) over (
PARTITION BY tx_hash
ORDER BY
event_index
) AS min_with_index,
MAX(event_index) over (
PARTITION BY tx_hash
ORDER BY
event_index DESC
) AS max_with_index
FROM
evnts
WHERE
event_type = '0x1::coin::WithdrawEvent'
),
v1_mints_with_nft_address AS (
SELECT
*
FROM
(
SELECT
mint.*,
deposit.event_data :amount :: NUMBER AS nft_count,
deposit.event_data AS deposit_data,
deposit.event_data :id.property_version :: NUMBER AS property_version,
deposit.event_data :id.token_data_id.creator || '::' || deposit.event_data :id.token_data_id.collection || '::' || deposit.event_data :id.token_data_id.name || '::' || deposit.event_data :id.property_version AS nft_address
FROM
v1_mint_events_raw mint
JOIN deposit_v1_token_tx deposit
ON mint.tx_hash = deposit.tx_hash
AND mint.event_data :id.name = deposit.event_data :id.token_data_id.name
) qualify ROW_NUMBER() over (
PARTITION BY tx_hash,
event_index
ORDER BY
property_version DESC
) = 1
),
v1_mints_with_token_info AS (
SELECT
*,
MIN(event_index) over (
PARTITION BY tx_hash
ORDER BY
event_index
) AS min_mint_index,
event_data :id.collection AS project_name,
event_data :id.name AS tokenid
FROM
v1_mints_with_nft_address
),
v1_mints_with_withdrawals AS (
SELECT
*,
(
CASE
WHEN next_event_index_raw = 0 THEN max_with_index
ELSE next_event_index_raw
END
) AS next_event_index
FROM
(
SELECT
main.*,
withdraw.max_with_index
FROM
v1_mints_with_token_info main
JOIN coin_withdraw_events_v2 withdraw
ON main.tx_hash = withdraw.tx_hash
GROUP BY
ALL
)
),
v1_mints_with_price_raw1 AS (
SELECT
* exclude (
next_event_index,
max_with_index
)
FROM
(
SELECT
main.*,
SUM(
withdraw.event_data :amount :: NUMBER
) over (
PARTITION BY main.tx_hash,
main.event_index
ORDER BY
withdraw.event_index
) AS price_raw
FROM
v1_mints_with_withdrawals main
JOIN coin_withdraw_events_v2 withdraw
ON main.tx_hash = withdraw.tx_hash
AND main.event_index < withdraw.event_index
AND main.next_event_index >= withdraw.event_index
AND main.min_mint_index < withdraw.min_with_index
) qualify ROW_NUMBER() over (
PARTITION BY tx_hash,
event_index
ORDER BY
price_raw DESC
) = 1
),
v1_mints_with_price_raw2 AS (
SELECT
* exclude (
next_event_index,
max_with_index
)
FROM
(
SELECT
main.*,
SUM(
withdraw.event_data :amount :: NUMBER
) over (
PARTITION BY main.tx_hash,
main.event_index
ORDER BY
withdraw.event_index
) AS price_raw
FROM
v1_mints_with_withdrawals main
JOIN coin_withdraw_events_v2 withdraw
ON main.tx_hash = withdraw.tx_hash
AND main.event_index > withdraw.event_index
AND main.prev_event_index <= withdraw.event_index
AND main.min_mint_index > withdraw.min_with_index
) qualify ROW_NUMBER() over (
PARTITION BY tx_hash,
event_index
ORDER BY
price_raw DESC
) = 1
),
combined_raw1_raw2 AS (
SELECT
*
FROM
v1_mints_with_price_raw1
UNION
SELECT
*
FROM
v1_mints_with_price_raw2
),
with_tx_count AS (
SELECT
tx_hash,
COUNT(tx_hash) AS with_tx_count
FROM
v1_mints_with_withdrawals
GROUP BY
tx_hash
),
comb_tx_count AS (
SELECT
tx_hash,
COUNT(tx_hash) AS comb_tx_count
FROM
combined_raw1_raw2
GROUP BY
tx_hash
),
tx_count_full AS (
SELECT
withd.*,
comb.comb_tx_count
FROM
with_tx_count withd
JOIN comb_tx_count comb
ON withd.tx_hash = comb.tx_hash
),
v1_shared_mint_cost AS (
SELECT
* exclude (
max_with_index,
next_event_index,
total_amount
)
FROM
(
SELECT
main.*,
SUM(
withdraw.event_data :amount
) over (
PARTITION BY withdraw.tx_hash
ORDER BY
withdraw.tx_hash
) AS total_amount,
total_amount :: NUMBER / main.mint_count AS price_raw
FROM
v1_mints_with_withdrawals main
JOIN coin_withdraw_events_v2 withdraw
ON main.tx_hash = withdraw.tx_hash
WHERE
main.tx_hash IN (
SELECT
tx_hash
FROM
tx_count_full
WHERE
with_tx_count != comb_tx_count
)
)
GROUP BY
ALL
),
fin AS (
SELECT
*
FROM
v1_shared_mint_cost
UNION ALL
SELECT
*
FROM
combined_raw1_raw2
WHERE
tx_hash NOT IN (
SELECT
tx_hash
FROM
v1_shared_mint_cost
)
UNION ALL
SELECT
*,
0 AS price_raw
FROM
v1_mints_with_token_info
WHERE
tx_hash NOT IN (
SELECT
tx_hash
FROM
v1_mints_with_withdrawals
)
)
SELECT
block_timestamp,
block_number,
version,
tx_hash,
event_index,
event_resource AS event_type,
nft_address :: STRING AS nft_address,
project_name :: STRING AS project_name,
event_address AS nft_from_address,
sender AS nft_to_address,
tokenid :: STRING AS tokenid,
token_version,
nft_count,
price_raw,
{{ dbt_utils.generate_surrogate_key(
['tx_hash','event_index']
) }} AS nft_mints_v1_id,
SYSDATE() AS inserted_timestamp,
SYSDATE() AS modified_timestamp,
'{{ invocation_id }}' AS _invocation_id
FROM
fin

View File

@ -0,0 +1,517 @@
{{ config(
materialized = 'incremental',
unique_key = "nft_mints_v2_id",
incremental_strategy = 'merge',
cluster_by = ['block_timestamp::DATE','inserted_timestamp::DATE'],
merge_exclude_columns = ["inserted_timestamp"],
tags = ['noncore']
) }}
{% if execute %}
{% if is_incremental() %}
{% set min_bts_query %}
SELECT
MIN(block_timestamp) :: DATE
FROM
{{ ref('core__fact_events') }}
WHERE
_inserted_timestamp > GREATEST(
(
SELECT
MAX(_inserted_timestamp)
FROM
{{ this }}
),
SYSDATE() :: DATE - 3
) {% endset %}
{% set min_bts = run_query(min_bts_query) [0] [0] %}
{% if not min_bts or min_bts == 'None' %}
{% set min_bts = '2099-01-01' %}
{% endif %}
{% endif %}
{% endif %}
WITH evnts AS (
SELECT
block_number,
block_timestamp,
version,
tx_hash,
event_index,
payload_function,
sender as account_address,
event_address,
event_resource,
event_data,
event_module,
event_type,
inserted_timestamp
FROM
{{ ref('core__fact_events') }}
WHERE
success
AND event_type IN (
'0x4::collection::Mint',
'0x1::coin::WithdrawEvent'
)
{% if is_incremental() %}
AND inserted_timestamp >= GREATEST(
(
SELECT
MAX(inserted_timestamp)
FROM
{{ this }}
),
SYSDATE() :: DATE - 3
)
{% endif %}
),
chngs AS (
SELECT
block_timestamp,
tx_hash,
change_data,
address,
inner_change_type,
change_resource
FROM
{{ ref('core__fact_changes') }}
WHERE
success
AND (
inner_change_type IN (
'0x4::collection::Collection',
'0x4::token::Token'
)
)
{% if is_incremental() %}
AND inserted_timestamp >= GREATEST(
(
SELECT
MAX(inserted_timestamp)
FROM
{{ this }}
),
SYSDATE() :: DATE - 3
)
{% endif %}
),
xfers AS (
SELECT
tx_hash,
account_address,
transfer_event,
token_address
FROM
{{ ref('silver__transfers') }}
WHERE
success
{% if is_incremental() %}
AND inserted_timestamp >= GREATEST(
(
SELECT
MAX(inserted_timestamp)
FROM
{{ this }}
),
SYSDATE() :: DATE - 3
)
{% endif %}
UNION ALL
SELECT
tx_hash,
owner_address AS account_address,
transfer_event,
metadata_address AS token_address
FROM
{{ ref('silver__transfers_fungible') }}
WHERE
success
{% if is_incremental() %}
AND inserted_timestamp >= GREATEST(
(
SELECT
MAX(inserted_timestamp)
FROM
{{ this }}
),
SYSDATE() :: DATE - 3
)
{% endif %}
),
v2_mint_events_raw AS (
SELECT
*,
'v2' AS token_version,
LAG(
event_index,
1,
0
) over (
PARTITION BY tx_hash
ORDER BY
event_index
) AS prev_event_index,
LEAD(
event_index,
1,
0
) over (
PARTITION BY tx_hash
ORDER BY
event_index
) AS next_event_index_raw,
1 AS nft_count,
event_data :token :: STRING AS nft_address,
event_data :collection :: STRING as collection_address
FROM
evnts
WHERE
event_type = '0x4::collection::Mint'
),
mint_collection_names_v2 AS (
SELECT
*
FROM
chngs
WHERE
inner_change_type = '0x4::collection::Collection'
),
mint_token_names_v2 AS (
SELECT
*
FROM
chngs
WHERE
inner_change_type = '0x4::token::Token'
),
v2_mints_with_project_name AS (
SELECT
main.*,
collection.change_data :name AS project_name
FROM
v2_mint_events_raw main
LEFT JOIN mint_collection_names_v2 collection
ON main.tx_hash = collection.tx_hash
AND main.collection_address = collection.address
GROUP BY
ALL
),
v2_mints_with_token_id_raw AS (
SELECT
*
FROM
(
SELECT
main.*,
token.change_data :name AS tokenid,
MIN(
main.event_index
) over (
PARTITION BY main.tx_hash
ORDER BY
main.event_index
) AS min_mint_index
FROM
v2_mints_with_project_name main
JOIN mint_token_names_v2 token
ON main.tx_hash = token.tx_hash
AND main.nft_address = token.address
)
GROUP BY
ALL
),
coin_withdraw_events_v2 AS (
SELECT
*,
MIN(event_index) over (
PARTITION BY tx_hash
ORDER BY
event_index
) AS min_with_index,
MAX(event_index) over (
PARTITION BY tx_hash
ORDER BY
event_index DESC
) AS max_with_index
FROM
evnts
WHERE
event_type = '0x1::coin::WithdrawEvent'
),
v2_mints_with_token_id AS (
SELECT
*,
(
CASE
WHEN next_event_index_raw = 0 THEN max_with_index
ELSE next_event_index_raw
END
) AS next_event_index
FROM
(
SELECT
main.*,
withdraw.max_with_index
FROM
v2_mints_with_token_id_raw main
LEFT JOIN coin_withdraw_events_v2 withdraw
ON main.tx_hash = withdraw.tx_hash
GROUP BY
ALL
)
),
v2_mint_events_with_price AS (
SELECT
*
FROM
(
SELECT
main.*,
withdraw.event_index AS with_ev_index,
withdraw.event_data :amount :: NUMBER AS withdraw_amount
FROM
v2_mint_events_raw main
LEFT JOIN coin_withdraw_events_v2 withdraw
ON main.tx_hash = withdraw.tx_hash
AND main.prev_event_index <= withdraw.event_index
AND main.event_index > withdraw.event_index
)
),
mint_counts_per_tx AS (
SELECT
tx_hash,
COUNT(tx_hash) AS tx_hash_count
FROM
v2_mints_with_token_id
WHERE
tx_hash IN (
SELECT
tx_hash
FROM
xfers
)
GROUP BY
tx_hash
),
withdrawal_counts_per_tx AS (
SELECT
tx_hash,
COUNT(tx_hash) AS tx_hash_count
FROM
coin_withdraw_events_v2
WHERE
tx_hash IN (
SELECT
tx_hash
FROM
xfers
)
GROUP BY
tx_hash
),
joined_mint_with_count AS (
SELECT
mint.tx_hash,
mint.tx_hash_count AS mint_tx_count,
withdrawal.tx_hash_count AS withdrawal_tx_count
FROM
mint_counts_per_tx mint
JOIN withdrawal_counts_per_tx withdrawal
ON mint.tx_hash = withdrawal.tx_hash
ORDER BY
mint_tx_count
),
mints_v2_with_price_raw1 AS (
SELECT
*
FROM
(
SELECT
main.*,
SUM(
withdraw.event_data :amount :: NUMBER
) over (
PARTITION BY main.tx_hash,
main.event_index
ORDER BY
withdraw.event_index
) AS price_raw
FROM
v2_mints_with_token_id main
JOIN coin_withdraw_events_v2 withdraw
ON main.tx_hash = withdraw.tx_hash
AND main.event_index < withdraw.event_index
AND main.next_event_index >= withdraw.event_index
WHERE
main.tx_hash IN (
SELECT
tx_hash
FROM
xfers
)
AND main.tx_hash NOT IN (
SELECT
tx_hash
FROM
joined_mint_with_count
WHERE
withdrawal_tx_count = 1
AND withdrawal_tx_count < mint_tx_count
)
AND main.min_mint_index < withdraw.min_with_index
) qualify ROW_NUMBER() over (
PARTITION BY tx_hash,
event_index
ORDER BY
price_raw DESC
) = 1
),
mints_v2_with_price_raw2 AS (
SELECT
*
FROM
(
SELECT
main.*,
SUM(
withdraw.event_data :amount :: NUMBER
) over (
PARTITION BY main.tx_hash,
main.event_index
ORDER BY
withdraw.event_index
) AS price_raw
FROM
v2_mints_with_token_id main
JOIN coin_withdraw_events_v2 withdraw
ON main.tx_hash = withdraw.tx_hash
AND main.event_index > withdraw.event_index
AND main.prev_event_index <= withdraw.event_index
WHERE
main.tx_hash IN (
SELECT
tx_hash
FROM
xfers
)
AND main.tx_hash NOT IN (
SELECT
tx_hash
FROM
joined_mint_with_count
WHERE
withdrawal_tx_count = 1
AND withdrawal_tx_count < mint_tx_count
)
AND main.min_mint_index > withdraw.min_with_index
) qualify ROW_NUMBER() over (
PARTITION BY tx_hash,
event_index
ORDER BY
price_raw DESC
) = 1
),
mints_v2_with_price_raw3 AS (
SELECT
*
FROM
(
SELECT
main.*,
withdraw.event_data :amount :: NUMBER / tx_count.mint_tx_count AS price_raw
FROM
v2_mints_with_token_id main
JOIN coin_withdraw_events_v2 withdraw
ON main.tx_hash = withdraw.tx_hash
JOIN joined_mint_with_count tx_count
ON main.tx_hash = tx_count.tx_hash
WHERE
main.tx_hash IN (
SELECT
tx_hash
FROM
xfers
)
AND main.tx_hash IN (
SELECT
tx_hash
FROM
joined_mint_with_count
WHERE
withdrawal_tx_count = 1
AND withdrawal_tx_count < mint_tx_count
)
) qualify ROW_NUMBER() over (
PARTITION BY tx_hash,
event_index
ORDER BY
price_raw DESC
) = 1
),
mints_v2_with_price AS (
SELECT
*
FROM
mints_v2_with_price_raw3
UNION
SELECT
*
FROM
mints_v2_with_price_raw2
UNION
SELECT
*
FROM
mints_v2_with_price_raw1
),
mints_v2_with_no_price AS (
SELECT
*,
0 AS price_raw
FROM
v2_mints_with_token_id
WHERE
tx_hash NOT IN (
SELECT
tx_hash
FROM
xfers
)
),
fin AS (
SELECT
*
FROM
mints_v2_with_no_price
UNION ALL
SELECT
*
FROM
mints_v2_with_price
)
SELECT
block_timestamp,
block_number,
version,
tx_hash,
event_index,
event_resource AS event_type,
nft_address :: STRING AS nft_address,
project_name :: STRING AS project_name,
event_address AS nft_from_address,
account_address AS nft_to_address,
tokenid :: STRING AS tokenid,
token_version,
nft_count,
price_raw,
{{ dbt_utils.generate_surrogate_key(
['tx_hash','event_index']
) }} AS nft_mints_v2_id,
SYSDATE() AS inserted_timestamp,
SYSDATE() AS modified_timestamp,
'{{ invocation_id }}' AS _invocation_id
FROM
fin

View File

@ -0,0 +1,123 @@
{{ config(
materialized = 'incremental',
unique_key = "nft_sales_tradeport_id",
incremental_strategy = 'merge',
cluster_by = ['block_timestamp::DATE','inserted_timestamp::DATE'],
merge_exclude_columns = ["inserted_timestamp"],
tags = ['noncore']
) }}
WITH events AS (
SELECT
block_number,
block_timestamp,
version,
tx_hash,
event_index,
payload_function,
account_address,
event_address,
event_resource,
event_data,
event_module,
event_type,
CASE
WHEN event_resource = 'BuyEvent' THEN 'sale'
WHEN event_resource IN (
'AcceptCollectionBidEvent',
'AcceptTokenBidEvent'
) THEN 'bid_won'
END AS event_kind,
inserted_timestamp
FROM
{{ ref('core__fact_events') }}
WHERE
event_address = '0xf81bea5757d1ff70b441b1ec64db62436df5f451cde6eab81aec489791f22aa0'
AND event_resource IN (
'BuyEvent',
'AcceptCollectionBidEvent',
'AcceptTokenBidEvent'
)
AND success
{% if is_incremental() %}
AND inserted_timestamp >= GREATEST(
(
SELECT
MAX(inserted_timestamp)
FROM
{{ this }}
),
SYSDATE() :: DATE - 3
)
{% endif %}
),
chngs AS (
SELECT
block_timestamp,
tx_hash,
change_data,
address,
inner_change_type,
change_resource
FROM
{{ ref('core__fact_changes') }}
WHERE
success
AND inner_change_type = '0x4::collection::Collection'
{% if is_incremental() %}
AND inserted_timestamp >= GREATEST(
(
SELECT
MAX(inserted_timestamp)
FROM
{{ this }}
),
SYSDATE() :: DATE - 3
)
{% endif %}
),
nft_collection_lookup AS (
SELECT DISTINCT
nft_address,
project_name
FROM
{{ ref('silver__nft_mints_combined') }}
WHERE
project_name IS NOT NULL
),
sales_with_nft_info AS (
SELECT
*,
event_data :token :inner :: STRING AS nft_address
FROM
events
)
SELECT
main.block_timestamp,
main.block_number,
main.version,
main.tx_hash,
main.event_index,
main.event_kind AS event_type,
COALESCE(main.event_data :buyer, main.event_data :bid_buyer) :: STRING AS buyer_address,
COALESCE(main.event_data :seller, main.event_data :bid_seller) :: STRING AS seller_address,
main.nft_address,
lookup.project_name,
'v2' AS token_version,
main.event_address AS platform_address,
'Tradeport' AS platform_name,
'tradeport_marketplace_token_v1' AS platform_exchange_version,
main.event_data :price :: NUMBER AS total_price_raw,
{{ dbt_utils.generate_surrogate_key(
['main.tx_hash','main.event_index']
) }} AS nft_sales_tradeport_id,
SYSDATE() AS inserted_timestamp,
SYSDATE() AS modified_timestamp,
'{{ invocation_id }}' AS _invocation_id
FROM
sales_with_nft_info main
LEFT JOIN nft_collection_lookup lookup
ON main.nft_address = lookup.nft_address

View File

@ -0,0 +1,545 @@
version: 2
models:
- name: silver__nft_mints_v1
description: NFT mints for v1 token standard using MintTokenEvent and DepositEvent
config:
contract:
enforced: true
tests:
- dbt_utils.unique_combination_of_columns:
combination_of_columns:
- tx_hash
- event_index
tags: ['test_quality']
columns:
- name: block_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp of the block
tests:
- not_null:
tags: ['test_quality']
- name: block_number
data_type: NUMBER
description: Block number of the transaction
tests:
- not_null:
tags: ['test_quality']
- name: version
data_type: NUMBER
description: Transaction version number
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: tx_hash
data_type: VARCHAR
description: Transaction hash
tests:
- not_null:
tags: ['test_quality']
- name: event_index
data_type: NUMBER
description: Index of the event within the transaction
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: event_type
data_type: VARCHAR
description: Type of the mint event
tests:
- not_null:
tags: ['test_quality']
- name: nft_address
data_type: VARCHAR
description: NFT token address
tests:
- not_null:
tags: ['test_quality']
- name: project_name
data_type: VARCHAR
description: Name of the NFT project/collection
tests:
- not_null:
tags: ['test_quality']
- name: nft_from_address
data_type: VARCHAR
description: Address that minted the NFT (contract address)
tests:
- not_null:
tags: ['test_quality']
- name: nft_to_address
data_type: VARCHAR
description: Address that received the minted NFT
tests:
- not_null:
tags: ['test_quality']
- name: tokenid
data_type: VARCHAR
description: Token ID of the minted NFT
tests:
- not_null:
tags: ['test_quality']
- name: token_version
data_type: VARCHAR
description: Token standard version (v1)
tests:
- not_null:
tags: ['test_quality']
- accepted_values:
values: ['v1']
tags: ['test_quality']
- name: nft_count
data_type: NUMBER
description: Number of NFTs minted
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: "> 0"
tags: ['test_quality']
- name: price_raw
data_type: NUMBER
description: Raw price paid for minting
tests:
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: nft_mints_v1_id
data_type: VARCHAR
description: Unique identifier for the v1 mint record
tests:
- unique:
tags: ['test_quality']
- not_null:
tags: ['test_quality']
- name: inserted_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp when the record was inserted
tests:
- not_null:
tags: ['test_quality']
- name: modified_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp when the record was last modified
tests:
- not_null:
tags: ['test_quality']
- name: _invocation_id
data_type: VARCHAR
description: DBT invocation identifier
- name: silver__nft_mints_v2
description: NFT mints for v2 token standard using MintTokenEvent and DepositEvent
config:
contract:
enforced: true
tests:
- dbt_utils.unique_combination_of_columns:
combination_of_columns:
- tx_hash
- event_index
tags: ['test_quality']
columns:
- name: block_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp of the block
tests:
- not_null:
tags: ['test_quality']
- name: block_number
data_type: NUMBER
description: Block number of the transaction
tests:
- not_null:
tags: ['test_quality']
- name: version
data_type: NUMBER
description: Transaction version number
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: tx_hash
data_type: VARCHAR
description: Transaction hash
tests:
- not_null:
tags: ['test_quality']
- name: event_index
data_type: NUMBER
description: Index of the event within the transaction
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: event_type
data_type: VARCHAR
description: Type of the mint event
tests:
- not_null:
tags: ['test_quality']
- name: nft_address
data_type: VARCHAR
description: NFT token address
tests:
- not_null:
tags: ['test_quality']
- name: project_name
data_type: VARCHAR
description: Name of the NFT project/collection
tests:
- not_null:
tags: ['test_quality']
- name: nft_from_address
data_type: VARCHAR
description: Address that minted the NFT (contract address)
tests:
- not_null:
tags: ['test_quality']
- name: nft_to_address
data_type: VARCHAR
description: Address that received the minted NFT
tests:
- not_null:
tags: ['test_quality']
- name: tokenid
data_type: VARCHAR
description: Token ID of the minted NFT
tests:
- not_null:
tags: ['test_quality']
- name: token_version
data_type: VARCHAR
description: Token standard version (v2)
tests:
- not_null:
tags: ['test_quality']
- accepted_values:
values: ['v2']
tags: ['test_quality']
- name: nft_count
data_type: NUMBER
description: Number of NFTs minted
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: "> 0"
tags: ['test_quality']
- name: price_raw
data_type: NUMBER
description: Raw price paid for minting
tests:
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: nft_mints_v2_id
data_type: VARCHAR
description: Unique identifier for the v2 mint record
tests:
- unique:
tags: ['test_quality']
- not_null:
tags: ['test_quality']
- name: inserted_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp when the record was inserted
tests:
- not_null:
tags: ['test_quality']
- name: modified_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp when the record was last modified
tests:
- not_null:
tags: ['test_quality']
- name: _invocation_id
data_type: VARCHAR
description: DBT invocation identifier
- name: silver__nft_mints_combined
description: Combined NFT mints from both v1 and v2 standards with currency address information
config:
contract:
enforced: true
tests:
- dbt_utils.unique_combination_of_columns:
combination_of_columns:
- tx_hash
- event_index
tags: ['test_quality']
columns:
- name: block_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp of the block
tests:
- not_null:
tags: ['test_quality']
- name: block_number
data_type: NUMBER
description: Block number of the transaction
tests:
- not_null:
tags: ['test_quality']
- name: version
data_type: NUMBER
description: Transaction version number
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: tx_hash
data_type: VARCHAR
description: Transaction hash
tests:
- not_null:
tags: ['test_quality']
- name: event_index
data_type: NUMBER
description: Index of the event within the transaction
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: event_type
data_type: VARCHAR
description: Type of the mint event
tests:
- not_null:
tags: ['test_quality']
- name: nft_address
data_type: VARCHAR
description: NFT token address
tests:
- not_null:
tags: ['test_quality']
- name: project_name
data_type: VARCHAR
description: Name of the NFT project/collection
tests:
- not_null:
tags: ['test_quality']
- name: nft_from_address
data_type: VARCHAR
description: Address that minted the NFT (contract address)
tests:
- not_null:
tags: ['test_quality']
- name: nft_to_address
data_type: VARCHAR
description: Address that received the minted NFT
tests:
- not_null:
tags: ['test_quality']
- name: tokenid
data_type: VARCHAR
description: Token ID of the minted NFT
tests:
- not_null:
tags: ['test_quality']
- name: token_version
data_type: VARCHAR
description: Token standard version (v1 or v2)
tests:
- not_null:
tags: ['test_quality']
- accepted_values:
values: ['v1', 'v2']
tags: ['test_quality']
- name: nft_count
data_type: NUMBER
description: Number of NFTs minted
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: "> 0"
tags: ['test_quality']
- name: total_price_raw
data_type: NUMBER
description: Total raw price paid for minting
tests:
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: currency_address
data_type: VARCHAR
description: Address of the currency/token used for payment
- name: nft_mints_combined_id
data_type: VARCHAR
description: Unique identifier for the combined mint record
tests:
- unique:
tags: ['test_quality']
- not_null:
tags: ['test_quality']
- name: inserted_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp when the record was inserted
tests:
- not_null:
tags: ['test_quality']
- name: modified_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp when the record was last modified
tests:
- not_null:
tags: ['test_quality']
- name: _invocation_id
data_type: VARCHAR
description: DBT invocation identifier
- name: silver__nft_sales_tradeport
description: NFT sales events from the Tradeport marketplace platform
config:
contract:
enforced: true
tests:
- dbt_utils.unique_combination_of_columns:
combination_of_columns:
- tx_hash
- event_index
tags: ['test_quality']
columns:
- name: block_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp of the block
tests:
- not_null:
tags: ['test_quality']
- name: block_number
data_type: NUMBER
description: Block number of the transaction
tests:
- not_null:
tags: ['test_quality']
- name: version
data_type: NUMBER
description: Transaction version number
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: tx_hash
data_type: VARCHAR
description: Transaction hash
tests:
- not_null:
tags: ['test_quality']
- name: event_index
data_type: NUMBER
description: Index of the event within the transaction
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: ">= 0"
tags: ['test_quality']
- name: event_type
data_type: VARCHAR
description: Type of sale event (sale, bid_won)
tests:
- not_null:
tags: ['test_quality']
- accepted_values:
values: ['sale', 'bid_won']
tags: ['test_quality']
- name: buyer_address
data_type: VARCHAR
description: Address of the NFT buyer
tests:
- not_null:
tags: ['test_quality']
- name: seller_address
data_type: VARCHAR
description: Address of the NFT seller
tests:
- not_null:
tags: ['test_quality']
- name: nft_address
data_type: VARCHAR
description: NFT token address
tests:
- not_null:
tags: ['test_quality']
- name: project_name
data_type: VARCHAR
description: Name of the NFT project/collection
- name: token_version
data_type: VARCHAR
description: Token standard version (v2)
tests:
- not_null:
tags: ['test_quality']
- accepted_values:
values: ['v2']
tags: ['test_quality']
- name: platform_address
data_type: VARCHAR
description: Address of the marketplace platform
tests:
- not_null:
tags: ['test_quality']
- name: platform_name
data_type: VARCHAR
description: Name of the marketplace platform
tests:
- not_null:
tags: ['test_quality']
- accepted_values:
values: ['Tradeport']
tags: ['test_quality']
- name: platform_exchange_version
data_type: VARCHAR
description: Version of the marketplace exchange
tests:
- not_null:
tags: ['test_quality']
- name: total_price_raw
data_type: NUMBER
description: Total raw price of the sale
tests:
- not_null:
tags: ['test_quality']
- dbt_utils.expression_is_true:
expression: "> 0"
tags: ['test_quality']
- name: nft_sales_tradeport_id
data_type: VARCHAR
description: Unique identifier for the Tradeport sale record
tests:
- unique:
tags: ['test_quality']
- not_null:
tags: ['test_quality']
- name: inserted_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp when the record was inserted
tests:
- not_null:
tags: ['test_quality']
- name: modified_timestamp
data_type: TIMESTAMP_NTZ
description: Timestamp when the record was last modified
tests:
- not_null:
tags: ['test_quality']
- name: _invocation_id
data_type: VARCHAR
description: DBT invocation identifier

View File

@ -7,6 +7,7 @@ sources:
- name: dim_date_hours
- name: address_tags
- name: dim_dates
- name: dim_labels
- name: crosschain_silver
database: "{{ 'crosschain' if target.database == 'MOVEMENT' else 'crosschain_dev' }}"
schema: silver