Source Code

;; title: analytics-aggregator
;; version: 1.0.0
;; summary: Advanced metrics beyond basic view tracking
;; description: Conversion tracking, ROI calculation, publisher reports, hourly performance, and fraud scoring

;; constants
(define-constant contract-owner tx-sender)
(define-constant err-owner-only (err u100))
(define-constant err-not-found (err u101))
(define-constant err-unauthorized (err u102))
(define-constant err-invalid-data (err u103))

;; data vars
(define-data-var total-conversions uint u0)
(define-data-var total-revenue uint u0)
(define-data-var total-ad-spend uint u0)

;; data maps
(define-map conversions
    { campaign-id: uint, conversion-id: uint }
    {
        user: principal,
        value: uint,
        conversion-type: (string-ascii 30),
        timestamp: uint,
        attributed-view-id: uint
    }
)

(define-map conversion-count
    { campaign-id: uint }
    {
        total: uint
    }
)

(define-map campaign-roi
    { campaign-id: uint }
    {
        total-spend: uint,
        total-revenue: uint,
        roi-percentage: uint, ;; Multiplied by 100
        conversions: uint,
        last-updated: uint
    }
)

(define-map publisher-reports
    { publisher: principal, period: uint }
    {
        total-views: uint,
        total-earnings: uint,
        avg-cpv: uint,
        quality-score: uint,
        campaigns-participated: uint,
        period-start: uint,
        period-end: uint
    }
)

(define-map hourly-stats
    { campaign-id: uint, hour-block: uint }
    {
        views: uint,
        clicks: uint,
        conversions: uint,
        revenue: uint,
        ctr: uint, ;; Click-through rate * 10000
        cvr: uint  ;; Conversion rate * 10000
    }
)

(define-map retention-metrics
    { campaign-id: uint }
    {
        unique-viewers: uint,
        returning-viewers: uint,
        retention-rate: uint, ;; Percentage * 100
        avg-views-per-user: uint
    }
)

(define-map fraud-scores
    { campaign-id: uint }
    {
        suspicious-views: uint,
        total-views: uint,
        fraud-percentage: uint, ;; * 100
        flagged-publishers: uint,
        last-audit: uint
    }
)

(define-map category-benchmarks
    { category: (string-ascii 30) }
    {
        avg-ctr: uint,
        avg-cvr: uint,
        avg-cpc: uint,
        total-campaigns: uint,
        last-updated: uint
    }
)

(define-map user-engagement
    { user: principal, campaign-id: uint }
    {
        total-views: uint,
        total-clicks: uint,
        time-spent: uint,
        last-interaction: uint,
        engagement-score: uint
    }
)

;; private functions
(define-private (calculate-roi (revenue uint) (spend uint))
    (if (> spend u0)
        (/ (* (- revenue spend) u10000) spend)
        u0
    )
)

(define-private (calculate-rate (numerator uint) (denominator uint))
    (if (> denominator u0)
        (/ (* numerator u10000) denominator)
        u0
    )
)

;; read only functions
(define-read-only (get-conversion (campaign-id uint) (conversion-id uint))
    (map-get? conversions { campaign-id: campaign-id, conversion-id: conversion-id })
)

(define-read-only (get-conversion-count (campaign-id uint))
    (default-to { total: u0 } (map-get? conversion-count { campaign-id: campaign-id }))
)

(define-read-only (get-campaign-roi (campaign-id uint))
    (map-get? campaign-roi { campaign-id: campaign-id })
)

(define-read-only (get-publisher-report (publisher principal) (period uint))
    (map-get? publisher-reports { publisher: publisher, period: period })
)

(define-read-only (get-hourly-stats (campaign-id uint) (hour-block uint))
    (map-get? hourly-stats { campaign-id: campaign-id, hour-block: hour-block })
)

(define-read-only (get-retention-metrics (campaign-id uint))
    (map-get? retention-metrics { campaign-id: campaign-id })
)

(define-read-only (get-fraud-score (campaign-id uint))
    (map-get? fraud-scores { campaign-id: campaign-id })
)

(define-read-only (get-category-benchmark (category (string-ascii 30)))
    (map-get? category-benchmarks { category: category })
)

(define-read-only (get-user-engagement (user principal) (campaign-id uint))
    (map-get? user-engagement { user: user, campaign-id: campaign-id })
)

;; public functions
(define-public (record-conversion
    (campaign-id uint)
    (user principal)
    (value uint)
    (conversion-type (string-ascii 30))
    (attributed-view-id uint)
)
    (let
        (
            (count-data (get-conversion-count campaign-id))
            (conversion-id (+ (get total count-data) u1))
        )
        (map-set conversions
            { campaign-id: campaign-id, conversion-id: conversion-id }
            {
                user: user,
                value: value,
                conversion-type: conversion-type,
                timestamp: stacks-block-time,
                attributed-view-id: attributed-view-id
            }
        )

        (map-set conversion-count
            { campaign-id: campaign-id }
            { total: conversion-id }
        )

        (var-set total-conversions (+ (var-get total-conversions) u1))
        (var-set total-revenue (+ (var-get total-revenue) value))

        (ok conversion-id)
    )
)

(define-public (update-campaign-roi
    (campaign-id uint)
    (spend uint)
    (revenue uint)
    (num-conversions uint)
)
    (let
        (
            (roi (calculate-roi revenue spend))
        )
        (map-set campaign-roi
            { campaign-id: campaign-id }
            {
                total-spend: spend,
                total-revenue: revenue,
                roi-percentage: roi,
                conversions: num-conversions,
                last-updated: stacks-block-time
            }
        )

        (ok roi)
    )
)

