Skip to main content
Schema: optimism.defi Table: ez_lending_borrows Type: View

What

This table provides a comprehensive view of borrowing transactions across all major lending protocols on EVM blockchains. It captures when users borrow assets against their deposited collateral, enabling analysis of lending market dynamics, user behavior, and protocol performance.

Key Use Cases

  • Tracking borrowing volumes and user activity across protocols
  • Analyzing most borrowed assets and their trends
  • Understanding user borrowing patterns and behavior
  • Monitoring protocol market share and growth
  • Calculating outstanding loan positions

Important Relationships

  • Links to ez_lending_deposits for collateral analysis
  • Joins with ez_lending_repayments to track loan lifecycle
  • References ez_lending_liquidations for risk analysis
  • Connects to price.ez_prices_hourly for USD valuations

Commonly-used Fields

  • borrower: Address that borrowed assets
  • platform: Lending protocol name
  • token_address/token_symbol: Borrowed asset details
  • amount/amount_usd: Borrowed quantity and USD value
  • block_timestamp: When borrow occurred

Sample queries

-- Daily borrowing volume by protocol
SELECT
    DATE_TRUNC('day', block_timestamp) AS date,
    platform,
    COUNT(DISTINCT tx_hash) AS borrow_txns,
    COUNT(DISTINCT borrower) AS unique_borrowers,
    SUM(amount_usd) AS volume_usd
FROM <blockchain_name>.defi.ez_lending_borrows
WHERE block_timestamp >= CURRENT_DATE - 30
    AND amount_usd IS NOT NULL
GROUP BY 1, 2
ORDER BY 1 DESC, 5 DESC;

-- Top borrowed assets analysis
SELECT
    token_symbol,
    token_address,
    COUNT(*) AS borrow_count,
    SUM(amount) AS total_borrowed,
    SUM(amount_usd) AS total_borrowed_usd,
    AVG(amount_usd) AS avg_borrow_size_usd
FROM <blockchain_name>.defi.ez_lending_borrows
WHERE block_timestamp >= CURRENT_DATE - 7
    AND token_symbol IS NOT NULL
GROUP BY 1, 2
ORDER BY 5 DESC
LIMIT 20;

-- Wallet Specific Borrow Analysis
SELECT
    b.borrower,
    b.token_address AS borrowed_token_address,
    b.token_symbol AS borrowed_token_symbol,
    DATE_TRUNC('week', b.block_timestamp) AS weekly_block_timestamp,
    SUM(b.amount) AS total_borrow_amount,
    SUM(b.amount_usd) AS total_borrow_usd,
    SUM(r.amount) AS total_repayment_amount,
    SUM(r.amount_usd) AS total_repayment_usd,
    SUM(b.amount) - SUM(r.amount) AS net_borrowed_amount,
    SUM(b.amount_usd) - SUM(r.amount_usd) AS net_borrowed_usd
FROM
    <blockchain_name>.defi.ez_lending_borrows b
LEFT JOIN <blockchain_name>.defi.ez_lending_repayments r
    ON b.borrower = r.borrower
    AND b.token_address = r.token_address
WHERE
    b.borrower = LOWER('<user_address>')
GROUP BY 1, 2, 3, 4

-- User borrowing patterns
WITH user_stats AS (
    SELECT
        borrower,
        COUNT(DISTINCT DATE_TRUNC('day', block_timestamp)) AS active_days,
        COUNT(DISTINCT platform) AS platforms_used,
        COUNT(DISTINCT token_address) AS assets_borrowed,
        SUM(amount_usd) AS total_borrowed_usd,
        AVG(amount_usd) AS avg_borrow_size
    FROM <blockchain_name>.defi.ez_lending_borrows
    WHERE block_timestamp >= CURRENT_DATE - 30
        AND amount_usd IS NOT NULL
    GROUP BY 1
)
SELECT
    CASE
        WHEN total_borrowed_usd < 1000 THEN '< $1K'
        WHEN total_borrowed_usd < 10000 THEN '$1K - $10K'
        WHEN total_borrowed_usd < 100000 THEN '$10K - $100K'
        ELSE '> $100K'
    END AS borrower_tier,
    COUNT(*) AS user_count,
    AVG(active_days) AS avg_active_days,
    AVG(platforms_used) AS avg_platforms,
    AVG(total_borrowed_usd) AS avg_total_borrowed
FROM user_stats
GROUP BY 1
ORDER BY 5 DESC;

