diff --git a/internal/migrations/023-fee-configs.sql b/internal/migrations/023-fee-configs.sql new file mode 100644 index 000000000..03796aaf1 --- /dev/null +++ b/internal/migrations/023-fee-configs.sql @@ -0,0 +1,28 @@ +-- Fee configuration table for ERC20 bridge operations +-- Stores configurable fees and treasury address for deposit and withdraw operations + +CREATE TABLE IF NOT EXISTS fee_configs ( + operation_type TEXT NOT NULL, + fee_percentage NUMERIC(4, 4) NOT NULL, + treasury_address TEXT NOT NULL, + created_at INT8 NOT NULL, + updated_at INT8 NOT NULL, + + PRIMARY KEY (operation_type), + + -- Constraints + CHECK (operation_type IN ('deposit', 'withdraw')), + CHECK (fee_percentage >= 0 AND fee_percentage <= 1), -- 0% to 100% + CHECK (treasury_address LIKE '0x%' AND LENGTH(treasury_address) = 42) +); + +-- Action to get fee configuration by operation type +CREATE OR REPLACE ACTION get_fee_config_by_type($operation_type TEXT) +PUBLIC VIEW RETURNS ( + fee_percentage NUMERIC(4, 4), + treasury_address TEXT +) { + FOR $config IN SELECT fee_percentage, treasury_address FROM fee_configs WHERE operation_type = $operation_type { + RETURN $config.fee_percentage, $config.treasury_address; + } +}; diff --git a/internal/migrations/024-erc20-bridge.sql b/internal/migrations/024-erc20-bridge.sql new file mode 100644 index 000000000..8772354da --- /dev/null +++ b/internal/migrations/024-erc20-bridge.sql @@ -0,0 +1,70 @@ +CREATE OR REPLACE ACTION get_erc20_bridge_info() +PUBLIC VIEW RETURNS ( + chain TEXT, + escrow TEXT, + epoch_period TEXT, + erc20 TEXT, + decimals INT, + balance NUMERIC(78, 0), + synced BOOLEAN, + synced_at INT8, + enabled BOOLEAN +) { + FOR $row IN sepolia_bridge.info() { + RETURN $row.chain, $row.escrow, $row.epoch_period, $row.erc20, $row.decimals, $row.balance, $row.synced, $row.synced_at, $row.enabled; + } +}; + +-- TESTNET +CREATE OR REPLACE ACTION sepolia_wallet_balance($wallet_address TEXT) PUBLIC VIEW RETURNS (balance NUMERIC(78, 0)) { + $balance := sepolia_bridge.balance($wallet_address); + RETURN $balance; +}; + +CREATE OR REPLACE ACTION sepolia_admin_bridge_tokens($amount TEXT) PUBLIC { + $num_amount := $amount::NUMERIC(78, 0); + + -- Get fee configuration for withdraw + $found := 0; + FOR $config IN SELECT fee_percentage, treasury_address FROM fee_configs WHERE operation_type = 'withdraw' ORDER BY created_at DESC LIMIT 1 { + $fee := $num_amount * $config.fee_percentage; + + IF $fee > 0::NUMERIC(78, 0) { + sepolia_bridge.lock($fee); + sepolia_bridge.unlock($config.treasury_address, $fee); + } + + -- Bridge the rest to users + sepolia_bridge.bridge($num_amount - $fee); + $found := 1; + } + + IF $found = 0 { ERROR('No fee_configs rows for operation_type=withdraw'); } +}; + +-- MAINNET +CREATE OR REPLACE ACTION mainnet_wallet_balance($wallet_address TEXT) PUBLIC VIEW RETURNS (balance NUMERIC(78, 0)) { + $balance := mainnet_bridge.balance($wallet_address); + RETURN $balance; +}; + +CREATE OR REPLACE ACTION mainnet_admin_bridge_tokens($amount TEXT) PUBLIC { + $num_amount := $amount::NUMERIC(78, 0); + + -- Get fee configuration for withdraw + $found := 0; + FOR $config IN SELECT fee_percentage, treasury_address FROM fee_configs WHERE operation_type = 'withdraw' ORDER BY created_at DESC LIMIT 1 { + $fee := $num_amount * $config.fee_percentage; + + IF $fee > 0::NUMERIC(78, 0) { + mainnet_bridge.lock($fee); + mainnet_bridge.unlock($config.treasury_address, $fee); + } + + -- Bridge the rest to users + mainnet_bridge.bridge($num_amount - $fee); + $found := 1; + } + + IF $found = 0 { ERROR('No fee_configs rows for operation_type=withdraw'); } +}; \ No newline at end of file diff --git a/internal/migrations/erc20-bridge/001-actions.sql b/internal/migrations/erc20-bridge/001-actions.sql deleted file mode 100644 index 517a85163..000000000 --- a/internal/migrations/erc20-bridge/001-actions.sql +++ /dev/null @@ -1,55 +0,0 @@ -CREATE OR REPLACE ACTION get_erc20_bridge_info() -PUBLIC VIEW RETURNS ( - chain TEXT, - escrow TEXT, - epoch_period TEXT, - erc20 TEXT, - decimals INT, - balance NUMERIC(78, 0), - synced BOOLEAN, - synced_at INT8, - enabled BOOLEAN -) { - FOR $row IN sepolia_bridge.info() { - RETURN $row.chain, $row.escrow, $row.epoch_period, $row.erc20, $row.decimals, $row.balance, $row.synced, $row.synced_at, $row.enabled; - } -}; - --- TESTNET -CREATE OR REPLACE ACTION sepolia_wallet_balance($wallet_address TEXT) PUBLIC VIEW RETURNS (balance NUMERIC(78, 0)) { - $balance := sepolia_bridge.balance($wallet_address); - RETURN $balance; -}; - -CREATE OR REPLACE ACTION sepolia_admin_bridge_tokens($amount TEXT) PUBLIC { - -- Calculate 1% fee and lock it in our treasury - $num_amount := $amount::NUMERIC(78, 0); - $fee := $num_amount * 0.01; - sepolia_bridge.lock($fee); - - $treasury_address := '0xDe5B2aBce299eBdC3567895B1B4b02Ca2c33C94A'; - sepolia_bridge.unlock($treasury_address, $fee); - - -- Bridge the rest to users - sepolia_bridge.bridge($num_amount - $fee); -}; - --- MAINNET -CREATE OR REPLACE ACTION mainnet_wallet_balance($wallet_address TEXT) PUBLIC VIEW RETURNS (balance NUMERIC(78, 0)) { - $balance := mainnet_bridge.balance($wallet_address); - RETURN $balance; -}; - -CREATE OR REPLACE ACTION mainnet_admin_bridge_tokens($amount TEXT) PUBLIC { - -- Calculate 1% fee and lock it in our treasury - $numAmount := $amount::NUMERIC(78, 0); - $fee := $numAmount * 0.01; - mainnet_bridge.lock($fee); - - -- TODO: update when we have treasury address on mainnet - -- $treasury_address := '' - -- sepolia_bridge.unlock($treasury_address, $fee); - - -- Bridge the rest to users - mainnet_bridge.bridge($numAmount - $fee); -}; \ No newline at end of file