(define-public (generate-publisher-report
    (publisher principal)
    (period uint)
    (total-views uint)
    (total-earnings uint)
    (campaigns-participated uint)
)
    (let
        (
            (avg-cpv (if (> total-views u0) (/ total-earnings total-views) u0))
            (quality-score u100) ;; Simplified - would be calculated based on performance
        )
        (map-set publisher-reports
            { publisher: publisher, period: period }
            {
                total-views: total-views,
                total-earnings: total-earnings,
                avg-cpv: avg-cpv,
                quality-score: quality-score,
                campaigns-participated: campaigns-participated,
                period-start: (- stacks-block-time period),
                period-end: stacks-block-time
            }
        )

        (ok true)
    )
)

(define-public (track-hourly-performance
    (campaign-id uint)
    (views uint)
    (clicks uint)
    (num-conversions uint)
    (revenue uint)
)
    (let
        (
            (hour-block (/ stacks-block-time u144)) ;; ~1 hour blocks
            (ctr (calculate-rate clicks views))
            (cvr (calculate-rate num-conversions views))
        )
        (map-set hourly-stats
            { campaign-id: campaign-id, hour-block: hour-block }
            {
                views: views,
                clicks: clicks,
                conversions: num-conversions,
                revenue: revenue,
                ctr: ctr,
                cvr: cvr
            }
        )

        (ok true)
    )
)

(define-public (update-retention-rate
    (campaign-id uint)
    (unique-viewers uint)
    (returning-viewers uint)
    (total-views uint)
)
    (let
        (
            (retention-rate (calculate-rate returning-viewers unique-viewers))
            (avg-views (if (> unique-viewers u0) (/ total-views unique-viewers) u0))
        )
        (map-set retention-metrics
            { campaign-id: campaign-id }
            {
                unique-viewers: unique-viewers,
                returning-viewers: returning-viewers,
                retention-rate: retention-rate,
                avg-views-per-user: avg-views
            }
        )

        (ok retention-rate)
    )
)

(define-public (calculate-fraud-score
    (campaign-id uint)
    (suspicious-views uint)
    (total-views uint)
    (flagged-publishers uint)
)
    (let
        (
            (fraud-percentage (calculate-rate suspicious-views total-views))
        )
        (map-set fraud-scores
            { campaign-id: campaign-id }
            {
                suspicious-views: suspicious-views,
                total-views: total-views,
                fraud-percentage: fraud-percentage,
                flagged-publishers: flagged-publishers,
                last-audit: stacks-block-time
            }
        )

        (ok fraud-percentage)
    )
)

(define-public (update-category-benchmark
    (category (string-ascii 30))
    (avg-ctr uint)
    (avg-cvr uint)
    (avg-cpc uint)
    (total-campaigns uint)
)
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)

        (map-set category-benchmarks
            { category: category }
            {
                avg-ctr: avg-ctr,
                avg-cvr: avg-cvr,
                avg-cpc: avg-cpc,
                total-campaigns: total-campaigns,
                last-updated: stacks-block-time
            }
        )

        (ok true)
    )
)

(define-public (track-user-engagement
    (user principal)
    (campaign-id uint)
    (views uint)
    (clicks uint)
    (time-spent uint)
)
    (let
        (
            (engagement-score (+ (* views u1) (* clicks u5) (/ time-spent u100)))
        )
        (map-set user-engagement
            { user: user, campaign-id: campaign-id }
            {
                total-views: views,
                total-clicks: clicks,
                time-spent: time-spent,
                last-interaction: stacks-block-time,
                engagement-score: engagement-score
            }
        )

        (ok engagement-score)
    )
)

(define-public (batch-update-metrics
    (campaign-id uint)
    (spend uint)
    (revenue uint)
    (num-conversions uint)
    (unique-viewers uint)
    (returning-viewers uint)
)
    (let
        (
            (roi-result (update-campaign-roi campaign-id spend revenue num-conversions))
            (retention-result (update-retention-rate campaign-id unique-viewers returning-viewers (+ unique-viewers returning-viewers)))
        )
        (ok true)
    )
)

;; Admin functions
(define-public (reset-campaign-analytics (campaign-id uint))
    (begin
        (asserts! (is-eq tx-sender contract-owner) err-owner-only)

        (map-delete campaign-roi { campaign-id: campaign-id })
        (map-delete retention-metrics { campaign-id: campaign-id })
        (map-delete fraud-scores { campaign-id: campaign-id })

        (ok true)
    )
)

Functions (14)

FunctionAccessArgs
calculate-roiprivaterevenue: uint, spend: uint
calculate-rateprivatenumerator: uint, denominator: uint
get-conversionread-onlycampaign-id: uint, conversion-id: uint
get-conversion-countread-onlycampaign-id: uint
get-campaign-roiread-onlycampaign-id: uint
get-publisher-reportread-onlypublisher: principal, period: uint
get-hourly-statsread-onlycampaign-id: uint, hour-block: uint
get-retention-metricsread-onlycampaign-id: uint
get-fraud-scoreread-onlycampaign-id: uint
get-category-benchmarkread-onlycategory: (string-ascii 30
get-user-engagementread-onlyuser: principal, campaign-id: uint
record-conversionpubliccampaign-id: uint, user: principal, value: uint, conversion-type: (string-ascii 30
update-category-benchmarkpubliccategory: (string-ascii 30
reset-campaign-analyticspubliccampaign-id: uint