-- Protocol market share
WITH protocol_volume AS (
    SELECT
        platform,
        SUM(amount_usd) AS total_volume,
        COUNT(DISTINCT borrower) AS unique_users,
        COUNT(*) AS transaction_count
    FROM <blockchain_name>.defi.ez_lending_borrows
    WHERE block_timestamp >= CURRENT_DATE - 30
        AND amount_usd IS NOT NULL
    GROUP BY 1
)
SELECT
    platform,
    total_volume,
    total_volume * 100.0 / SUM(total_volume) OVER () AS market_share_pct,
    unique_users,
    transaction_count,
    total_volume / transaction_count AS avg_borrow_size
FROM protocol_volume
ORDER BY total_volume DESC;

Columns

Column NameData TypeDescription
BLOCK_NUMBERNUMBERSequential counter representing the position of a block in the blockchain since genesis (block 0).
Key Facts:
  • Immutable once finalized
  • Primary ordering mechanism for blockchain data
  • Increments by 1 for each new block
  • Used as a proxy for time in many analyses
Usage in Queries:
-- Recent data
WHERE block_number >= (SELECT MAX(block_number) - 1000 FROM fact_blocks)

-- Historical analysis
WHERE block_number BETWEEN 15000000 AND 16000000

-- Join across tables
JOIN <blockchain_name>.core.fact_event_logs USING (block_number)
Important: Block numbers are chain-specific. Block 15000000 on Ethereum ≠ block 15000000 on Polygon. | | BLOCK_TIMESTAMP | TIMESTAMP_NTZ | UTC timestamp when the block was produced by validators/miners. Format: TIMESTAMP_NTZ (no timezone) Precision: Second-level accuracy Reliability:
  • Set by block producer
  • Can have minor variations (±15 seconds)
  • Always increasing (newer blocks = later timestamps)
Best Practices:
-- Time-based filtering (most efficient)
WHERE block_timestamp >= DATEADD('day', -7, CURRENT_TIMESTAMP)

-- Hourly aggregations
DATE_TRUNC('hour', block_timestamp) AS hour

-- UTC date extraction
DATE(block_timestamp) AS block_date
Note: Use for time-series analysis, but be aware that block production rates vary by chain. | | TX_HASH | TEXT | Unique 66-character identifier for the transaction. Format: 0x + 64 hexadecimal characters Usage:
  • Primary key for transaction lookups
  • Join key for traces, logs, and token transfers
  • Immutable once confirmed
Example: 0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060 | | CONTRACT_ADDRESS | TEXT | Smart contract address that emitted this event or received the transaction. Key Points:
  • Always the immediate event emitter for logs
  • May differ from transaction to_address
  • Lowercase normalized format
  • Never NULL for valid events | | EVENT_NAME | TEXT | The event name as defined in the contract’s ABI.
Format: PascalCase event identifier Examples:
  • Transfer - Token transfers
  • Swap - DEX trades
  • OwnershipTransferred - Admin changes
  • Approval - Token approvals
Usage Pattern:
-- Find all event types for a contract
SELECT DISTINCT event_name, COUNT(*) as occurrences
FROM ez_decoded_event_logs
WHERE contract_address = LOWER('0x...')
GROUP BY 1
ORDER BY 2 DESC;
``` |
| EVENT_INDEX | NUMBER | Zero-based sequential position of the event within a transaction's execution.

**Key Facts**:
- Starts at 0 for first event
- Increments across all contracts in transaction
- Preserves execution order
- Essential for deterministic event ordering

**Usage Example**:
```sql
-- Trace event execution flow
SELECT
    event_index,
    contract_address,
    topic_0,
    SUBSTRING(data, 1, 10) AS data_preview
FROM <blockchain_name>.core.fact_event_logs
WHERE tx_hash = '0xabc...'
ORDER BY event_index;
``` |
| ORIGIN_FUNCTION_SIGNATURE | TEXT | Function signature (first 4 bytes) of the called method.

**Format**: 0x + 8 hex characters

**Common Signatures**:
- 0xa9059cbb: transfer(address,uint256)
- 0x095ea7b3: approve(address,uint256)
- 0x23b872dd: transferFrom(address,address,uint256)

**Note**: NULL for simple transfers or invalid calls |
| ORIGIN_FROM_ADDRESS | TEXT | The externally-owned account (EOA) or contract address that initiated the transaction.

**Key Points**:
- Always 42 characters (0x + 40 hex chars)
- Lowercase normalized in all tables
- Cannot be NULL for valid transactions
- For contract creation: sender of creation transaction

**Common Patterns**:
- EOA → EOA: Simple transfer
- EOA → Contract: User interaction
- Contract → Contract: Internal calls (see fact_traces)
- Known addresses: Exchange hot wallets, protocol deployers

**Query Examples**:
```sql
-- User activity analysis
SELECT from_address, COUNT(*) as tx_count
FROM <blockchain_name>.core.fact_transactions
WHERE block_timestamp >= CURRENT_DATE - 30
GROUP BY 1
ORDER BY 2 DESC;

