diff --git a/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_eth_transfers.sql b/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_eth_transfers.sql new file mode 100644 index 00000000000..198d83b0a05 --- /dev/null +++ b/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_eth_transfers.sql @@ -0,0 +1,83 @@ +{{ + config( + materialized='incremental', + schema = 'safe_linea', + alias= 'eth_transfers', + partition_by = ['block_month'], + unique_key = ['block_date', 'address', 'tx_hash', 'trace_address'], + on_schema_change='fail', + file_format ='delta', + incremental_strategy='merge', + post_hook = '{{ expose_spells( + blockchains = \'["linea"]\', + spell_type = "project", + spell_name = "safe", + contributors = \'["danielpartida"]\') }}' + ) +}} + +{% set project_start_date = '2023-07-11' %} + +select + t.*, + p.price * t.amount_raw / 1e18 AS amount_usd + +from ( + + select + 'linea' as blockchain, + 'ETH' as symbol, + s.address, + try_cast(date_trunc('day', et.block_time) as date) as block_date, + CAST(date_trunc('month', et.block_time) as DATE) as block_month, + et.block_time, + -CAST(et.value AS INT256) as amount_raw, + et.tx_hash, + array_join(et.trace_address, ',') as trace_address + from {{ source('linea', 'traces') }} et + join {{ ref('safe_linea_safes') }} s on et."from" = s.address + and et."from" != et.to -- exclude calls to self to guarantee unique key property + and et.success = true + and (lower(et.call_type) not in ('delegatecall', 'callcode', 'staticcall') or et.call_type is null) + and et.value > UINT256 '0' -- et.value is uint256 type + {% if not is_incremental() %} + where et.block_time > timestamp '{{project_start_date}}' -- for initial query optimisation + {% else %} + -- to prevent potential counterfactual safe deployment issues we take a bigger interval + where et.block_time > date_trunc('day', now() - interval '10' day) + {% endif %} + + union all + + select + 'linea' as blockchain, + 'ETH' as symbol, + s.address, + try_cast(date_trunc('day', et.block_time) as date) as block_date, + CAST(date_trunc('month', et.block_time) as DATE) as block_month, + et.block_time, + CAST(et.value AS INT256) as amount_raw, + et.tx_hash, + array_join(et.trace_address, ',') as trace_address + from {{ source('linea', 'traces') }} et + join {{ ref('safe_linea_safes') }} s on et.to = s.address + and et."from" != et.to -- exclude calls to self to guarantee unique key property + and et.success = true + and (lower(et.call_type) not in ('delegatecall', 'callcode', 'staticcall') or et.call_type is null) + and et.value > UINT256 '0' -- et.value is uint256 type + {% if not is_incremental() %} + where et.block_time > timestamp '{{project_start_date}}' -- for initial query optimisation + {% endif %} + {% if is_incremental() %} + -- to prevent potential counterfactual safe deployment issues we take a bigger interval + where et.block_time > date_trunc('day', now() - interval '10' day) + {% endif %} +) t + +left join {{ source('prices', 'usd') }} p on p.blockchain is null + and p.symbol = t.symbol + and p.minute = date_trunc('minute', t.block_time) + {% if is_incremental() %} + -- to prevent potential counterfactual safe deployment issues we take a bigger interval + and p.minute > date_trunc('day', now() - interval '10' day) + {% endif %} \ No newline at end of file diff --git a/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_safes.sql b/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_safes.sql new file mode 100644 index 00000000000..ff805dcb211 --- /dev/null +++ b/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_safes.sql @@ -0,0 +1,48 @@ +{{ + config( + materialized='incremental', + schema = 'safe_linea', + alias= 'safes', + partition_by = ['block_month'], + unique_key = ['block_date', 'address'], + on_schema_change='fail', + file_format ='delta', + incremental_strategy='merge', + post_hook = '{{ expose_spells( + blockchains = \'["linea"]\', + spell_type = "project", + spell_name = "safe", + contributors = \'["danielpartida"]\') }}' + ) +}} + +{% set project_start_date = '2024-02-24' %} + +select + 'linea' as blockchain, + et."from" as address, + case + when et.to = 0xd9db270c1b5e3bd161e8c8503c55ceabee709552 then '1.3.0' + when et.to = 0x3e5c63644e683549055b9be8653de26e0b4cd36e then '1.3.0L2' + when et.to = 0x41675C099F32341bf84BFc5382aF534df5C7461a then '1.4.1' + when et.to = 0x29fcB43b46531BcA003ddC8FCB67FFE91900C762 then '1.4.1L2' + else 'unknown' + end as creation_version, + try_cast(date_trunc('day', et.block_time) as date) as block_date, + CAST(date_trunc('month', et.block_time) as DATE) as block_month, + et.block_time as creation_time, + et.tx_hash +from {{ source('linea', 'traces') }} et +join {{ ref('safe_linea_singletons') }} s + on et.to = s.address +where et.success = true + and et.call_type = 'delegatecall' -- delegatecall to singleton is Safe (proxy) address + and bytearray_substring(et.input, 1, 4) in ( + 0xb63e800d -- setup method v1.3.0, v1.3.0L2, v1.4.1, v.1.4.1L2 + ) + and et.gas_used > 10000 -- to ensure the setup call was successful. excludes e.g. setup calls with missing params that fallback + {% if not is_incremental() %} + and et.block_time > TIMESTAMP '{{project_start_date}}' -- for initial query optimisation + {% else %} + and {{ incremental_predicate('et.block_time') }} + {% endif %} diff --git a/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_schema.yml b/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_schema.yml new file mode 100644 index 00000000000..ba3782bfdd4 --- /dev/null +++ b/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_schema.yml @@ -0,0 +1,149 @@ +version: 2 + +models: + - name: safe_linea_safes + meta: + blockchain: linea + project: safe + contributors: danielpartida + config: + tags: ['safe', 'linea'] + description: "Safe addresses" + tests: + - dbt_utils.unique_combination_of_columns: + combination_of_columns: + - block_date + - address + columns: + - &blockchain + name: blockchain + description: "The blockchain on which the Safe is deployed" + - &address + name: address + description: "Safe contract address" + - &creation_version + name: creation_version + description: "Version of initially created safe" + - &block_date + name: block_date + - &block_month + name: block_month + - &creation_time + name: creation_time + description: "Date/time of safe creation" + - &tx_hash + name: tx_hash + + - name: safe_linea_eth_transfers + meta: + blockchain: linea + project: safe + contributors: danielpartida + config: + tags: ['safe', 'transfers', 'linea'] + description: "Eth transfers for safes" + tests: + - dbt_utils.unique_combination_of_columns: + combination_of_columns: + - block_date + - address + - tx_hash + - trace_address + columns: + - *blockchain + - &symbol + name: symbol + description: "Symbol of native gas token: ETH" + - *address + - *block_date + - *block_month + - &block_time + name: block_time + description: "Date of Eth transfer" + - &amount_raw + name: amount_raw + description: "Raw amount of transferred ETH" + - *tx_hash + - &trace_address + name: trace_address + - &amount_usd + name: amount_usd + description: "USD amount of transferred ETH" + + - name: safe_linea_singletons + meta: + blockchain: linea + project: safe + contributors: danielpartida + config: + tags: ['safe', 'singletons', 'linea'] + description: "Singletons addresses used with Safes" + columns: + - name: address + description: "Safe contract address" + tests: + - unique + - not_null + + - name: safe_linea_transactions + meta: + blockchain: linea + project: safe + contributors: danielpartida + config: + tags: ['safe', 'linea'] + description: "Safe transactions" + tests: + - dbt_utils.unique_combination_of_columns: + combination_of_columns: + - block_date + - tx_hash + - trace_address + columns: + - *blockchain + - *block_date + - *block_month + - *block_time + - &block_number + name: block_number + description: "Number of block" + - *tx_hash + - name: address + description: "Safe contract address" + - &to + name: to + description: "Destination address" + - &value + name: value + description: "Value of transaction" + - &gas + name: gas + description: "Gas limit set for transaction" + - &execution_gas_used + name: execution_gas_used + description: "Execution gas used during transaction, for more details see https://dune.com/docs/data-tables/raw/evm/traces/?h=traces#gas-used-in-traces" + - &total_gas_used + name: total_gas_used + description: "Total gas used during transaction" + - &tx_index + name: tx_index + description: "Transaction index" + - &sub_traces + name: sub_traces + description: "Number of sub traces" + - *trace_address + - &success + name: success + description: "Success state of transaction" + - &error + name: error + description: "Error of transaction if any" + - &code + name: code + description: "Code" + - &input + name: input + description: "Input data" + - &output + name: output + description: "Output data" diff --git a/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_singletons.sql b/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_singletons.sql new file mode 100644 index 00000000000..799270755a3 --- /dev/null +++ b/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_singletons.sql @@ -0,0 +1,22 @@ +{{ + config( + materialized='table', + schema = 'safe_linea', + alias= 'singletons', + post_hook = '{{ expose_spells( + blockchains = \'["linea"]\', + spell_type = "project", + spell_name = "safe", + contributors = \'["danielpartida"]\') }}' + ) +}} + + +-- Fetch all known singleton/mastercopy addresses used via factories. +select distinct singleton as address +from {{ source('gnosis_safe_linea', 'SafeProxyFactoryv_1_3_0_evt_ProxyCreation') }} + +union + +select distinct singleton as address +from {{ source('gnosis_safe_linea', 'SafeProxyFactoryv_1_4_1_evt_ProxyCreation') }} diff --git a/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_transactions.sql b/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_transactions.sql new file mode 100644 index 00000000000..4426629ee39 --- /dev/null +++ b/dbt_subprojects/hourly_spellbook/models/_project/safe/linea/safe_linea_transactions.sql @@ -0,0 +1,68 @@ +{{ + config( + materialized='incremental', +schema = 'safe_linea', + alias= 'transactions', + partition_by = ['block_month'], + unique_key = ['block_date', 'tx_hash', 'trace_address'], + file_format ='delta', + incremental_strategy='merge', + post_hook = '{{ expose_spells( + blockchains = \'["linea"]\', + spell_type = "project", + spell_name = "safe", + contributors = \'["danielpartida"]\') }}' + ) +}} + +{% set project_start_date = '2023-07-11' %} + +select + 'linea' as blockchain, + try_cast(date_trunc('day', tr.block_time) as date) as block_date, + CAST(date_trunc('month', tr.block_time) as DATE) as block_month, + tr.block_time, + tr.block_number, + tr.tx_hash, + s.address, + tr.to, + tr.value, + tr.gas, + tr.gas_used as execution_gas_used, + et.gas_used as total_gas_used, + tr.tx_index, + tr.sub_traces, + tr.trace_address, + tr.success, + tr.error, + tr.code, + tr.input, + tr.output, + case + when bytearray_substring(tr.input, 1, 4) = 0x6a761202 then 'execTransaction' + when bytearray_substring(tr.input, 1, 4) = 0x468721a7 then 'execTransactionFromModule' + when bytearray_substring(tr.input, 1, 4) = 0x5229073f then 'execTransactionFromModuleReturnData' + else 'unknown' + end as method +from {{ source('linea', 'traces') }} tr +join {{ ref('safe_linea_safes') }} s + on s.address = tr."from" +join {{ ref('safe_linea_singletons') }} ss + on tr.to = ss.address +join {{ source('linea', 'transactions') }} et + on tr.tx_hash = et.hash + {% if is_incremental() %} + and {{ incremental_predicate('et.block_time') }} + {% endif %} + and tr.block_number = et.block_number +where bytearray_substring(tr.input, 1, 4) in ( + 0x6a761202, -- execTransaction + 0x468721a7, -- execTransactionFromModule + 0x5229073f -- execTransactionFromModuleReturnData + ) + and tr.call_type = 'delegatecall' + {% if not is_incremental() %} + and tr.block_time > TIMESTAMP '{{project_start_date}}' -- for initial query optimisation + {% else %} + and {{ incremental_predicate('tr.block_time') }} + {% endif %} diff --git a/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_native_transfers_all.sql b/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_native_transfers_all.sql index a4dd6a3f1fd..8649285c293 100644 --- a/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_native_transfers_all.sql +++ b/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_native_transfers_all.sql @@ -1,7 +1,7 @@ {{ config( schema = 'safe', alias = 'native_transfers_all', - post_hook='{{ expose_spells(\'["arbitrum","avalanche_c","base","bnb","celo","ethereum","gnosis","goerli","optimism","polygon","zkevm","zksync"]\', + post_hook='{{ expose_spells(\'["arbitrum","avalanche_c","base","bnb","celo","ethereum","gnosis","goerli","linea","optimism","polygon","zkevm","zksync"]\', "project", "safe", \'["kryptaki", "danielpartida"]\') }}' @@ -17,6 +17,7 @@ ,ref('safe_ethereum_eth_transfers') ,ref('safe_gnosis_xdai_transfers') ,ref('safe_goerli_eth_transfers') +,ref('safe_linea_eth_transfers') ,ref('safe_optimism_eth_transfers') ,ref('safe_polygon_matic_transfers') ,ref('safe_zkevm_matic_transfers') diff --git a/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_safes_all.sql b/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_safes_all.sql index 6bd372417a6..c27ef8a062f 100644 --- a/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_safes_all.sql +++ b/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_safes_all.sql @@ -1,7 +1,7 @@ {{ config( schema = 'safe', alias = 'safes_all', - post_hook='{{ expose_spells(\'["arbitrum","avalanche_c","base","bnb","celo","ethereum","fantom","gnosis","goerli","optimism","polygon","zkevm","zksync"]\', + post_hook='{{ expose_spells(\'["arbitrum","avalanche_c","base","bnb","celo","ethereum","fantom","gnosis","goerli","linea","optimism","polygon","zkevm","zksync"]\', "project", "safe", \'["tschubotz", "danielpartida", "kryptaki"]\') }}' @@ -18,6 +18,7 @@ ,ref('safe_fantom_safes') ,ref('safe_gnosis_safes') ,ref('safe_goerli_safes') +,ref('safe_linea_safes') ,ref('safe_optimism_safes') ,ref('safe_polygon_safes') ,ref('safe_zkevm_safes') diff --git a/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_transactions_all.sql b/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_transactions_all.sql index be19fb40a9c..b8eb899323d 100644 --- a/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_transactions_all.sql +++ b/dbt_subprojects/hourly_spellbook/models/_project/safe/safe_transactions_all.sql @@ -1,7 +1,7 @@ {{ config( schema = 'safe', alias = 'transactions_all', - post_hook='{{ expose_spells(\'["arbitrum","avalanche_c","base","bnb","celo","ethereum","fantom","gnosis","goerli","optimism","polygon","zkevm","zksync"]\', + post_hook='{{ expose_spells(\'["arbitrum","avalanche_c","base","bnb","celo","ethereum","fantom","gnosis","goerli","linea","optimism","polygon","zkevm","zksync"]\', "project", "safe", \'["kryptaki", "danielpartida"]\') }}' @@ -18,6 +18,7 @@ ,ref('safe_fantom_transactions') ,ref('safe_gnosis_transactions') ,ref('safe_goerli_transactions') +,ref('safe_linea_transactions') ,ref('safe_optimism_transactions') ,ref('safe_polygon_transactions') ,ref('safe_zkevm_transactions') diff --git a/sources/safe/linea/safe_linea_sources.yml b/sources/safe/linea/safe_linea_sources.yml new file mode 100644 index 00000000000..886b18edb41 --- /dev/null +++ b/sources/safe/linea/safe_linea_sources.yml @@ -0,0 +1,45 @@ +version: 2 + +sources: + - name: gnosis_safe_linea + freshness: + warn_after: { count: 12, period: hour } + tables: + - name: SafeProxyFactoryv_1_3_0_evt_ProxyCreation + loaded_at_field: evt_block_time + description: "Safe v1.3.0 factory ProxyCreation events" + columns: + - &contract_address + name: contract_address + description: "Contract address" + - &evt_block_number + name: evt_block_number + description: "Event block number" + - &evt_block_time + name: evt_block_time + description: "Event block time" + - &evt_index + name: evt_index + description: "Event index" + - &evt_tx_hash + name: evt_tx_hash + description: "Event tx hash" + - &proxy + name: proxy + description: "Address of created Safe proxy" + - &singleton + name: singleton + description: "Singleton" + - name: SafeProxyFactoryv_1_4_1_evt_ProxyCreation + loaded_at_field: evt_block_time + description: "Safe v1.4.1 factory ProxyCreation events" + columns: + - *contract_address + - *evt_block_number + - *evt_block_time + - *evt_index + - *evt_tx_hash + - name: proxy + description: "Address of created Safe proxy" + - name: singleton + description: "Singleton" \ No newline at end of file