From f240b96847dfbd599405e898c6e122f14ebf104c Mon Sep 17 00:00:00 2001 From: Face <69168154+face-hh@users.noreply.github.com> Date: Sat, 31 May 2025 20:49:39 +0300 Subject: [PATCH] Reapply "feat: implement auto-pump mechanism to prevent pool drainage" This reverts commit fdfba7efd291df0c300f916f2183e7e95f34b19e. --- website/drizzle/0003_adorable_leper_queen.sql | 4 + website/drizzle/meta/0003_snapshot.json | 1587 +++++++++++++++++ website/drizzle/meta/_journal.json | 7 + .../src/lib/components/self/TradeModal.svelte | 34 + website/src/lib/server/db/schema.ts | 4 + website/src/lib/types/market.ts | 2 + .../routes/api/coin/[coinSymbol]/+server.ts | 6 +- .../api/coin/[coinSymbol]/trade/+server.ts | 112 +- 8 files changed, 1727 insertions(+), 29 deletions(-) create mode 100644 website/drizzle/0003_adorable_leper_queen.sql create mode 100644 website/drizzle/meta/0003_snapshot.json diff --git a/website/drizzle/0003_adorable_leper_queen.sql b/website/drizzle/0003_adorable_leper_queen.sql new file mode 100644 index 0000000..861536d --- /dev/null +++ b/website/drizzle/0003_adorable_leper_queen.sql @@ -0,0 +1,4 @@ +ALTER TABLE "coin" ADD COLUMN "pump_fee_rate" numeric(10, 8) DEFAULT '0.00500000' NOT NULL;--> statement-breakpoint +ALTER TABLE "coin" ADD COLUMN "burn_rate" numeric(10, 8) DEFAULT '0.00100000' NOT NULL;--> statement-breakpoint +ALTER TABLE "transaction" ADD COLUMN "pump_fee_applied" numeric(30, 8) DEFAULT '0.00000000';--> statement-breakpoint +ALTER TABLE "transaction" ADD COLUMN "tokens_burned" numeric(30, 8) DEFAULT '0.00000000'; \ No newline at end of file diff --git a/website/drizzle/meta/0003_snapshot.json b/website/drizzle/meta/0003_snapshot.json new file mode 100644 index 0000000..8e5c2b5 --- /dev/null +++ b/website/drizzle/meta/0003_snapshot.json @@ -0,0 +1,1587 @@ +{ + "id": "fe886c44-c30d-423a-8f04-82b880a2eb39", + "prevId": "42dd73ba-ecbe-4b72-aa93-3bfbfee8f534", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.account_deletion_request": { + "name": "account_deletion_request", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "scheduled_deletion_at": { + "name": "scheduled_deletion_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_processed": { + "name": "is_processed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "account_deletion_request_user_id_idx": { + "name": "account_deletion_request_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "account_deletion_request_scheduled_deletion_idx": { + "name": "account_deletion_request_scheduled_deletion_idx", + "columns": [ + { + "expression": "scheduled_deletion_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "account_deletion_request_open_idx": { + "name": "account_deletion_request_open_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "is_processed = false", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_deletion_request_user_id_user_id_fk": { + "name": "account_deletion_request_user_id_user_id_fk", + "tableFrom": "account_deletion_request", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "account_deletion_request_user_id_unique": { + "name": "account_deletion_request_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + } + }, + "public.coin": { + "name": "coin", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "symbol": { + "name": "symbol", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "creator_id": { + "name": "creator_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "initial_supply": { + "name": "initial_supply", + "type": "numeric(30, 8)", + "primaryKey": false, + "notNull": true + }, + "circulating_supply": { + "name": "circulating_supply", + "type": "numeric(30, 8)", + "primaryKey": false, + "notNull": true + }, + "current_price": { + "name": "current_price", + "type": "numeric(20, 8)", + "primaryKey": false, + "notNull": true + }, + "market_cap": { + "name": "market_cap", + "type": "numeric(30, 2)", + "primaryKey": false, + "notNull": true + }, + "volume_24h": { + "name": "volume_24h", + "type": "numeric(30, 2)", + "primaryKey": false, + "notNull": false, + "default": "'0.00'" + }, + "change_24h": { + "name": "change_24h", + "type": "numeric(30, 4)", + "primaryKey": false, + "notNull": false, + "default": "'0.0000'" + }, + "pool_coin_amount": { + "name": "pool_coin_amount", + "type": "numeric(30, 8)", + "primaryKey": false, + "notNull": true, + "default": "'0.00000000'" + }, + "pool_base_currency_amount": { + "name": "pool_base_currency_amount", + "type": "numeric(30, 8)", + "primaryKey": false, + "notNull": true, + "default": "'0.00000000'" + }, + "pump_fee_rate": { + "name": "pump_fee_rate", + "type": "numeric(10, 8)", + "primaryKey": false, + "notNull": true, + "default": "'0.00500000'" + }, + "burn_rate": { + "name": "burn_rate", + "type": "numeric(10, 8)", + "primaryKey": false, + "notNull": true, + "default": "'0.00100000'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_listed": { + "name": "is_listed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + } + }, + "indexes": {}, + "foreignKeys": { + "coin_creator_id_user_id_fk": { + "name": "coin_creator_id_user_id_fk", + "tableFrom": "coin", + "tableTo": "user", + "columnsFrom": [ + "creator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "coin_symbol_unique": { + "name": "coin_symbol_unique", + "nullsNotDistinct": false, + "columns": [ + "symbol" + ] + } + } + }, + "public.comment": { + "name": "comment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "coin_id": { + "name": "coin_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "varchar(500)", + "primaryKey": false, + "notNull": true + }, + "likes_count": { + "name": "likes_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_deleted": { + "name": "is_deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "comment_user_id_idx": { + "name": "comment_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "comment_coin_id_idx": { + "name": "comment_coin_id_idx", + "columns": [ + { + "expression": "coin_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "comment_user_id_user_id_fk": { + "name": "comment_user_id_user_id_fk", + "tableFrom": "comment", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "comment_coin_id_coin_id_fk": { + "name": "comment_coin_id_coin_id_fk", + "tableFrom": "comment", + "tableTo": "coin", + "columnsFrom": [ + "coin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.comment_like": { + "name": "comment_like", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "comment_id": { + "name": "comment_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "comment_like_user_id_user_id_fk": { + "name": "comment_like_user_id_user_id_fk", + "tableFrom": "comment_like", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "comment_like_comment_id_comment_id_fk": { + "name": "comment_like_comment_id_comment_id_fk", + "tableFrom": "comment_like", + "tableTo": "comment", + "columnsFrom": [ + "comment_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "comment_like_user_id_comment_id_pk": { + "name": "comment_like_user_id_comment_id_pk", + "columns": [ + "user_id", + "comment_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.prediction_bet": { + "name": "prediction_bet", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "question_id": { + "name": "question_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "side": { + "name": "side", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "amount": { + "name": "amount", + "type": "numeric(20, 8)", + "primaryKey": false, + "notNull": true + }, + "actual_winnings": { + "name": "actual_winnings", + "type": "numeric(20, 8)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "settled_at": { + "name": "settled_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "prediction_bet_user_id_idx": { + "name": "prediction_bet_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "prediction_bet_question_id_idx": { + "name": "prediction_bet_question_id_idx", + "columns": [ + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "prediction_bet_user_question_idx": { + "name": "prediction_bet_user_question_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "question_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "prediction_bet_created_at_idx": { + "name": "prediction_bet_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "prediction_bet_user_id_user_id_fk": { + "name": "prediction_bet_user_id_user_id_fk", + "tableFrom": "prediction_bet", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "prediction_bet_question_id_prediction_question_id_fk": { + "name": "prediction_bet_question_id_prediction_question_id_fk", + "tableFrom": "prediction_bet", + "tableTo": "prediction_question", + "columnsFrom": [ + "question_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.prediction_question": { + "name": "prediction_question", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "creator_id": { + "name": "creator_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "question": { + "name": "question", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "prediction_market_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ACTIVE'" + }, + "resolution_date": { + "name": "resolution_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ai_resolution": { + "name": "ai_resolution", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "total_yes_amount": { + "name": "total_yes_amount", + "type": "numeric(20, 8)", + "primaryKey": false, + "notNull": true, + "default": "'0.00000000'" + }, + "total_no_amount": { + "name": "total_no_amount", + "type": "numeric(20, 8)", + "primaryKey": false, + "notNull": true, + "default": "'0.00000000'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "requires_web_search": { + "name": "requires_web_search", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "validation_reason": { + "name": "validation_reason", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "prediction_question_creator_id_idx": { + "name": "prediction_question_creator_id_idx", + "columns": [ + { + "expression": "creator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "prediction_question_status_idx": { + "name": "prediction_question_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "prediction_question_resolution_date_idx": { + "name": "prediction_question_resolution_date_idx", + "columns": [ + { + "expression": "resolution_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "prediction_question_creator_id_user_id_fk": { + "name": "prediction_question_creator_id_user_id_fk", + "tableFrom": "prediction_question", + "tableTo": "user", + "columnsFrom": [ + "creator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.price_history": { + "name": "price_history", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "coin_id": { + "name": "coin_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "price": { + "name": "price", + "type": "numeric(20, 8)", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "price_history_coin_id_coin_id_fk": { + "name": "price_history_coin_id_coin_id_fk", + "tableFrom": "price_history", + "tableTo": "coin", + "columnsFrom": [ + "coin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.promo_code": { + "name": "promo_code", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "code": { + "name": "code", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reward_amount": { + "name": "reward_amount", + "type": "numeric(20, 8)", + "primaryKey": false, + "notNull": true + }, + "max_uses": { + "name": "max_uses", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_by": { + "name": "created_by", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "promo_code_created_by_user_id_fk": { + "name": "promo_code_created_by_user_id_fk", + "tableFrom": "promo_code", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "promo_code_code_unique": { + "name": "promo_code_code_unique", + "nullsNotDistinct": false, + "columns": [ + "code" + ] + } + } + }, + "public.promo_code_redemption": { + "name": "promo_code_redemption", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "promo_code_id": { + "name": "promo_code_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reward_amount": { + "name": "reward_amount", + "type": "numeric(20, 8)", + "primaryKey": false, + "notNull": true + }, + "redeemed_at": { + "name": "redeemed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "promo_code_redemption_user_id_user_id_fk": { + "name": "promo_code_redemption_user_id_user_id_fk", + "tableFrom": "promo_code_redemption", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "promo_code_redemption_promo_code_id_promo_code_id_fk": { + "name": "promo_code_redemption_promo_code_id_promo_code_id_fk", + "tableFrom": "promo_code_redemption", + "tableTo": "promo_code", + "columnsFrom": [ + "promo_code_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "promo_code_redemption_user_id_promo_code_id_unique": { + "name": "promo_code_redemption_user_id_promo_code_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id", + "promo_code_id" + ] + } + } + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + } + }, + "public.transaction": { + "name": "transaction", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "coin_id": { + "name": "coin_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "transaction_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "numeric(30, 8)", + "primaryKey": false, + "notNull": true + }, + "price_per_coin": { + "name": "price_per_coin", + "type": "numeric(20, 8)", + "primaryKey": false, + "notNull": true + }, + "total_base_currency_amount": { + "name": "total_base_currency_amount", + "type": "numeric(30, 8)", + "primaryKey": false, + "notNull": true + }, + "pump_fee_applied": { + "name": "pump_fee_applied", + "type": "numeric(30, 8)", + "primaryKey": false, + "notNull": false, + "default": "'0.00000000'" + }, + "tokens_burned": { + "name": "tokens_burned", + "type": "numeric(30, 8)", + "primaryKey": false, + "notNull": false, + "default": "'0.00000000'" + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "recipient_user_id": { + "name": "recipient_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sender_user_id": { + "name": "sender_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "transaction_user_id_user_id_fk": { + "name": "transaction_user_id_user_id_fk", + "tableFrom": "transaction", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "transaction_coin_id_coin_id_fk": { + "name": "transaction_coin_id_coin_id_fk", + "tableFrom": "transaction", + "tableTo": "coin", + "columnsFrom": [ + "coin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "transaction_recipient_user_id_user_id_fk": { + "name": "transaction_recipient_user_id_user_id_fk", + "tableFrom": "transaction", + "tableTo": "user", + "columnsFrom": [ + "recipient_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "transaction_sender_user_id_user_id_fk": { + "name": "transaction_sender_user_id_user_id_fk", + "tableFrom": "transaction", + "tableTo": "user", + "columnsFrom": [ + "sender_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_banned": { + "name": "is_banned", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "base_currency_balance": { + "name": "base_currency_balance", + "type": "numeric(20, 8)", + "primaryKey": false, + "notNull": true, + "default": "'100.00000000'" + }, + "bio": { + "name": "bio", + "type": "varchar(160)", + "primaryKey": false, + "notNull": false, + "default": "'Hello am 48 year old man from somalia. Sorry for my bed england. I selled my wife for internet connection for play “conter stirk”'" + }, + "username": { + "name": "username", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true + }, + "volume_master": { + "name": "volume_master", + "type": "numeric(3, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0.70'" + }, + "volume_muted": { + "name": "volume_muted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "last_reward_claim": { + "name": "last_reward_claim", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "total_rewards_claimed": { + "name": "total_rewards_claimed", + "type": "numeric(20, 8)", + "primaryKey": false, + "notNull": true, + "default": "'0.00000000'" + }, + "login_streak": { + "name": "login_streak", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "user_username_unique": { + "name": "user_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + } + } + }, + "public.user_portfolio": { + "name": "user_portfolio", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "coin_id": { + "name": "coin_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "numeric(30, 8)", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_portfolio_user_id_user_id_fk": { + "name": "user_portfolio_user_id_user_id_fk", + "tableFrom": "user_portfolio", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_portfolio_coin_id_coin_id_fk": { + "name": "user_portfolio_coin_id_coin_id_fk", + "tableFrom": "user_portfolio", + "tableTo": "coin", + "columnsFrom": [ + "coin_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "user_portfolio_user_id_coin_id_pk": { + "name": "user_portfolio_user_id_coin_id_pk", + "columns": [ + "user_id", + "coin_id" + ] + } + }, + "uniqueConstraints": {} + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "public.prediction_market_status": { + "name": "prediction_market_status", + "schema": "public", + "values": [ + "ACTIVE", + "RESOLVED", + "CANCELLED" + ] + }, + "public.transaction_type": { + "name": "transaction_type", + "schema": "public", + "values": [ + "BUY", + "SELL", + "TRANSFER_IN", + "TRANSFER_OUT" + ] + } + }, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/website/drizzle/meta/_journal.json b/website/drizzle/meta/_journal.json index 18066b9..fb71918 100644 --- a/website/drizzle/meta/_journal.json +++ b/website/drizzle/meta/_journal.json @@ -22,6 +22,13 @@ "when": 1748700252762, "tag": "0002_lush_guardian", "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1748710560443, + "tag": "0003_adorable_leper_queen", + "breakpoints": true } ] } \ No newline at end of file diff --git a/website/src/lib/components/self/TradeModal.svelte b/website/src/lib/components/self/TradeModal.svelte index 417d6e7..242b870 100644 --- a/website/src/lib/components/self/TradeModal.svelte +++ b/website/src/lib/components/self/TradeModal.svelte @@ -34,6 +34,7 @@ : userHolding ); let estimatedResult = $derived(calculateEstimate(numericAmount, type, currentPrice)); + let estimatedAutoPump = $derived(calculateAutoPumpEffects(numericAmount, type, coin)); let hasValidAmount = $derived(numericAmount > 0); let userBalance = $derived($PORTFOLIO_DATA ? $PORTFOLIO_DATA.baseCurrencyBalance : 0); let hasEnoughFunds = $derived( @@ -64,6 +65,25 @@ } } + function calculateAutoPumpEffects(amount: number, tradeType: 'BUY' | 'SELL', coinData: any) { + if (!amount) return { fee: 0, burn: 0 }; + + const pumpFeeRate = Number(coinData.pumpFeeRate || 0.005); + const burnRate = Number(coinData.burnRate || 0.001); + + if (tradeType === 'BUY') { + const fee = amount * pumpFeeRate; + const estimatedTokens = calculateEstimate(amount, tradeType, currentPrice).result; + const burn = estimatedTokens * burnRate; + return { fee, burn }; + } else { + const estimatedValue = calculateEstimate(amount, tradeType, currentPrice).result; + const fee = estimatedValue * pumpFeeRate; + const burn = amount * burnRate; + return { fee, burn }; + } + } + function handleClose() { open = false; amount = ''; @@ -183,6 +203,20 @@ : `~$${estimatedResult.result.toFixed(6)}`} + {#if estimatedAutoPump.fee > 0 || estimatedAutoPump.burn > 0} +
AMM estimation - includes slippage from pool impact
diff --git a/website/src/lib/server/db/schema.ts b/website/src/lib/server/db/schema.ts index 666d265..881fa1f 100644 --- a/website/src/lib/server/db/schema.ts +++ b/website/src/lib/server/db/schema.ts @@ -83,6 +83,8 @@ export const coin = pgTable("coin", { change24h: decimal("change_24h", { precision: 30, scale: 4 }).default("0.0000"), // Percentage poolCoinAmount: decimal("pool_coin_amount", { precision: 30, scale: 8 }).notNull().default("0.00000000"), poolBaseCurrencyAmount: decimal("pool_base_currency_amount", { precision: 30, scale: 8, }).notNull().default("0.00000000"), + pumpFeeRate: decimal("pump_fee_rate", { precision: 10, scale: 8 }).notNull().default("0.00500000"), // 0.5% + burnRate: decimal("burn_rate", { precision: 10, scale: 8 }).notNull().default("0.00100000"), // 0.1% createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), isListed: boolean("is_listed").default(true).notNull(), @@ -109,6 +111,8 @@ export const transaction = pgTable("transaction", { quantity: decimal("quantity", { precision: 30, scale: 8 }).notNull(), pricePerCoin: decimal("price_per_coin", { precision: 20, scale: 8 }).notNull(), totalBaseCurrencyAmount: decimal("total_base_currency_amount", { precision: 30, scale: 8 }).notNull(), + pumpFeeApplied: decimal("pump_fee_applied", { precision: 30, scale: 8 }).default("0.00000000"), + tokensBurned: decimal("tokens_burned", { precision: 30, scale: 8 }).default("0.00000000"), timestamp: timestamp("timestamp", { withTimezone: true }).notNull().defaultNow(), recipientUserId: integer('recipient_user_id').references(() => user.id, { onDelete: 'set null' }), senderUserId: integer('sender_user_id').references(() => user.id, { onDelete: 'set null' }), diff --git a/website/src/lib/types/market.ts b/website/src/lib/types/market.ts index 3fb508c..e73b2ce 100644 --- a/website/src/lib/types/market.ts +++ b/website/src/lib/types/market.ts @@ -8,6 +8,8 @@ export interface CoinData { change24h: number; createdAt: string; creatorName: string | null; + pumpFeeRate?: number; + burnRate?: number; } export interface MarketFilters { diff --git a/website/src/routes/api/coin/[coinSymbol]/+server.ts b/website/src/routes/api/coin/[coinSymbol]/+server.ts index 818bca8..1466dd9 100644 --- a/website/src/routes/api/coin/[coinSymbol]/+server.ts +++ b/website/src/routes/api/coin/[coinSymbol]/+server.ts @@ -130,6 +130,8 @@ export async function GET({ params, url }) { isListed: coin.isListed, createdAt: coin.createdAt, creatorId: coin.creatorId, + pumpFeeRate: coin.pumpFeeRate, + burnRate: coin.burnRate, creatorName: user.name, creatorUsername: user.username, creatorBio: user.bio, @@ -184,7 +186,9 @@ export async function GET({ params, url }) { poolCoinAmount: Number(coinData.poolCoinAmount), poolBaseCurrencyAmount: Number(coinData.poolBaseCurrencyAmount), circulatingSupply: Number(coinData.circulatingSupply), - initialSupply: Number(coinData.initialSupply) + initialSupply: Number(coinData.initialSupply), + pumpFeeRate: Number(coinData.pumpFeeRate), + burnRate: Number(coinData.burnRate) }, candlestickData, volumeData, diff --git a/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts b/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts index 0696cea..710bbcf 100644 --- a/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts +++ b/website/src/routes/api/coin/[coinSymbol]/trade/+server.ts @@ -42,6 +42,34 @@ async function calculate24hMetrics(coinId: number, currentPrice: number) { return { change24h: Number(change24h.toFixed(4)), volume24h: Number(volume24h.toFixed(4)) }; } +function calculateAutoPumpEffects( + coinData: any, + transactionValue: number, + tokensTraded: number +) { + const pumpFeeRate = Number(coinData.pumpFeeRate); + const burnRate = Number(coinData.burnRate); + + const pumpFee = transactionValue * pumpFeeRate; + const tokensBurned = tokensTraded * burnRate; + + // Ensure we don't burn more tokens than available in pool + const maxBurnableTokens = Number(coinData.poolCoinAmount) * 0.99; // Keep 1% minimum + const actualTokensBurned = Math.min(tokensBurned, maxBurnableTokens); + + const newPoolBase = Number(coinData.poolBaseCurrencyAmount) + pumpFee; + const newPoolCoin = Number(coinData.poolCoinAmount) - actualTokensBurned; + const newCirculatingSupply = Number(coinData.circulatingSupply) - actualTokensBurned; + + return { + pumpFee, + tokensBurned: actualTokensBurned, + newPoolBase, + newPoolCoin, + newCirculatingSupply + }; +} + export async function POST({ params, request }) { const session = await auth.api.getSession({ headers: request.headers @@ -127,6 +155,13 @@ export async function POST({ params, request }) { throw error(400, 'Trade amount too small - would result in zero tokens'); } + // Apply auto-pump effects + const autoPumpEffects = calculateAutoPumpEffects(coinData, amount, coinsBought); + + const finalPoolBase = autoPumpEffects.newPoolBase; + const finalPoolCoin = autoPumpEffects.newPoolCoin; + const finalPrice = finalPoolBase / finalPoolCoin; + await tx.update(user) .set({ baseCurrencyBalance: (userBalance - totalCost).toString(), @@ -168,22 +203,25 @@ export async function POST({ params, request }) { type: 'BUY', quantity: coinsBought.toString(), pricePerCoin: (totalCost / coinsBought).toString(), - totalBaseCurrencyAmount: totalCost.toString() + totalBaseCurrencyAmount: totalCost.toString(), + pumpFeeApplied: autoPumpEffects.pumpFee.toString(), + tokensBurned: autoPumpEffects.tokensBurned.toString() }); await tx.insert(priceHistory).values({ coinId: coinData.id, - price: newPrice.toString() + price: finalPrice.toString() }); - const metrics = await calculate24hMetrics(coinData.id, newPrice); + const metrics = await calculate24hMetrics(coinData.id, finalPrice); await tx.update(coin) .set({ - currentPrice: newPrice.toString(), - marketCap: (Number(coinData.circulatingSupply) * newPrice).toString(), - poolCoinAmount: newPoolCoin.toString(), - poolBaseCurrencyAmount: newPoolBaseCurrency.toString(), + currentPrice: finalPrice.toString(), + marketCap: (autoPumpEffects.newCirculatingSupply * finalPrice).toString(), + poolCoinAmount: finalPoolCoin.toString(), + poolBaseCurrencyAmount: finalPoolBase.toString(), + circulatingSupply: autoPumpEffects.newCirculatingSupply.toString(), change24h: metrics.change24h.toString(), volume24h: metrics.volume24h.toString(), updatedAt: new Date() @@ -191,12 +229,12 @@ export async function POST({ params, request }) { .where(eq(coin.id, coinData.id)); const priceUpdateData = { - currentPrice: newPrice, - marketCap: Number(coinData.circulatingSupply) * newPrice, + currentPrice: finalPrice, + marketCap: autoPumpEffects.newCirculatingSupply * finalPrice, change24h: metrics.change24h, volume24h: metrics.volume24h, - poolCoinAmount: newPoolCoin, - poolBaseCurrencyAmount: newPoolBaseCurrency + poolCoinAmount: finalPoolCoin, + poolBaseCurrencyAmount: finalPoolBase }; const tradeData = { @@ -232,9 +270,13 @@ export async function POST({ params, request }) { type: 'BUY', coinsBought, totalCost, - newPrice, - priceImpact, - newBalance: userBalance - totalCost + newPrice: finalPrice, + priceImpact: ((finalPrice - currentPrice) / currentPrice) * 100, + newBalance: userBalance - totalCost, + autoPumpEffects: { + feeApplied: autoPumpEffects.pumpFee, + tokensBurned: autoPumpEffects.tokensBurned, + } }); } else { @@ -275,6 +317,13 @@ export async function POST({ params, request }) { throw error(400, 'Trade amount results in zero base currency received'); } + // Apply auto-pump effects + const autoPumpEffects = calculateAutoPumpEffects(coinData, totalCost, amount); + + const finalPoolBase = autoPumpEffects.newPoolBase; + const finalPoolCoin = autoPumpEffects.newPoolCoin; + const finalPrice = finalPoolBase / finalPoolCoin; + await tx.update(user) .set({ baseCurrencyBalance: (userBalance + totalCost).toString(), @@ -307,22 +356,25 @@ export async function POST({ params, request }) { type: 'SELL', quantity: amount.toString(), pricePerCoin: (totalCost / amount).toString(), - totalBaseCurrencyAmount: totalCost.toString() + totalBaseCurrencyAmount: totalCost.toString(), + pumpFeeApplied: autoPumpEffects.pumpFee.toString(), + tokensBurned: autoPumpEffects.tokensBurned.toString() }); await tx.insert(priceHistory).values({ coinId: coinData.id, - price: newPrice.toString() + price: finalPrice.toString() }); - const metrics = await calculate24hMetrics(coinData.id, newPrice); + const metrics = await calculate24hMetrics(coinData.id, finalPrice); await tx.update(coin) .set({ - currentPrice: newPrice.toString(), - marketCap: (Number(coinData.circulatingSupply) * newPrice).toString(), - poolCoinAmount: newPoolCoin.toString(), - poolBaseCurrencyAmount: newPoolBaseCurrency.toString(), + currentPrice: finalPrice.toString(), + marketCap: (autoPumpEffects.newCirculatingSupply * finalPrice).toString(), + poolCoinAmount: finalPoolCoin.toString(), + poolBaseCurrencyAmount: finalPoolBase.toString(), + circulatingSupply: autoPumpEffects.newCirculatingSupply.toString(), change24h: metrics.change24h.toString(), volume24h: metrics.volume24h.toString(), updatedAt: new Date() @@ -330,12 +382,12 @@ export async function POST({ params, request }) { .where(eq(coin.id, coinData.id)); const priceUpdateData = { - currentPrice: newPrice, - marketCap: Number(coinData.circulatingSupply) * newPrice, + currentPrice: finalPrice, + marketCap: autoPumpEffects.newCirculatingSupply * finalPrice, change24h: metrics.change24h, volume24h: metrics.volume24h, - poolCoinAmount: newPoolCoin, - poolBaseCurrencyAmount: newPoolBaseCurrency + poolCoinAmount: finalPoolCoin, + poolBaseCurrencyAmount: finalPoolBase }; const tradeData = { @@ -371,9 +423,13 @@ export async function POST({ params, request }) { type: 'SELL', coinsSold: amount, totalReceived: totalCost, - newPrice, - priceImpact, - newBalance: userBalance + totalCost + newPrice: finalPrice, + priceImpact: ((finalPrice - currentPrice) / currentPrice) * 100, + newBalance: userBalance + totalCost, + autoPumpEffects: { + feeApplied: autoPumpEffects.pumpFee, + tokensBurned: autoPumpEffects.tokensBurned + } }); } });