-- New user detection
SELECT DISTINCT from_address
FROM <blockchain_name>.core.fact_transactions t1
WHERE NOT EXISTS (
    SELECT 1 FROM <blockchain_name>.core.fact_transactions t2
    WHERE t2.from_address = t1.from_address
    AND t2.block_number < t1.block_number
);
``` |
| ORIGIN_TO_ADDRESS | TEXT | The destination address for the transaction - either an EOA or contract address.

**Special Cases**:
- NULL: Contract creation transaction
- Contract address: Interacting with smart contract
- EOA address: Simple transfer or receiving funds

**Important Patterns**:
```sql
-- Contract deployments
WHERE to_address IS NULL

-- Popular contracts
SELECT to_address, COUNT(*) as interactions
FROM <blockchain_name>.core.fact_transactions
WHERE to_address IS NOT NULL
GROUP BY 1
ORDER BY 2 DESC;

-- Direct transfers only
WHERE to_address NOT IN (SELECT address FROM dim_contracts)
Note: For token transfers, this is the token contract, not the recipient. See ez_token_transfers tables for recipient. | | PLATFORM | TEXT | The lending protocol where the transaction occurred. Example: ‘aave’ | | PROTOCOL_MARKET | TEXT | The lending protocol’s receipt token issued to depositors. Example: ‘0xfedcbafedcbafedcbafedcbafedcbafedcbafed’ | | BORROWER | TEXT | The address that initiated a borrow or repayment transaction. Example: ‘0x1234567890123456789012345678901234567890’ | | TOKEN_ADDRESS | TEXT | The contract address of the underlying asset being lent or borrowed. Example: ‘0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48’ | | TOKEN_SYMBOL | TEXT | The ticker symbol of the asset involved in the lending transaction. Example: ‘USDC’ | | AMOUNT_UNADJ | NUMBER | The raw amount of tokens borrowed or repaid without decimal adjustment. Example: 1000000000 | | AMOUNT | FLOAT | The decimal-adjusted quantity of tokens in the transaction. Example: 1000.5 | | AMOUNT_USD | FLOAT | The USD value of tokens at transaction time. Example: 1500.75 | | EZ_LENDING_BORROWS_ID | TEXT | Primary key - unique identifier for each row ensuring data integrity. Format: Usually VARCHAR containing composite key generated using MD5 hash of the relevant columns. Example: MD5(block_number, tx_hash, trace_index) Usage:
  • Deduplication in incremental loads
  • Join operations for data quality checks
  • Troubleshooting specific records
Important: Implementation varies by table - check table-specific documentation. | | INSERTED_TIMESTAMP | TIMESTAMP_NTZ | UTC timestamp when the record was first added to the Flipside database. Format: TIMESTAMP_NTZ Use Cases:
  • Data freshness monitoring
  • Incremental processing markers
  • Debugging data pipeline issues
  • SLA tracking
Query Example:
-- Check data latency
SELECT
    DATE_TRUNC('hour', block_timestamp) as block_hour,
    DATE_TRUNC('hour', inserted_timestamp) as insert_hour,
    AVG(TIMESTAMPDIFF('minute', block_timestamp, inserted_timestamp)) as avg_latency_minutes
FROM <blockchain_name>.core.fact_transactions
WHERE block_timestamp >= CURRENT_DATE - 1
GROUP BY 1, 2;
``` |
| MODIFIED_TIMESTAMP | TIMESTAMP_NTZ | UTC timestamp of the most recent update to this record.

**Format**: TIMESTAMP_NTZ

**Triggers for Updates**:
- Data corrections
- Enrichment additions
- Reprocessing for accuracy
- Schema migrations

**Monitoring Usage**:
```sql
-- Recently modified records
SELECT *
FROM <blockchain_name>.core.fact_transactions
WHERE modified_timestamp > inserted_timestamp
AND modified_timestamp >= CURRENT_DATE - 1;

-- Data quality tracking
SELECT
    DATE(modified_timestamp) as mod_date,
    COUNT(*) as records_updated,
    COUNT(DISTINCT block_number) as blocks_affected
FROM <blockchain_name>.core.fact_transactions
WHERE modified_timestamp > inserted_timestamp
GROUP BY 1
ORDER BY 1 DESC;
``` |