From 550fcb16acbb23e5faf3010f84fb1678969693fc Mon Sep 17 00:00:00 2001 From: Mike Stepanovic Date: Thu, 21 Aug 2025 13:59:20 -0600 Subject: [PATCH] add nft and defi curation, with tests and docs --- models/descriptions/__overview__.md | 48 +- models/descriptions/address_name.md | 5 + models/descriptions/amount_unadj.md | 5 + models/descriptions/buyer_address.md | 5 + models/descriptions/currency_address.md | 5 + models/descriptions/direction.md | 5 + models/descriptions/event_name.md | 5 + models/descriptions/nft_address.md | 5 + models/descriptions/nft_count.md | 5 + models/descriptions/nft_from_address.md | 5 + models/descriptions/nft_to_address.md | 5 + models/descriptions/platform_address.md | 5 + .../descriptions/platform_exchange_version.md | 5 + models/descriptions/platform_name.md | 5 + models/descriptions/project_name.md | 5 + models/descriptions/seller_address.md | 5 + .../descriptions/tables/core__dim_labels.md | 5 + .../tables/defi__fact_bridge_activity.md | 5 + .../tables/nft__fact_nft_mints.md | 5 + .../tables/nft__fact_nft_sales.md | 5 + models/descriptions/token_version.md | 5 + models/descriptions/tokenid.md | 5 + models/descriptions/total_price_raw.md | 5 + models/descriptions/tx_sender.md | 5 + models/gold/core/core__dim_labels.sql | 36 ++ models/gold/core/gold_core.yml | 31 + .../gold/defi/defi__fact_bridge_activity.sql | 79 +++ models/gold/defi/gold_defi.yml | 54 ++ models/gold/nft/gold_nft.yml | 95 +++ models/gold/nft/nft__fact_nft_mints.sql | 43 ++ models/gold/nft/nft__fact_nft_sales.sql | 40 ++ models/silver/core/silver__labels.sql | 23 + models/silver/core/silver_core.yml | 60 +- .../silver__bridge_layerzero_transfers.sql | 238 ++++++++ models/silver/defi/silver_defi.yml | 158 +++++ .../nft/mints/silver__nft_mints_combined.sql | 127 ++++ .../silver/nft/mints/silver__nft_mints_v1.sql | 361 ++++++++++++ .../silver/nft/mints/silver__nft_mints_v2.sql | 517 +++++++++++++++++ .../nft/sales/silver__nft_sales_tradeport.sql | 123 ++++ models/silver/nft/silver_nft.yml | 545 ++++++++++++++++++ models/sources.yml | 1 + 41 files changed, 2679 insertions(+), 15 deletions(-) create mode 100644 models/descriptions/address_name.md create mode 100644 models/descriptions/amount_unadj.md create mode 100644 models/descriptions/buyer_address.md create mode 100644 models/descriptions/currency_address.md create mode 100644 models/descriptions/direction.md create mode 100644 models/descriptions/event_name.md create mode 100644 models/descriptions/nft_address.md create mode 100644 models/descriptions/nft_count.md create mode 100644 models/descriptions/nft_from_address.md create mode 100644 models/descriptions/nft_to_address.md create mode 100644 models/descriptions/platform_address.md create mode 100644 models/descriptions/platform_exchange_version.md create mode 100644 models/descriptions/platform_name.md create mode 100644 models/descriptions/project_name.md create mode 100644 models/descriptions/seller_address.md create mode 100644 models/descriptions/tables/core__dim_labels.md create mode 100644 models/descriptions/tables/defi__fact_bridge_activity.md create mode 100644 models/descriptions/tables/nft__fact_nft_mints.md create mode 100644 models/descriptions/tables/nft__fact_nft_sales.md create mode 100644 models/descriptions/token_version.md create mode 100644 models/descriptions/tokenid.md create mode 100644 models/descriptions/total_price_raw.md create mode 100644 models/descriptions/tx_sender.md create mode 100644 models/gold/core/core__dim_labels.sql create mode 100644 models/gold/defi/defi__fact_bridge_activity.sql create mode 100644 models/gold/defi/gold_defi.yml create mode 100644 models/gold/nft/gold_nft.yml create mode 100644 models/gold/nft/nft__fact_nft_mints.sql create mode 100644 models/gold/nft/nft__fact_nft_sales.sql create mode 100644 models/silver/core/silver__labels.sql create mode 100644 models/silver/defi/silver__bridge_layerzero_transfers.sql create mode 100644 models/silver/defi/silver_defi.yml create mode 100644 models/silver/nft/mints/silver__nft_mints_combined.sql create mode 100644 models/silver/nft/mints/silver__nft_mints_v1.sql create mode 100644 models/silver/nft/mints/silver__nft_mints_v2.sql create mode 100644 models/silver/nft/sales/silver__nft_sales_tradeport.sql create mode 100644 models/silver/nft/silver_nft.yml diff --git a/models/descriptions/__overview__.md b/models/descriptions/__overview__.md index 818a2f8..2d2bb5b 100644 --- a/models/descriptions/__overview__.md +++ b/models/descriptions/__overview__.md @@ -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`.``) -- [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`.``) + +**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`.``) **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`.``) +### DeFi Tables (`movement`.`DEFI`.``) -- [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`.``) + +**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`.``) + +**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).** diff --git a/models/descriptions/address_name.md b/models/descriptions/address_name.md new file mode 100644 index 0000000..e957961 --- /dev/null +++ b/models/descriptions/address_name.md @@ -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 %} diff --git a/models/descriptions/amount_unadj.md b/models/descriptions/amount_unadj.md new file mode 100644 index 0000000..6afa621 --- /dev/null +++ b/models/descriptions/amount_unadj.md @@ -0,0 +1,5 @@ +{% docs amount_unadj %} + +The raw, unadjusted amount of tokens or assets involved in the transaction, before any decimal adjustment. + +{% enddocs %} diff --git a/models/descriptions/buyer_address.md b/models/descriptions/buyer_address.md new file mode 100644 index 0000000..7798531 --- /dev/null +++ b/models/descriptions/buyer_address.md @@ -0,0 +1,5 @@ +{% docs buyer_address %} + +The address of the buyer in an NFT sale transaction. + +{% enddocs %} diff --git a/models/descriptions/currency_address.md b/models/descriptions/currency_address.md new file mode 100644 index 0000000..4b5038c --- /dev/null +++ b/models/descriptions/currency_address.md @@ -0,0 +1,5 @@ +{% docs currency_address %} + +The contract address of the currency or token used for payment in the transaction. + +{% enddocs %} diff --git a/models/descriptions/direction.md b/models/descriptions/direction.md new file mode 100644 index 0000000..cc812b9 --- /dev/null +++ b/models/descriptions/direction.md @@ -0,0 +1,5 @@ +{% docs direction %} + +The direction of the bridge transaction, typically indicating whether assets are being deposited or withdrawn. + +{% enddocs %} diff --git a/models/descriptions/event_name.md b/models/descriptions/event_name.md new file mode 100644 index 0000000..18df55a --- /dev/null +++ b/models/descriptions/event_name.md @@ -0,0 +1,5 @@ +{% docs event_name %} + +The name of the specific event being processed or emitted by the smart contract. + +{% enddocs %} diff --git a/models/descriptions/nft_address.md b/models/descriptions/nft_address.md new file mode 100644 index 0000000..de92024 --- /dev/null +++ b/models/descriptions/nft_address.md @@ -0,0 +1,5 @@ +{% docs nft_address %} + +The contract address of the NFT collection or smart contract. + +{% enddocs %} diff --git a/models/descriptions/nft_count.md b/models/descriptions/nft_count.md new file mode 100644 index 0000000..01dbd0c --- /dev/null +++ b/models/descriptions/nft_count.md @@ -0,0 +1,5 @@ +{% docs nft_count %} + +The number of NFT tokens involved in the transaction, typically 1 for unique NFTs. + +{% enddocs %} diff --git a/models/descriptions/nft_from_address.md b/models/descriptions/nft_from_address.md new file mode 100644 index 0000000..0ec1a74 --- /dev/null +++ b/models/descriptions/nft_from_address.md @@ -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 %} diff --git a/models/descriptions/nft_to_address.md b/models/descriptions/nft_to_address.md new file mode 100644 index 0000000..6edb853 --- /dev/null +++ b/models/descriptions/nft_to_address.md @@ -0,0 +1,5 @@ +{% docs nft_to_address %} + +The address to which the NFT is being transferred or minted. + +{% enddocs %} diff --git a/models/descriptions/platform_address.md b/models/descriptions/platform_address.md new file mode 100644 index 0000000..05561c5 --- /dev/null +++ b/models/descriptions/platform_address.md @@ -0,0 +1,5 @@ +{% docs platform_address %} + +The contract address of the marketplace or platform facilitating the NFT transaction. + +{% enddocs %} diff --git a/models/descriptions/platform_exchange_version.md b/models/descriptions/platform_exchange_version.md new file mode 100644 index 0000000..3ec075e --- /dev/null +++ b/models/descriptions/platform_exchange_version.md @@ -0,0 +1,5 @@ +{% docs platform_exchange_version %} + +The version of the exchange or marketplace contract being used for the transaction. + +{% enddocs %} diff --git a/models/descriptions/platform_name.md b/models/descriptions/platform_name.md new file mode 100644 index 0000000..df8990d --- /dev/null +++ b/models/descriptions/platform_name.md @@ -0,0 +1,5 @@ +{% docs platform_name %} + +The name of the marketplace or platform facilitating the NFT transaction. + +{% enddocs %} diff --git a/models/descriptions/project_name.md b/models/descriptions/project_name.md new file mode 100644 index 0000000..3016c5f --- /dev/null +++ b/models/descriptions/project_name.md @@ -0,0 +1,5 @@ +{% docs project_name %} + +The name of the project, protocol, or application associated with the transaction or event. + +{% enddocs %} diff --git a/models/descriptions/seller_address.md b/models/descriptions/seller_address.md new file mode 100644 index 0000000..ed59485 --- /dev/null +++ b/models/descriptions/seller_address.md @@ -0,0 +1,5 @@ +{% docs seller_address %} + +The address of the seller in an NFT sale transaction. + +{% enddocs %} diff --git a/models/descriptions/tables/core__dim_labels.md b/models/descriptions/tables/core__dim_labels.md new file mode 100644 index 0000000..e6277c1 --- /dev/null +++ b/models/descriptions/tables/core__dim_labels.md @@ -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 %} diff --git a/models/descriptions/tables/defi__fact_bridge_activity.md b/models/descriptions/tables/defi__fact_bridge_activity.md new file mode 100644 index 0000000..51ae963 --- /dev/null +++ b/models/descriptions/tables/defi__fact_bridge_activity.md @@ -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 %} diff --git a/models/descriptions/tables/nft__fact_nft_mints.md b/models/descriptions/tables/nft__fact_nft_mints.md new file mode 100644 index 0000000..4682e46 --- /dev/null +++ b/models/descriptions/tables/nft__fact_nft_mints.md @@ -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 %} diff --git a/models/descriptions/tables/nft__fact_nft_sales.md b/models/descriptions/tables/nft__fact_nft_sales.md new file mode 100644 index 0000000..a3479a4 --- /dev/null +++ b/models/descriptions/tables/nft__fact_nft_sales.md @@ -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 %} diff --git a/models/descriptions/token_version.md b/models/descriptions/token_version.md new file mode 100644 index 0000000..937ff8b --- /dev/null +++ b/models/descriptions/token_version.md @@ -0,0 +1,5 @@ +{% docs token_version %} + +The version of the token standard being used (e.g., v1, v2) for the NFT contract. + +{% enddocs %} diff --git a/models/descriptions/tokenid.md b/models/descriptions/tokenid.md new file mode 100644 index 0000000..533cd2c --- /dev/null +++ b/models/descriptions/tokenid.md @@ -0,0 +1,5 @@ +{% docs tokenid %} + +The unique identifier of the specific NFT token within the collection. + +{% enddocs %} diff --git a/models/descriptions/total_price_raw.md b/models/descriptions/total_price_raw.md new file mode 100644 index 0000000..65463f2 --- /dev/null +++ b/models/descriptions/total_price_raw.md @@ -0,0 +1,5 @@ +{% docs total_price_raw %} + +The raw total price paid for the NFT transaction, before any decimal adjustments. + +{% enddocs %} diff --git a/models/descriptions/tx_sender.md b/models/descriptions/tx_sender.md new file mode 100644 index 0000000..98157a9 --- /dev/null +++ b/models/descriptions/tx_sender.md @@ -0,0 +1,5 @@ +{% docs tx_sender %} + +The address that sent or initiated the transaction. + +{% enddocs %} diff --git a/models/gold/core/core__dim_labels.sql b/models/gold/core/core__dim_labels.sql new file mode 100644 index 0000000..ef1a86b --- /dev/null +++ b/models/gold/core/core__dim_labels.sql @@ -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 %} \ No newline at end of file diff --git a/models/gold/core/gold_core.yml b/models/gold/core/gold_core.yml index ad13abf..d911e74 100644 --- a/models/gold/core/gold_core.yml +++ b/models/gold/core/gold_core.yml @@ -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: diff --git a/models/gold/defi/defi__fact_bridge_activity.sql b/models/gold/defi/defi__fact_bridge_activity.sql new file mode 100644 index 0000000..0b5d762 --- /dev/null +++ b/models/gold/defi/defi__fact_bridge_activity.sql @@ -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 %} \ No newline at end of file diff --git a/models/gold/defi/gold_defi.yml b/models/gold/defi/gold_defi.yml new file mode 100644 index 0000000..16fddc9 --- /dev/null +++ b/models/gold/defi/gold_defi.yml @@ -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") }}' diff --git a/models/gold/nft/gold_nft.yml b/models/gold/nft/gold_nft.yml new file mode 100644 index 0000000..b8f2654 --- /dev/null +++ b/models/gold/nft/gold_nft.yml @@ -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") }}' diff --git a/models/gold/nft/nft__fact_nft_mints.sql b/models/gold/nft/nft__fact_nft_mints.sql new file mode 100644 index 0000000..2905170 --- /dev/null +++ b/models/gold/nft/nft__fact_nft_mints.sql @@ -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 %} \ No newline at end of file diff --git a/models/gold/nft/nft__fact_nft_sales.sql b/models/gold/nft/nft__fact_nft_sales.sql new file mode 100644 index 0000000..904cf28 --- /dev/null +++ b/models/gold/nft/nft__fact_nft_sales.sql @@ -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 %} \ No newline at end of file diff --git a/models/silver/core/silver__labels.sql b/models/silver/core/silver__labels.sql new file mode 100644 index 0000000..cb38110 --- /dev/null +++ b/models/silver/core/silver__labels.sql @@ -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' \ No newline at end of file diff --git a/models/silver/core/silver_core.yml b/models/silver/core/silver_core.yml index 593f547..bd982d0 100644 --- a/models/silver/core/silver_core.yml +++ b/models/silver/core/silver_core.yml @@ -520,4 +520,62 @@ models: - name: modified_timestamp data_type: TIMESTAMP_NTZ - name: _invocation_id - data_type: VARCHAR \ No newline at end of file + 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'] \ No newline at end of file diff --git a/models/silver/defi/silver__bridge_layerzero_transfers.sql b/models/silver/defi/silver__bridge_layerzero_transfers.sql new file mode 100644 index 0000000..7deb01b --- /dev/null +++ b/models/silver/defi/silver__bridge_layerzero_transfers.sql @@ -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 \ No newline at end of file diff --git a/models/silver/defi/silver_defi.yml b/models/silver/defi/silver_defi.yml new file mode 100644 index 0000000..330e8ba --- /dev/null +++ b/models/silver/defi/silver_defi.yml @@ -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 diff --git a/models/silver/nft/mints/silver__nft_mints_combined.sql b/models/silver/nft/mints/silver__nft_mints_combined.sql new file mode 100644 index 0000000..f063115 --- /dev/null +++ b/models/silver/nft/mints/silver__nft_mints_combined.sql @@ -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 \ No newline at end of file diff --git a/models/silver/nft/mints/silver__nft_mints_v1.sql b/models/silver/nft/mints/silver__nft_mints_v1.sql new file mode 100644 index 0000000..89bd64f --- /dev/null +++ b/models/silver/nft/mints/silver__nft_mints_v1.sql @@ -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 \ No newline at end of file diff --git a/models/silver/nft/mints/silver__nft_mints_v2.sql b/models/silver/nft/mints/silver__nft_mints_v2.sql new file mode 100644 index 0000000..3ff9e1f --- /dev/null +++ b/models/silver/nft/mints/silver__nft_mints_v2.sql @@ -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 \ No newline at end of file diff --git a/models/silver/nft/sales/silver__nft_sales_tradeport.sql b/models/silver/nft/sales/silver__nft_sales_tradeport.sql new file mode 100644 index 0000000..a64f96a --- /dev/null +++ b/models/silver/nft/sales/silver__nft_sales_tradeport.sql @@ -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 \ No newline at end of file diff --git a/models/silver/nft/silver_nft.yml b/models/silver/nft/silver_nft.yml new file mode 100644 index 0000000..a2f45a8 --- /dev/null +++ b/models/silver/nft/silver_nft.yml @@ -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 diff --git a/models/sources.yml b/models/sources.yml index 8e809e3..fa0ac32 100644 --- a/models/sources.yml +++ b/models/sources.yml @@ -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