Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 67 additions & 19 deletions internal/migrations/033-order-book-settlement.sql
Original file line number Diff line number Diff line change
Expand Up @@ -121,24 +121,71 @@ CREATE OR REPLACE ACTION distribute_fees(
$query_id INT,
$total_fees NUMERIC(78, 0)
) PRIVATE {
-- Get market's bridge for unlock operations
-- Get market details for fee split and unlock
$bridge TEXT;
for $row in SELECT bridge FROM ob_queries WHERE id = $query_id {
$query_components BYTEA;
for $row in SELECT bridge, query_components FROM ob_queries WHERE id = $query_id {
$bridge := $row.bridge;
$query_components := $row.query_components;
}
if $bridge IS NULL {
ERROR('Market not found for query_id: ' || $query_id::TEXT);
}

-- Step 1: Count distinct blocks sampled for this market
-- Step 0: Calculate Shares (75/12.5/12.5 split)
-- Target: 1.5% (LPs), 0.25% (DP), 0.25% (Validator) out of 2.0% total fees
-- Split of the 2.0% pool: 75% LPs, 12.5% DP, 12.5% Validator
$lp_share NUMERIC(78, 0) := ($total_fees * 75::NUMERIC(78, 0)) / 100::NUMERIC(78, 0);
$infra_share NUMERIC(78, 0) := ($total_fees * 125::NUMERIC(78, 0)) / 1000::NUMERIC(78, 0);

-- Ensure 100% distribution: add any rounding dust to LP pool
$lp_share := $lp_share + ($total_fees - $lp_share - (2::NUMERIC(78, 0) * $infra_share));

-- Step 1: Payout Data Provider (0.25%)
$dp_addr BYTEA;
for $row in tn_utils.unpack_query_components($query_components) {
$dp_addr := $row.data_provider;
}

$actual_dp_fees NUMERIC(78, 0) := '0'::NUMERIC(78, 0);
if $dp_addr IS NOT NULL AND $infra_share > '0'::NUMERIC(78, 0) {
$dp_wallet TEXT := '0x' || encode($dp_addr, 'hex');
if $bridge = 'hoodi_tt2' {
hoodi_tt2.unlock($dp_wallet, $infra_share);
$actual_dp_fees := $infra_share;
} else if $bridge = 'sepolia_bridge' {
sepolia_bridge.unlock($dp_wallet, $infra_share);
$actual_dp_fees := $infra_share;
} else if $bridge = 'ethereum_bridge' {
ethereum_bridge.unlock($dp_wallet, $infra_share);
$actual_dp_fees := $infra_share;
}
}

-- Step 2: Payout Validator (Leader) (0.25%)
-- Use @leader_sender to incentivize active block production
$actual_validator_fees NUMERIC(78, 0) := '0'::NUMERIC(78, 0);
if @leader_sender IS NOT NULL AND $infra_share > '0'::NUMERIC(78, 0) {
$validator_wallet TEXT := '0x' || encode(@leader_sender, 'hex');
if $bridge = 'hoodi_tt2' {
hoodi_tt2.unlock($validator_wallet, $infra_share);
$actual_validator_fees := $infra_share;
} else if $bridge = 'sepolia_bridge' {
sepolia_bridge.unlock($validator_wallet, $infra_share);
$actual_validator_fees := $infra_share;
} else if $bridge = 'ethereum_bridge' {
ethereum_bridge.unlock($validator_wallet, $infra_share);
$actual_validator_fees := $infra_share;
}
}

-- Step 3: Count distinct blocks sampled for this market
$block_count INT := 0;
for $row in SELECT COUNT(DISTINCT block) as cnt FROM ob_rewards WHERE query_id = $query_id {
$block_count := $row.cnt;
}

-- Step 2: Generate distribution ID and create summary record ALWAYS
-- This provides visibility into settled markets even if no rewards were distributed
-- or if zero fees were collected.
-- Step 4: Generate distribution ID and create summary record ALWAYS
$distribution_id INT;
for $row in SELECT COALESCE(MAX(id), 0) + 1 as next_id FROM ob_fee_distributions {
$distribution_id := $row.next_id;
Expand All @@ -152,11 +199,8 @@ CREATE OR REPLACE ACTION distribute_fees(
$wallet_addresses TEXT[];
$amounts NUMERIC(78, 0)[];

if $block_count > 0 AND $total_fees > '0'::NUMERIC(78, 0) {
-- Step 3: Calculate rewards with zero-loss distribution
-- We calculate all distribution arrays in a SINGLE query using simplified logic
-- to avoid performance bottlenecks in the Kwil engine.

if $block_count > 0 AND $lp_share > '0'::NUMERIC(78, 0) {
-- Step 5: Calculate rewards with zero-loss distribution
-- Get the first participant ID to handle the remainder (dust)
$min_participant_id INT;
for $row in SELECT MIN(participant_id) as mid FROM ob_rewards WHERE query_id = $query_id {
Expand All @@ -166,7 +210,7 @@ CREATE OR REPLACE ACTION distribute_fees(
-- Calculate total distributed to find the remainder
$total_distributed_base NUMERIC(78, 0) := '0'::NUMERIC(78, 0);
for $row in
SELECT SUM((($total_fees::NUMERIC(78, 20) * total_percent_numeric) / (100::NUMERIC(78, 20) * $block_count::NUMERIC(78, 20)))::NUMERIC(78, 0))::NUMERIC(78, 0) as total
SELECT SUM((($lp_share::NUMERIC(78, 20) * total_percent_numeric) / (100::NUMERIC(78, 20) * $block_count::NUMERIC(78, 20)))::NUMERIC(78, 0))::NUMERIC(78, 0) as total
FROM (
SELECT SUM(reward_percent)::NUMERIC(78, 20) as total_percent_numeric
FROM ob_rewards
Expand All @@ -177,7 +221,7 @@ CREATE OR REPLACE ACTION distribute_fees(
$total_distributed_base := $row.total;
}

$remainder NUMERIC(78, 0) := $total_fees - $total_distributed_base;
$remainder NUMERIC(78, 0) := $lp_share - $total_distributed_base;

-- Aggregate into arrays for batch processing
for $result in
Expand All @@ -195,7 +239,7 @@ CREATE OR REPLACE ACTION distribute_fees(
SELECT
participant_id,
wallet_address,
(($total_fees::NUMERIC(78, 20) * total_percent_numeric) / (100::NUMERIC(78, 20) * $block_count::NUMERIC(78, 20)))::NUMERIC(78, 0) +
(($lp_share::NUMERIC(78, 20) * total_percent_numeric) / (100::NUMERIC(78, 20) * $block_count::NUMERIC(78, 20)))::NUMERIC(78, 0) +
(CASE WHEN participant_id = $min_participant_id THEN $remainder ELSE '0'::NUMERIC(78, 0) END) as final_reward
FROM participant_totals
),
Expand All @@ -214,31 +258,35 @@ CREATE OR REPLACE ACTION distribute_fees(

if $wallet_addresses IS NOT NULL AND COALESCE(array_length($wallet_addresses), 0) > 0 {
$lp_count := array_length($wallet_addresses);
$actual_fees_distributed := $total_fees;
$actual_fees_distributed := $lp_share;

-- Step 4: Batch unlock to all qualifying LPs
-- Step 6: Batch unlock to all qualifying LPs
ob_batch_unlock_collateral($bridge, $wallet_addresses, $amounts);
}
}

-- Step 5: Insert distribution summary
-- Step 7: Insert distribution summary
INSERT INTO ob_fee_distributions (
id,
query_id,
total_fees_distributed,
total_dp_fees,
total_validator_fees,
total_lp_count,
block_count,
distributed_at
) VALUES (
$distribution_id,
$query_id,
$actual_fees_distributed,
$actual_dp_fees,
$actual_validator_fees,
$lp_count,
$block_count,
@block_timestamp
);

-- Step 6: Insert per-LP details (only if LPs exist)
-- Step 8: Insert per-LP details (only if LPs exist)
if $lp_count > 0 {
for $payout in SELECT wallet, amount FROM UNNEST($wallet_addresses, $amounts) AS p(wallet, amount) {
$wallet_hex TEXT := $payout.wallet;
Expand Down Expand Up @@ -276,7 +324,7 @@ CREATE OR REPLACE ACTION distribute_fees(
}
}

-- Step 7: Cleanup
-- Step 9: Cleanup
if $lp_count > 0 {
DELETE FROM ob_rewards WHERE query_id = $query_id;
}
Expand Down
18 changes: 13 additions & 5 deletions internal/migrations/036-order-book-audit.sql
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@
CREATE TABLE IF NOT EXISTS ob_fee_distributions (
id INT PRIMARY KEY,
query_id INT NOT NULL,
total_fees_distributed NUMERIC(78, 0) NOT NULL,
total_fees_distributed NUMERIC(78, 0) NOT NULL, -- Total LP fees
total_dp_fees NUMERIC(78, 0) NOT NULL DEFAULT 0,
total_validator_fees NUMERIC(78, 0) NOT NULL DEFAULT 0,
total_lp_count INT NOT NULL,
block_count INT NOT NULL,
distributed_at INT8 NOT NULL,
Expand Down Expand Up @@ -151,23 +153,27 @@ CREATE OR REPLACE ACTION get_distribution_summary(
$query_id INT
) PUBLIC VIEW RETURNS TABLE(
distribution_id INT,
total_fees_distributed NUMERIC(78, 0),
total_lp_fees_distributed NUMERIC(78, 0),
total_dp_fees NUMERIC(78, 0),
total_validator_fees NUMERIC(78, 0),
total_lp_count INT,
block_count INT,
distributed_at INT8
) {
for $row in
SELECT
id as distribution_id,
total_fees_distributed,
total_fees_distributed as total_lp_fees_distributed,
total_dp_fees,
total_validator_fees,
total_lp_count,
block_count,
distributed_at
FROM ob_fee_distributions
WHERE query_id = $query_id
ORDER BY distributed_at DESC
{
RETURN NEXT $row.distribution_id, $row.total_fees_distributed, $row.total_lp_count, $row.block_count, $row.distributed_at;
RETURN NEXT $row.distribution_id, $row.total_lp_fees_distributed, $row.total_dp_fees, $row.total_validator_fees, $row.total_lp_count, $row.block_count, $row.distributed_at;
}
};

Expand All @@ -191,20 +197,22 @@ CREATE OR REPLACE ACTION get_distribution_summary(
CREATE OR REPLACE ACTION get_distribution_details(
$distribution_id INT
) PUBLIC VIEW RETURNS TABLE(
participant_id INT,
wallet_address TEXT,
reward_amount NUMERIC(78, 0),
total_reward_percent NUMERIC(10, 2)
) {
for $row in
SELECT
participant_id,
'0x' || encode(wallet_address, 'hex') as wallet_hex,
reward_amount,
total_reward_percent
FROM ob_fee_distribution_details
WHERE distribution_id = $distribution_id
ORDER BY reward_amount DESC
{
RETURN NEXT $row.wallet_hex, $row.reward_amount, $row.total_reward_percent;
RETURN NEXT $row.participant_id, $row.wallet_hex, $row.reward_amount, $row.total_reward_percent;
}
};

Expand Down
6 changes: 3 additions & 3 deletions internal/migrations/042-lp-rewards-config.sql
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
CREATE TABLE IF NOT EXISTS lp_rewards_config (
id INT PRIMARY KEY DEFAULT 1,
enabled BOOL NOT NULL DEFAULT TRUE,
sampling_interval_blocks INT NOT NULL DEFAULT 10,
sampling_interval_blocks INT NOT NULL DEFAULT 50,
max_markets_per_run INT NOT NULL DEFAULT 1000,
CHECK (id = 1),
CHECK (sampling_interval_blocks >= 1),
Expand All @@ -41,7 +41,7 @@ CREATE TABLE IF NOT EXISTS lp_rewards_config (

-- Insert default configuration
INSERT INTO lp_rewards_config (id, enabled, sampling_interval_blocks, max_markets_per_run)
VALUES (1, TRUE, 10, 1000)
VALUES (1, TRUE, 50, 1000)
ON CONFLICT (id) DO NOTHING;

-- ============================================================================
Expand All @@ -66,7 +66,7 @@ PUBLIC VIEW RETURNS (
RETURN $row.enabled, $row.sampling_interval_blocks, $row.max_markets_per_run;
}
-- Default if no row exists
RETURN TRUE, 10, 1000;
RETURN TRUE, 50, 1000;
};

/**
Expand Down