feat: implement activity tracking with database models and queries

This commit is contained in:
2025-06-08 14:42:09 +08:00
commit 9e281897ba
23 changed files with 7401 additions and 0 deletions

21
README.md Normal file
View File

@@ -0,0 +1,21 @@
# dbx
## Usage
```sh
go run .
```
## Development
### Generate
```sh
task gen:sqlc
```
### Run
```sh
task
```

25
Taskfile.yml Normal file
View File

@@ -0,0 +1,25 @@
# yaml-language-server: $schema=https://taskfile.dev/schema.json
version: "3"
silent: true
vars:
GIT_ROOT:
sh: git rev-parse --show-toplevel
tasks:
default:
cmds:
- go mod tidy
- task -l --json | jq -r '.tasks[].name' | fzf --tmux | xargs task
gen:templ:
aliases: [templ]
desc: Templ Generate
cmds:
- templ generate
gen:sqlc:
aliases: [sqlc]
desc: Sqlc Generate
cmds:
- sqlc generate

31
activity/db.go Normal file
View File

@@ -0,0 +1,31 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package activity
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}

99
activity/models.go Normal file
View File

@@ -0,0 +1,99 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package activity
import (
"database/sql"
"time"
)
type Activity struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
AccountID string `json:"account_id"`
TxHash sql.NullString `json:"tx_hash"`
TxType string `json:"tx_type"`
Status string `json:"status"`
Amount sql.NullString `json:"amount"`
Fee sql.NullString `json:"fee"`
GasUsed sql.NullInt64 `json:"gas_used"`
GasWanted sql.NullInt64 `json:"gas_wanted"`
Memo sql.NullString `json:"memo"`
BlockHeight sql.NullInt64 `json:"block_height"`
Timestamp time.Time `json:"timestamp"`
RawLog sql.NullString `json:"raw_log"`
Error sql.NullString `json:"error"`
}
type CryptoListing struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
ApiID string `json:"api_id"`
Name string `json:"name"`
Symbol string `json:"symbol"`
WebsiteSlug string `json:"website_slug"`
}
type FearGreedIndex struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Value int64 `json:"value"`
ValueClassification string `json:"value_classification"`
Timestamp time.Time `json:"timestamp"`
TimeUntilUpdate sql.NullString `json:"time_until_update"`
}
type GlobalMarket struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
TotalMarketCapUsd sql.NullFloat64 `json:"total_market_cap_usd"`
Total24hVolumeUsd sql.NullFloat64 `json:"total_24h_volume_usd"`
BitcoinPercentageOfMarketCap sql.NullFloat64 `json:"bitcoin_percentage_of_market_cap"`
ActiveCurrencies sql.NullInt64 `json:"active_currencies"`
ActiveAssets sql.NullInt64 `json:"active_assets"`
ActiveMarkets sql.NullInt64 `json:"active_markets"`
LastUpdated time.Time `json:"last_updated"`
}
type Health struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
EndpointUrl string `json:"endpoint_url"`
EndpointType string `json:"endpoint_type"`
ChainID sql.NullString `json:"chain_id"`
Status string `json:"status"`
ResponseTimeMs sql.NullInt64 `json:"response_time_ms"`
LastChecked time.Time `json:"last_checked"`
NextCheck sql.NullTime `json:"next_check"`
FailureCount int64 `json:"failure_count"`
SuccessCount int64 `json:"success_count"`
ResponseData sql.NullString `json:"response_data"`
ErrorMessage sql.NullString `json:"error_message"`
}
type Service struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Name string `json:"name"`
Description sql.NullString `json:"description"`
ChainID string `json:"chain_id"`
Address string `json:"address"`
OwnerAddress string `json:"owner_address"`
Metadata sql.NullString `json:"metadata"`
Status string `json:"status"`
BlockHeight int64 `json:"block_height"`
}

63
activity/querier.go Normal file
View File

@@ -0,0 +1,63 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package activity
import (
"context"
"database/sql"
)
type Querier interface {
GetActivityByID(ctx context.Context, id string) (Activity, error)
GetActivityByTxHash(ctx context.Context, txHash sql.NullString) (Activity, error)
GetCryptoListingByApiID(ctx context.Context, apiID string) (CryptoListing, error)
GetCryptoListingByID(ctx context.Context, id string) (CryptoListing, error)
GetCryptoListingBySymbol(ctx context.Context, symbol string) (CryptoListing, error)
GetCryptoListingByWebsiteSlug(ctx context.Context, websiteSlug string) (CryptoListing, error)
GetFearGreedIndexByID(ctx context.Context, id string) (FearGreedIndex, error)
GetGlobalMarketByID(ctx context.Context, id string) (GlobalMarket, error)
GetHealthByEndpoint(ctx context.Context, endpointUrl string) (Health, error)
GetHealthByID(ctx context.Context, id string) (Health, error)
GetLatestFearGreedIndex(ctx context.Context) (FearGreedIndex, error)
GetLatestGlobalMarket(ctx context.Context) (GlobalMarket, error)
GetServiceByAddress(ctx context.Context, address string) (Service, error)
GetServiceByChainAndAddress(ctx context.Context, arg GetServiceByChainAndAddressParams) (Service, error)
GetServiceByID(ctx context.Context, id string) (Service, error)
// ACTIVITY QUERIES
InsertActivity(ctx context.Context, arg InsertActivityParams) (Activity, error)
// CRYPTO LISTINGS QUERIES (NEW)
InsertCryptoListing(ctx context.Context, arg InsertCryptoListingParams) (CryptoListing, error)
// FEAR AND GREED INDEX QUERIES (NEW)
InsertFearGreedIndex(ctx context.Context, arg InsertFearGreedIndexParams) (FearGreedIndex, error)
InsertGlobalMarket(ctx context.Context, arg InsertGlobalMarketParams) (GlobalMarket, error)
// HEALTH QUERIES
InsertHealth(ctx context.Context, arg InsertHealthParams) (Health, error)
InsertService(ctx context.Context, arg InsertServiceParams) (Service, error)
ListActivitiesByAccount(ctx context.Context, arg ListActivitiesByAccountParams) ([]Activity, error)
ListActivitiesByStatus(ctx context.Context, arg ListActivitiesByStatusParams) ([]Activity, error)
ListActivitiesByType(ctx context.Context, arg ListActivitiesByTypeParams) ([]Activity, error)
ListCryptoListings(ctx context.Context, arg ListCryptoListingsParams) ([]CryptoListing, error)
ListFearGreedIndexHistory(ctx context.Context, arg ListFearGreedIndexHistoryParams) ([]FearGreedIndex, error)
ListGlobalMarketHistory(ctx context.Context, arg ListGlobalMarketHistoryParams) ([]GlobalMarket, error)
ListHealthByChain(ctx context.Context, arg ListHealthByChainParams) ([]Health, error)
ListHealthByStatus(ctx context.Context, arg ListHealthByStatusParams) ([]Health, error)
ListHealthChecksNeedingUpdate(ctx context.Context, limit int64) ([]Health, error)
ListServicesByChain(ctx context.Context, arg ListServicesByChainParams) ([]Service, error)
ListServicesByOwner(ctx context.Context, arg ListServicesByOwnerParams) ([]Service, error)
SoftDeleteActivity(ctx context.Context, id string) error
SoftDeleteCryptoListing(ctx context.Context, id string) error
SoftDeleteFearGreedIndex(ctx context.Context, id string) error
SoftDeleteGlobalMarket(ctx context.Context, id string) error
SoftDeleteHealth(ctx context.Context, id string) error
SoftDeleteService(ctx context.Context, id string) error
UpdateActivityStatus(ctx context.Context, arg UpdateActivityStatusParams) (Activity, error)
UpdateCryptoListing(ctx context.Context, arg UpdateCryptoListingParams) (CryptoListing, error)
UpdateFearGreedIndex(ctx context.Context, arg UpdateFearGreedIndexParams) (FearGreedIndex, error)
UpdateGlobalMarket(ctx context.Context, arg UpdateGlobalMarketParams) (GlobalMarket, error)
UpdateHealthCheck(ctx context.Context, arg UpdateHealthCheckParams) (Health, error)
UpdateService(ctx context.Context, arg UpdateServiceParams) (Service, error)
}
var _ Querier = (*Queries)(nil)

1640
activity/query.sql.go Normal file

File diff suppressed because it is too large Load Diff

338
activity/sink/query.sql Normal file
View File

@@ -0,0 +1,338 @@
-- name: InsertService :one
INSERT INTO services (
name,
description,
chain_id,
address,
owner_address,
metadata,
status,
block_height
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetServiceByID :one
SELECT * FROM services
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetServiceByAddress :one
SELECT * FROM services
WHERE address = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetServiceByChainAndAddress :one
SELECT * FROM services
WHERE chain_id = ? AND address = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListServicesByChain :many
SELECT * FROM services
WHERE chain_id = ? AND deleted_at IS NULL
ORDER BY name ASC
LIMIT ? OFFSET ?;
-- name: ListServicesByOwner :many
SELECT * FROM services
WHERE owner_address = ? AND deleted_at IS NULL
ORDER BY created_at DESC
LIMIT ? OFFSET ?;
-- name: UpdateService :one
UPDATE services
SET
name = ?,
description = ?,
owner_address = ?,
metadata = ?,
status = ?,
block_height = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteService :exec
UPDATE services
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- ACTIVITY QUERIES
-- name: InsertActivity :one
INSERT INTO activities (
account_id,
tx_hash,
tx_type,
status,
amount,
fee,
gas_used,
gas_wanted,
memo,
block_height,
timestamp,
raw_log,
error
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetActivityByID :one
SELECT * FROM activities
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetActivityByTxHash :one
SELECT * FROM activities
WHERE tx_hash = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListActivitiesByAccount :many
SELECT * FROM activities
WHERE account_id = ? AND deleted_at IS NULL
ORDER BY timestamp DESC
LIMIT ? OFFSET ?;
-- name: ListActivitiesByType :many
SELECT * FROM activities
WHERE tx_type = ? AND deleted_at IS NULL
ORDER BY timestamp DESC
LIMIT ? OFFSET ?;
-- name: ListActivitiesByStatus :many
SELECT * FROM activities
WHERE status = ? AND deleted_at IS NULL
ORDER BY timestamp DESC
LIMIT ? OFFSET ?;
-- name: UpdateActivityStatus :one
UPDATE activities
SET
status = ?,
tx_hash = ?,
block_height = ?,
gas_used = ?,
raw_log = ?,
error = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteActivity :exec
UPDATE activities
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- HEALTH QUERIES
-- name: InsertHealth :one
INSERT INTO health (
endpoint_url,
endpoint_type,
chain_id,
status,
response_time_ms,
last_checked,
next_check,
failure_count,
success_count,
response_data,
error_message
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetHealthByID :one
SELECT * FROM health
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetHealthByEndpoint :one
SELECT * FROM health
WHERE endpoint_url = ? AND deleted_at IS NULL
ORDER BY last_checked DESC
LIMIT 1;
-- name: ListHealthByChain :many
SELECT * FROM health
WHERE chain_id = ? AND deleted_at IS NULL
ORDER BY last_checked DESC
LIMIT ? OFFSET ?;
-- name: ListHealthByStatus :many
SELECT * FROM health
WHERE status = ? AND deleted_at IS NULL
ORDER BY last_checked DESC
LIMIT ? OFFSET ?;
-- name: ListHealthChecksNeedingUpdate :many
SELECT * FROM health
WHERE next_check <= CURRENT_TIMESTAMP AND deleted_at IS NULL
ORDER BY next_check ASC
LIMIT ?;
-- name: UpdateHealthCheck :one
UPDATE health
SET
status = ?,
response_time_ms = ?,
last_checked = CURRENT_TIMESTAMP,
next_check = ?,
failure_count = CASE WHEN status = 'failed' THEN failure_count + 1 ELSE failure_count END,
success_count = CASE WHEN status = 'success' THEN success_count + 1 ELSE success_count END,
response_data = ?,
error_message = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteHealth :exec
UPDATE health
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- name: GetGlobalMarketByID :one
SELECT * FROM global_market
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetLatestGlobalMarket :one
SELECT * FROM global_market
WHERE deleted_at IS NULL
ORDER BY last_updated DESC
LIMIT 1;
-- name: ListGlobalMarketHistory :many
SELECT * FROM global_market
WHERE deleted_at IS NULL
ORDER BY last_updated DESC
LIMIT ? OFFSET ?;
-- name: UpdateGlobalMarket :one
UPDATE global_market
SET
total_market_cap_usd = ?,
total_24h_volume_usd = ?,
bitcoin_percentage_of_market_cap = ?,
active_currencies = ?,
active_assets = ?,
active_markets = ?,
last_updated = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteGlobalMarket :exec
UPDATE global_market
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- FEAR AND GREED INDEX QUERIES (NEW)
-- name: InsertFearGreedIndex :one
INSERT INTO fear_greed_index (
value,
value_classification,
timestamp,
time_until_update
) VALUES (?, ?, ?, ?)
RETURNING *;
-- name: GetFearGreedIndexByID :one
SELECT * FROM fear_greed_index
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetLatestFearGreedIndex :one
SELECT * FROM fear_greed_index
WHERE deleted_at IS NULL
ORDER BY timestamp DESC
LIMIT 1;
-- name: ListFearGreedIndexHistory :many
SELECT * FROM fear_greed_index
WHERE deleted_at IS NULL
ORDER BY timestamp DESC
LIMIT ? OFFSET ?;
-- name: UpdateFearGreedIndex :one
UPDATE fear_greed_index
SET
value = ?,
value_classification = ?,
timestamp = ?,
time_until_update = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: InsertGlobalMarket :one
INSERT INTO global_market (
total_market_cap_usd,
total_24h_volume_usd,
bitcoin_percentage_of_market_cap,
active_currencies,
active_assets,
active_markets,
last_updated
) VALUES (?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: SoftDeleteFearGreedIndex :exec
UPDATE fear_greed_index
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- CRYPTO LISTINGS QUERIES (NEW)
-- name: InsertCryptoListing :one
INSERT INTO crypto_listings (
api_id,
name,
symbol,
website_slug
) VALUES (?, ?, ?, ?)
RETURNING *;
-- name: GetCryptoListingByID :one
SELECT * FROM crypto_listings
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetCryptoListingByApiID :one
SELECT * FROM crypto_listings
WHERE api_id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetCryptoListingBySymbol :one
SELECT * FROM crypto_listings
WHERE symbol = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetCryptoListingByWebsiteSlug :one
SELECT * FROM crypto_listings
WHERE website_slug = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListCryptoListings :many
SELECT * FROM crypto_listings
WHERE deleted_at IS NULL
ORDER BY name ASC
LIMIT ? OFFSET ?;
-- name: UpdateCryptoListing :one
UPDATE crypto_listings
SET
name = ?,
symbol = ?,
website_slug = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteCryptoListing :exec
UPDATE crypto_listings
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;

136
activity/sink/schema.sql Normal file
View File

@@ -0,0 +1,136 @@
-- Service for Service Records sourced on chain
CREATE TABLE services (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
name TEXT NOT NULL,
description TEXT,
chain_id TEXT NOT NULL,
address TEXT NOT NULL,
owner_address TEXT NOT NULL,
metadata TEXT CHECK(json_valid(metadata)),
status TEXT NOT NULL,
block_height INTEGER NOT NULL,
FOREIGN KEY (chain_id) REFERENCES assets(chain_id),
UNIQUE(chain_id, address)
);
-- Activity table for basic transaction broadcast activity
CREATE TABLE activities (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
account_id TEXT NOT NULL,
tx_hash TEXT,
tx_type TEXT NOT NULL,
status TEXT NOT NULL,
amount TEXT,
fee TEXT,
gas_used INTEGER,
gas_wanted INTEGER,
memo TEXT,
block_height INTEGER,
timestamp TIMESTAMP NOT NULL,
raw_log TEXT,
error TEXT,
FOREIGN KEY (account_id) REFERENCES accounts(id)
);
-- Health table for scheduled checks for API endpoints
CREATE TABLE health (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
endpoint_url TEXT NOT NULL,
endpoint_type TEXT NOT NULL,
chain_id TEXT,
status TEXT NOT NULL,
response_time_ms INTEGER,
last_checked TIMESTAMP NOT NULL,
next_check TIMESTAMP,
failure_count INTEGER NOT NULL DEFAULT 0,
success_count INTEGER NOT NULL DEFAULT 0,
response_data TEXT,
error_message TEXT,
FOREIGN KEY (chain_id) REFERENCES assets(chain_id)
);
-- Global market data from Alternative.me API
CREATE TABLE global_market (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
total_market_cap_usd REAL,
total_24h_volume_usd REAL,
bitcoin_percentage_of_market_cap REAL,
active_currencies INTEGER,
active_assets INTEGER,
active_markets INTEGER,
last_updated TIMESTAMP NOT NULL
);
-- Fear and Greed Index data from Alternative.me
CREATE TABLE fear_greed_index (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
value INTEGER NOT NULL,
value_classification TEXT NOT NULL,
timestamp TIMESTAMP NOT NULL,
time_until_update TEXT
);
-- Listings data from Alternative.me API
CREATE TABLE crypto_listings (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
api_id TEXT NOT NULL,
name TEXT NOT NULL,
symbol TEXT NOT NULL,
website_slug TEXT NOT NULL,
UNIQUE(api_id)
);
CREATE INDEX idx_services_name ON services(name);
CREATE INDEX idx_services_chain_id ON services(chain_id);
CREATE INDEX idx_services_address ON services(address);
CREATE INDEX idx_services_owner_address ON services(owner_address);
CREATE INDEX idx_services_status ON services(status);
CREATE INDEX idx_services_deleted_at ON services(deleted_at);
CREATE INDEX idx_activities_account_id ON activities(account_id);
CREATE INDEX idx_activities_tx_hash ON activities(tx_hash);
CREATE INDEX idx_activities_tx_type ON activities(tx_type);
CREATE INDEX idx_activities_status ON activities(status);
CREATE INDEX idx_activities_timestamp ON activities(timestamp);
CREATE INDEX idx_activities_block_height ON activities(block_height);
CREATE INDEX idx_activities_deleted_at ON activities(deleted_at);
CREATE INDEX idx_health_endpoint_url ON health(endpoint_url);
CREATE INDEX idx_health_endpoint_type ON health(endpoint_type);
CREATE INDEX idx_health_chain_id ON health(chain_id);
CREATE INDEX idx_health_status ON health(status);
CREATE INDEX idx_health_last_checked ON health(last_checked);
CREATE INDEX idx_health_next_check ON health(next_check);
CREATE INDEX idx_health_deleted_at ON health(deleted_at);
CREATE INDEX idx_global_market_last_updated ON global_market(last_updated);
CREATE INDEX idx_global_market_deleted_at ON global_market(deleted_at);
CREATE INDEX idx_fear_greed_index_timestamp ON fear_greed_index(timestamp);
CREATE INDEX idx_fear_greed_index_value ON fear_greed_index(value);
CREATE INDEX idx_fear_greed_index_deleted_at ON fear_greed_index(deleted_at);
CREATE INDEX idx_crypto_listings_api_id ON crypto_listings(api_id);
CREATE INDEX idx_crypto_listings_symbol ON crypto_listings(symbol);
CREATE INDEX idx_crypto_listings_website_slug ON crypto_listings(website_slug);
CREATE INDEX idx_crypto_listings_deleted_at ON crypto_listings(deleted_at);

1
connection.go Normal file
View File

@@ -0,0 +1 @@
package dbx

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module github.com/cf-sonr/dbx
go 1.24.2

31
network/db.go Normal file
View File

@@ -0,0 +1,31 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package network
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}

96
network/models.go Normal file
View File

@@ -0,0 +1,96 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package network
import (
"database/sql"
"time"
)
type Asset struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Decimals int64 `json:"decimals"`
ChainID string `json:"chain_id"`
Channel string `json:"channel"`
AssetType string `json:"asset_type"`
CoingeckoID sql.NullString `json:"coingecko_id"`
}
type Blockchain struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
ChainName string `json:"chain_name"`
ChainIDCosmos sql.NullString `json:"chain_id_cosmos"`
ChainIDEvm sql.NullString `json:"chain_id_evm"`
ApiName sql.NullString `json:"api_name"`
BechAccountPrefix sql.NullString `json:"bech_account_prefix"`
BechValidatorPrefix sql.NullString `json:"bech_validator_prefix"`
MainAssetSymbol sql.NullString `json:"main_asset_symbol"`
MainAssetDenom sql.NullString `json:"main_asset_denom"`
StakingAssetSymbol sql.NullString `json:"staking_asset_symbol"`
StakingAssetDenom sql.NullString `json:"staking_asset_denom"`
IsStakeEnabled bool `json:"is_stake_enabled"`
ChainImage sql.NullString `json:"chain_image"`
MainAssetImage sql.NullString `json:"main_asset_image"`
StakingAssetImage sql.NullString `json:"staking_asset_image"`
ChainType string `json:"chain_type"`
IsSupportMobileWallet bool `json:"is_support_mobile_wallet"`
IsSupportExtensionWallet bool `json:"is_support_extension_wallet"`
IsSupportErc20 bool `json:"is_support_erc20"`
DescriptionEn sql.NullString `json:"description_en"`
DescriptionKo sql.NullString `json:"description_ko"`
DescriptionJa sql.NullString `json:"description_ja"`
OriginGenesisTime sql.NullTime `json:"origin_genesis_time"`
AccountType string `json:"account_type"`
BtcStaking sql.NullString `json:"btc_staking"`
CosmosFeeInfo sql.NullString `json:"cosmos_fee_info"`
EvmFeeInfo sql.NullString `json:"evm_fee_info"`
LcdEndpoint sql.NullString `json:"lcd_endpoint"`
GrpcEndpoint sql.NullString `json:"grpc_endpoint"`
EvmRpcEndpoint sql.NullString `json:"evm_rpc_endpoint"`
Explorer sql.NullString `json:"explorer"`
About sql.NullString `json:"about"`
Forum sql.NullString `json:"forum"`
}
type Price struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
AssetID string `json:"asset_id"`
PriceUsd sql.NullFloat64 `json:"price_usd"`
PriceBtc sql.NullFloat64 `json:"price_btc"`
Volume24hUsd sql.NullFloat64 `json:"volume_24h_usd"`
MarketCapUsd sql.NullFloat64 `json:"market_cap_usd"`
AvailableSupply sql.NullFloat64 `json:"available_supply"`
TotalSupply sql.NullFloat64 `json:"total_supply"`
MaxSupply sql.NullFloat64 `json:"max_supply"`
PercentChange1h sql.NullFloat64 `json:"percent_change_1h"`
PercentChange24h sql.NullFloat64 `json:"percent_change_24h"`
PercentChange7d sql.NullFloat64 `json:"percent_change_7d"`
Rank sql.NullInt64 `json:"rank"`
LastUpdated time.Time `json:"last_updated"`
}
type PriceConversion struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
PriceID string `json:"price_id"`
CurrencyCode string `json:"currency_code"`
Price sql.NullFloat64 `json:"price"`
Volume24h sql.NullFloat64 `json:"volume_24h"`
MarketCap sql.NullFloat64 `json:"market_cap"`
LastUpdated time.Time `json:"last_updated"`
}

64
network/querier.go Normal file
View File

@@ -0,0 +1,64 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package network
import (
"context"
"database/sql"
)
type Querier interface {
CountBlockchainsByChainType(ctx context.Context, dollar_1 sql.NullString) (int64, error)
GetAssetByChainAndSymbol(ctx context.Context, arg GetAssetByChainAndSymbolParams) (Asset, error)
GetAssetByID(ctx context.Context, id string) (Asset, error)
GetAssetBySymbol(ctx context.Context, symbol string) (Asset, error)
GetAssetWithLatestPrice(ctx context.Context, id string) (GetAssetWithLatestPriceRow, error)
GetBlockchainByChainName(ctx context.Context, chainName string) (Blockchain, error)
GetBlockchainByCosmosChainID(ctx context.Context, chainIDCosmos sql.NullString) (Blockchain, error)
GetBlockchainByEvmChainID(ctx context.Context, chainIDEvm sql.NullString) (Blockchain, error)
GetBlockchainByID(ctx context.Context, id string) (Blockchain, error)
GetBlockchainEndpoints(ctx context.Context, id string) (GetBlockchainEndpointsRow, error)
GetBlockchainExplorer(ctx context.Context, id string) (GetBlockchainExplorerRow, error)
GetBlockchainWithAssetInfo(ctx context.Context, id string) (GetBlockchainWithAssetInfoRow, error)
GetPriceByAssetID(ctx context.Context, assetID string) (Price, error)
GetPriceByID(ctx context.Context, id string) (Price, error)
GetPriceConversionByCurrency(ctx context.Context, arg GetPriceConversionByCurrencyParams) (PriceConversion, error)
GetPriceConversionByID(ctx context.Context, id string) (PriceConversion, error)
GetPriceConversionsByPriceID(ctx context.Context, priceID string) ([]PriceConversion, error)
// ASSET QUERIES
InsertAsset(ctx context.Context, arg InsertAssetParams) (Asset, error)
// BLOCKCHAIN QUERIES
InsertBlockchain(ctx context.Context, arg InsertBlockchainParams) (Blockchain, error)
// PRICE QUERIES (UPDATED)
InsertPrice(ctx context.Context, arg InsertPriceParams) (Price, error)
// PRICE CONVERSION QUERIES (NEW)
InsertPriceConversion(ctx context.Context, arg InsertPriceConversionParams) (PriceConversion, error)
ListAllBlockchains(ctx context.Context) ([]Blockchain, error)
ListAssetsByChain(ctx context.Context, chainID string) ([]Asset, error)
ListAssetsWithLatestPrices(ctx context.Context, arg ListAssetsWithLatestPricesParams) ([]ListAssetsWithLatestPricesRow, error)
ListBlockchainsByChainType(ctx context.Context, dollar_1 sql.NullString) ([]Blockchain, error)
ListBlockchainsWithAssetInfo(ctx context.Context, arg ListBlockchainsWithAssetInfoParams) ([]ListBlockchainsWithAssetInfoRow, error)
ListBlockchainsWithERC20Support(ctx context.Context) ([]Blockchain, error)
ListBlockchainsWithExtensionSupport(ctx context.Context) ([]Blockchain, error)
ListBlockchainsWithMobileSupport(ctx context.Context) ([]Blockchain, error)
ListBlockchainsWithStaking(ctx context.Context) ([]Blockchain, error)
ListPriceHistoryByAssetID(ctx context.Context, arg ListPriceHistoryByAssetIDParams) ([]Price, error)
SearchBlockchains(ctx context.Context, arg SearchBlockchainsParams) ([]Blockchain, error)
SoftDeleteAsset(ctx context.Context, id string) error
SoftDeleteBlockchain(ctx context.Context, id string) error
SoftDeletePriceConversion(ctx context.Context, id string) error
UpdateAsset(ctx context.Context, arg UpdateAssetParams) (Asset, error)
UpdateBlockchain(ctx context.Context, arg UpdateBlockchainParams) (Blockchain, error)
UpdateBlockchainDescriptions(ctx context.Context, arg UpdateBlockchainDescriptionsParams) (Blockchain, error)
UpdateBlockchainEndpoints(ctx context.Context, arg UpdateBlockchainEndpointsParams) (Blockchain, error)
UpdateBlockchainExplorer(ctx context.Context, arg UpdateBlockchainExplorerParams) (Blockchain, error)
UpdateBlockchainFeeInfo(ctx context.Context, arg UpdateBlockchainFeeInfoParams) (Blockchain, error)
UpdateBlockchainImages(ctx context.Context, arg UpdateBlockchainImagesParams) (Blockchain, error)
UpdateBlockchainSocialLinks(ctx context.Context, arg UpdateBlockchainSocialLinksParams) (Blockchain, error)
UpdatePrice(ctx context.Context, arg UpdatePriceParams) (Price, error)
UpdatePriceConversion(ctx context.Context, arg UpdatePriceConversionParams) (PriceConversion, error)
}
var _ Querier = (*Queries)(nil)

2583
network/query.sql.go Normal file

File diff suppressed because it is too large Load Diff

453
network/sink/query.sql Normal file
View File

@@ -0,0 +1,453 @@
-- ASSET QUERIES
-- name: InsertAsset :one
INSERT INTO assets (
name,
symbol,
decimals,
chain_id,
channel,
asset_type,
coingecko_id
) VALUES (?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetAssetByID :one
SELECT * FROM assets
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAssetBySymbol :one
SELECT * FROM assets
WHERE symbol = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAssetByChainAndSymbol :one
SELECT * FROM assets
WHERE chain_id = ? AND symbol = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListAssetsByChain :many
SELECT * FROM assets
WHERE chain_id = ? AND deleted_at IS NULL
ORDER BY symbol ASC;
-- name: UpdateAsset :one
UPDATE assets
SET
name = ?,
decimals = ?,
channel = ?,
asset_type = ?,
coingecko_id = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteAsset :exec
UPDATE assets
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- PRICE QUERIES (UPDATED)
-- name: InsertPrice :one
INSERT INTO prices (
asset_id,
price_usd,
price_btc,
volume_24h_usd,
market_cap_usd,
available_supply,
total_supply,
max_supply,
percent_change_1h,
percent_change_24h,
percent_change_7d,
rank,
last_updated
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetPriceByAssetID :one
SELECT * FROM prices
WHERE asset_id = ? AND deleted_at IS NULL
ORDER BY last_updated DESC
LIMIT 1;
-- name: GetPriceByID :one
SELECT * FROM prices
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListPriceHistoryByAssetID :many
SELECT * FROM prices
WHERE asset_id = ? AND deleted_at IS NULL
ORDER BY last_updated DESC
LIMIT ? OFFSET ?;
-- name: GetAssetWithLatestPrice :one
SELECT a.*, p.price_usd, p.price_btc, p.volume_24h_usd, p.market_cap_usd,
p.available_supply, p.total_supply, p.max_supply,
p.percent_change_1h, p.percent_change_24h, p.percent_change_7d,
p.rank, p.last_updated
FROM assets a
LEFT JOIN (
SELECT p1.*
FROM prices p1
INNER JOIN (
SELECT asset_id, MAX(last_updated) as max_date
FROM prices
WHERE deleted_at IS NULL
GROUP BY asset_id
) p2 ON p1.asset_id = p2.asset_id AND p1.last_updated = p2.max_date
WHERE p1.deleted_at IS NULL
) p ON a.id = p.asset_id
WHERE a.id = ? AND a.deleted_at IS NULL
LIMIT 1;
-- name: ListAssetsWithLatestPrices :many
SELECT a.*, p.price_usd, p.price_btc, p.volume_24h_usd, p.market_cap_usd,
p.available_supply, p.total_supply, p.max_supply,
p.percent_change_1h, p.percent_change_24h, p.percent_change_7d,
p.rank, p.last_updated
FROM assets a
LEFT JOIN (
SELECT p1.*
FROM prices p1
INNER JOIN (
SELECT asset_id, MAX(last_updated) as max_date
FROM prices
WHERE deleted_at IS NULL
GROUP BY asset_id
) p2 ON p1.asset_id = p2.asset_id AND p1.last_updated = p2.max_date
WHERE p1.deleted_at IS NULL
) p ON a.id = p.asset_id
WHERE a.deleted_at IS NULL
ORDER BY p.rank ASC, a.symbol ASC
LIMIT ? OFFSET ?;
-- name: UpdatePrice :one
UPDATE prices
SET
price_usd = ?,
price_btc = ?,
volume_24h_usd = ?,
market_cap_usd = ?,
available_supply = ?,
total_supply = ?,
max_supply = ?,
percent_change_1h = ?,
percent_change_24h = ?,
percent_change_7d = ?,
rank = ?,
last_updated = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- PRICE CONVERSION QUERIES (NEW)
-- name: InsertPriceConversion :one
INSERT INTO price_conversions (
price_id,
currency_code,
price,
volume_24h,
market_cap,
last_updated
) VALUES (?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetPriceConversionByID :one
SELECT * FROM price_conversions
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetPriceConversionsByPriceID :many
SELECT * FROM price_conversions
WHERE price_id = ? AND deleted_at IS NULL
ORDER BY currency_code ASC;
-- name: GetPriceConversionByCurrency :one
SELECT * FROM price_conversions
WHERE price_id = ? AND currency_code = ? AND deleted_at IS NULL
LIMIT 1;
-- name: UpdatePriceConversion :one
UPDATE price_conversions
SET
price = ?,
volume_24h = ?,
market_cap = ?,
last_updated = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeletePriceConversion :exec
UPDATE price_conversions
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- BLOCKCHAIN QUERIES
-- name: InsertBlockchain :one
INSERT INTO blockchains (
id,
chain_name,
chain_id_cosmos,
chain_id_evm,
api_name,
bech_account_prefix,
bech_validator_prefix,
main_asset_symbol,
main_asset_denom,
staking_asset_symbol,
staking_asset_denom,
is_stake_enabled,
chain_image,
main_asset_image,
staking_asset_image,
chain_type,
is_support_mobile_wallet,
is_support_extension_wallet,
is_support_erc20,
description_en,
description_ko,
description_ja,
origin_genesis_time,
account_type,
btc_staking,
cosmos_fee_info,
evm_fee_info,
lcd_endpoint,
grpc_endpoint,
evm_rpc_endpoint,
explorer,
about,
forum
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING *;
-- name: GetBlockchainByID :one
SELECT * FROM blockchains
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBlockchainByChainName :one
SELECT * FROM blockchains
WHERE chain_name = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBlockchainByCosmosChainID :one
SELECT * FROM blockchains
WHERE chain_id_cosmos = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBlockchainByEvmChainID :one
SELECT * FROM blockchains
WHERE chain_id_evm = ? AND deleted_at IS NULL
LIMIT 1;
-- name: ListAllBlockchains :many
SELECT * FROM blockchains
WHERE deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: ListBlockchainsByChainType :many
SELECT * FROM blockchains
WHERE chain_type LIKE '%' || ? || '%' AND deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: ListBlockchainsWithStaking :many
SELECT * FROM blockchains
WHERE is_stake_enabled = 1 AND deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: ListBlockchainsWithMobileSupport :many
SELECT * FROM blockchains
WHERE is_support_mobile_wallet = 1 AND deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: ListBlockchainsWithExtensionSupport :many
SELECT * FROM blockchains
WHERE is_support_extension_wallet = 1 AND deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: ListBlockchainsWithERC20Support :many
SELECT * FROM blockchains
WHERE is_support_erc20 = 1 AND deleted_at IS NULL
ORDER BY chain_name ASC;
-- name: UpdateBlockchain :one
UPDATE blockchains
SET
chain_name = ?,
chain_id_cosmos = ?,
chain_id_evm = ?,
api_name = ?,
bech_account_prefix = ?,
bech_validator_prefix = ?,
main_asset_symbol = ?,
main_asset_denom = ?,
staking_asset_symbol = ?,
staking_asset_denom = ?,
is_stake_enabled = ?,
chain_image = ?,
main_asset_image = ?,
staking_asset_image = ?,
chain_type = ?,
is_support_mobile_wallet = ?,
is_support_extension_wallet = ?,
is_support_erc20 = ?,
description_en = ?,
description_ko = ?,
description_ja = ?,
origin_genesis_time = ?,
account_type = ?,
btc_staking = ?,
cosmos_fee_info = ?,
evm_fee_info = ?,
lcd_endpoint = ?,
grpc_endpoint = ?,
evm_rpc_endpoint = ?,
explorer = ?,
about = ?,
forum = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainEndpoints :one
UPDATE blockchains
SET
lcd_endpoint = ?,
grpc_endpoint = ?,
evm_rpc_endpoint = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainExplorer :one
UPDATE blockchains
SET
explorer = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainFeeInfo :one
UPDATE blockchains
SET
cosmos_fee_info = ?,
evm_fee_info = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainImages :one
UPDATE blockchains
SET
chain_image = ?,
main_asset_image = ?,
staking_asset_image = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainDescriptions :one
UPDATE blockchains
SET
description_en = ?,
description_ko = ?,
description_ja = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateBlockchainSocialLinks :one
UPDATE blockchains
SET
about = ?,
forum = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteBlockchain :exec
UPDATE blockchains
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- name: GetBlockchainWithAssetInfo :one
SELECT b.*, a.id as asset_id, a.symbol, a.decimals, p.price_usd, p.price_btc
FROM blockchains b
LEFT JOIN assets a ON b.main_asset_symbol = a.symbol
LEFT JOIN (
SELECT p1.*
FROM prices p1
INNER JOIN (
SELECT asset_id, MAX(last_updated) as max_date
FROM prices
WHERE deleted_at IS NULL
GROUP BY asset_id
) p2 ON p1.asset_id = p2.asset_id AND p1.last_updated = p2.max_date
WHERE p1.deleted_at IS NULL
) p ON a.id = p.asset_id
WHERE b.id = ? AND b.deleted_at IS NULL
LIMIT 1;
-- name: ListBlockchainsWithAssetInfo :many
SELECT b.*, a.id as asset_id, a.symbol, a.decimals, p.price_usd, p.price_btc
FROM blockchains b
LEFT JOIN assets a ON b.main_asset_symbol = a.symbol
LEFT JOIN (
SELECT p1.*
FROM prices p1
INNER JOIN (
SELECT asset_id, MAX(last_updated) as max_date
FROM prices
WHERE deleted_at IS NULL
GROUP BY asset_id
) p2 ON p1.asset_id = p2.asset_id AND p1.last_updated = p2.max_date
WHERE p1.deleted_at IS NULL
) p ON a.id = p.asset_id
WHERE b.deleted_at IS NULL
ORDER BY b.chain_name ASC
LIMIT ? OFFSET ?;
-- name: SearchBlockchains :many
SELECT * FROM blockchains
WHERE (
chain_name LIKE '%' || ? || '%' OR
main_asset_symbol LIKE '%' || ? || '%' OR
staking_asset_symbol LIKE '%' || ? || '%' OR
description_en LIKE '%' || ? || '%'
) AND deleted_at IS NULL
ORDER BY chain_name ASC
LIMIT ? OFFSET ?;
-- name: CountBlockchainsByChainType :one
SELECT COUNT(*) as count FROM blockchains
WHERE chain_type LIKE '%' || ? || '%' AND deleted_at IS NULL;
-- name: GetBlockchainEndpoints :one
SELECT id, chain_name, lcd_endpoint, grpc_endpoint, evm_rpc_endpoint
FROM blockchains
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetBlockchainExplorer :one
SELECT id, chain_name, explorer
FROM blockchains
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;

139
network/sink/schema.sql Normal file
View File

@@ -0,0 +1,139 @@
-- Assets represent tokens and coins
CREATE TABLE assets (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
name TEXT NOT NULL,
symbol TEXT NOT NULL,
decimals INTEGER NOT NULL CHECK(decimals >= 0),
chain_id TEXT NOT NULL,
channel TEXT NOT NULL,
asset_type TEXT NOT NULL,
coingecko_id TEXT,
UNIQUE(chain_id, symbol)
);
-- Prices entity based on the Alternative.me API for crypto prices
CREATE TABLE prices (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
asset_id TEXT NOT NULL,
price_usd REAL,
price_btc REAL,
volume_24h_usd REAL,
market_cap_usd REAL,
available_supply REAL,
total_supply REAL,
max_supply REAL,
percent_change_1h REAL,
percent_change_24h REAL,
percent_change_7d REAL,
rank INTEGER,
last_updated TIMESTAMP NOT NULL,
FOREIGN KEY (asset_id) REFERENCES assets(id)
);
-- Currency conversion rates for crypto prices
CREATE TABLE price_conversions (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
price_id TEXT NOT NULL,
currency_code TEXT NOT NULL,
price REAL,
volume_24h REAL,
market_cap REAL,
last_updated TIMESTAMP NOT NULL,
FOREIGN KEY (price_id) REFERENCES prices(id),
UNIQUE(price_id, currency_code)
);
-- Blockchains table to store chain configuration parameters
CREATE TABLE blockchains (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
-- Basic chain information
chain_name TEXT NOT NULL,
chain_id_cosmos TEXT,
chain_id_evm TEXT,
api_name TEXT,
bech_account_prefix TEXT,
bech_validator_prefix TEXT,
-- Chain assets
main_asset_symbol TEXT,
main_asset_denom TEXT,
staking_asset_symbol TEXT,
staking_asset_denom TEXT,
is_stake_enabled BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_stake_enabled IN (0,1)),
-- Chain images
chain_image TEXT,
main_asset_image TEXT,
staking_asset_image TEXT,
-- Chain types and features
chain_type TEXT NOT NULL CHECK(json_valid(chain_type)),
is_support_mobile_wallet BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_support_mobile_wallet IN (0,1)),
is_support_extension_wallet BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_support_extension_wallet IN (0,1)),
is_support_erc20 BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_support_erc20 IN (0,1)),
-- Descriptions in multiple languages
description_en TEXT,
description_ko TEXT,
description_ja TEXT,
-- Genesis information
origin_genesis_time TIMESTAMP,
-- Account types configuration
account_type TEXT NOT NULL CHECK(json_valid(account_type)),
-- BTC staking specific
btc_staking TEXT CHECK(json_valid(btc_staking)),
-- Cosmos fee information
cosmos_fee_info TEXT CHECK(json_valid(cosmos_fee_info)),
-- EVM fee information
evm_fee_info TEXT CHECK(json_valid(evm_fee_info)),
-- Endpoints
lcd_endpoint TEXT CHECK(json_valid(lcd_endpoint)),
grpc_endpoint TEXT CHECK(json_valid(grpc_endpoint)),
evm_rpc_endpoint TEXT CHECK(json_valid(evm_rpc_endpoint)),
-- Explorer information
explorer TEXT CHECK(json_valid(explorer)),
-- Social and documentation links
about TEXT CHECK(json_valid(about)),
forum TEXT CHECK(json_valid(forum))
);
-- Add all necessary indexes
CREATE INDEX idx_assets_symbol ON assets(symbol);
CREATE INDEX idx_assets_chain_id ON assets(chain_id);
CREATE INDEX idx_assets_deleted_at ON assets(deleted_at);
CREATE INDEX idx_prices_asset_id ON prices(asset_id);
CREATE INDEX idx_prices_rank ON prices(rank);
CREATE INDEX idx_prices_last_updated ON prices(last_updated);
CREATE INDEX idx_prices_deleted_at ON prices(deleted_at);
CREATE INDEX idx_price_conversions_price_id ON price_conversions(price_id);
CREATE INDEX idx_price_conversions_currency_code ON price_conversions(currency_code);
CREATE INDEX idx_price_conversions_deleted_at ON price_conversions(deleted_at);
CREATE INDEX idx_blockchains_chain_name ON blockchains(chain_name);
CREATE INDEX idx_blockchains_chain_id_cosmos ON blockchains(chain_id_cosmos);
CREATE INDEX idx_blockchains_chain_id_evm ON blockchains(chain_id_evm);
CREATE INDEX idx_blockchains_main_asset_symbol ON blockchains(main_asset_symbol);
CREATE INDEX idx_blockchains_deleted_at ON blockchains(deleted_at);

34
sqlc.yaml Normal file
View File

@@ -0,0 +1,34 @@
version: "2"
sql:
# Activity DB - User to User Interactions
- engine: "sqlite"
queries: "./activity/sink/query.sql"
schema: "./activity/sink/schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "activity"
out: "./activity"
# Network DB - Blockchain Parameters and Asset Metadata
- engine: "sqlite"
queries: "./network/sink/query.sql"
schema: "./network/sink/schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "network"
out: "./network"
# Users DB - Accounts, Profiles, and Vault Metadata
- engine: "sqlite"
queries: "./users/sink/query.sql"
schema: "./users/sink/schema.sql"
gen:
go:
emit_interface: true
emit_json_tags: true
package: "users"
out: "./users"

31
users/db.go Normal file
View File

@@ -0,0 +1,31 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package users
import (
"context"
"database/sql"
)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
return &Queries{
db: tx,
}
}

68
users/models.go Normal file
View File

@@ -0,0 +1,68 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package users
import (
"database/sql"
"time"
)
type Account struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Number int64 `json:"number"`
Sequence int64 `json:"sequence"`
Address string `json:"address"`
PublicKey string `json:"public_key"`
ChainID string `json:"chain_id"`
BlockCreated int64 `json:"block_created"`
Controller string `json:"controller"`
Label string `json:"label"`
Handle string `json:"handle"`
IsSubsidiary bool `json:"is_subsidiary"`
IsValidator bool `json:"is_validator"`
IsDelegator bool `json:"is_delegator"`
IsAccountable bool `json:"is_accountable"`
}
type Credential struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Handle string `json:"handle"`
CredentialID string `json:"credential_id"`
AuthenticatorAttachment string `json:"authenticator_attachment"`
Origin string `json:"origin"`
Type string `json:"type"`
Transports string `json:"transports"`
}
type Profile struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Address string `json:"address"`
Handle string `json:"handle"`
Origin string `json:"origin"`
Name string `json:"name"`
}
type Vault struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt sql.NullTime `json:"deleted_at"`
Handle string `json:"handle"`
Origin string `json:"origin"`
Address string `json:"address"`
Cid string `json:"cid"`
Config string `json:"config"`
SessionID string `json:"session_id"`
RedirectUri string `json:"redirect_uri"`
}

53
users/querier.go Normal file
View File

@@ -0,0 +1,53 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.28.0
package users
import (
"context"
)
type Querier interface {
CheckHandleExists(ctx context.Context, handle string) (bool, error)
GetAccountByAddress(ctx context.Context, address string) (Account, error)
GetAccountByController(ctx context.Context, controller string) (Account, error)
GetAccountByID(ctx context.Context, id string) (Account, error)
GetAccountByNumber(ctx context.Context, number int64) (Account, error)
GetAccountByPublicKey(ctx context.Context, publicKey string) (Account, error)
GetAccountBySequence(ctx context.Context, sequence int64) (Account, error)
GetAccountsByChainID(ctx context.Context, chainID string) ([]Account, error)
GetAccountsByController(ctx context.Context, controller string) ([]Account, error)
GetAccountsByHandle(ctx context.Context, handle string) ([]Account, error)
GetAccountsByLabel(ctx context.Context, label string) ([]Account, error)
GetCredentialByID(ctx context.Context, credentialID string) (Credential, error)
GetCredentialsByHandle(ctx context.Context, handle string) ([]Credential, error)
GetProfileByAddress(ctx context.Context, address string) (Profile, error)
GetProfileByHandle(ctx context.Context, handle string) (Profile, error)
GetProfileByID(ctx context.Context, id string) (Profile, error)
GetVaultByID(ctx context.Context, id string) (Vault, error)
GetVaultConfigByCID(ctx context.Context, cid string) (Vault, error)
GetVaultRedirectURIBySessionID(ctx context.Context, sessionID string) (string, error)
GetVaultsByHandle(ctx context.Context, handle string) ([]Vault, error)
// ACCOUNT QUERIES
InsertAccount(ctx context.Context, arg InsertAccountParams) (Account, error)
// CREDENTIAL QUERIES
InsertCredential(ctx context.Context, arg InsertCredentialParams) (Credential, error)
// PROFILE QUERIES
InsertProfile(ctx context.Context, arg InsertProfileParams) (Profile, error)
// VAULT QUERIES
InsertVault(ctx context.Context, arg InsertVaultParams) (Vault, error)
ListDelegatorAccounts(ctx context.Context) ([]Account, error)
ListProfiles(ctx context.Context, arg ListProfilesParams) ([]Profile, error)
ListValidatorAccounts(ctx context.Context) ([]Account, error)
SoftDeleteAccount(ctx context.Context, id string) error
SoftDeleteCredential(ctx context.Context, credentialID string) error
SoftDeleteProfile(ctx context.Context, address string) error
SoftDeleteVault(ctx context.Context, id string) error
UpdateAccountLabel(ctx context.Context, arg UpdateAccountLabelParams) (Account, error)
UpdateAccountSequence(ctx context.Context, arg UpdateAccountSequenceParams) (Account, error)
UpdateProfile(ctx context.Context, arg UpdateProfileParams) (Profile, error)
UpdateVault(ctx context.Context, arg UpdateVaultParams) (Vault, error)
}
var _ Querier = (*Queries)(nil)

1177
users/query.sql.go Normal file

File diff suppressed because it is too large Load Diff

234
users/sink/query.sql Normal file
View File

@@ -0,0 +1,234 @@
-- PROFILE QUERIES
-- name: InsertProfile :one
INSERT INTO profiles (
address,
handle,
origin,
name
) VALUES (?, ?, ?, ?)
RETURNING *;
-- name: GetProfileByID :one
SELECT * FROM profiles
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetProfileByAddress :one
SELECT * FROM profiles
WHERE address = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetProfileByHandle :one
SELECT * FROM profiles
WHERE handle = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: CheckHandleExists :one
SELECT COUNT(*) > 0 as handle_exists FROM profiles
WHERE handle = ?
AND deleted_at IS NULL;
-- name: UpdateProfile :one
UPDATE profiles
SET
name = ?,
handle = ?,
updated_at = CURRENT_TIMESTAMP
WHERE address = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteProfile :exec
UPDATE profiles
SET deleted_at = CURRENT_TIMESTAMP
WHERE address = ?;
-- name: ListProfiles :many
SELECT * FROM profiles
WHERE deleted_at IS NULL
ORDER BY created_at DESC
LIMIT ? OFFSET ?;
-- VAULT QUERIES
-- name: InsertVault :one
INSERT INTO vaults (
handle,
origin,
address,
cid,
config,
session_id,
redirect_uri
) VALUES (?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetVaultByID :one
SELECT * FROM vaults
WHERE id = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: GetVaultsByHandle :many
SELECT * FROM vaults
WHERE handle = ?
AND deleted_at IS NULL
ORDER BY created_at DESC;
-- name: GetVaultConfigByCID :one
SELECT * FROM vaults
WHERE cid = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: GetVaultRedirectURIBySessionID :one
SELECT redirect_uri FROM vaults
WHERE session_id = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: UpdateVault :one
UPDATE vaults
SET
config = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteVault :exec
UPDATE vaults
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- ACCOUNT QUERIES
-- name: InsertAccount :one
INSERT INTO accounts (
number,
sequence,
address,
public_key,
chain_id,
block_created,
controller,
label,
is_subsidiary,
is_validator,
is_delegator,
is_accountable
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetAccountByID :one
SELECT * FROM accounts
WHERE id = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountByAddress :one
SELECT * FROM accounts
WHERE address = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountsByHandle :many
SELECT * FROM accounts
WHERE handle = ? AND deleted_at IS NULL
ORDER BY created_at DESC;
-- name: GetAccountByController :one
SELECT * FROM accounts
WHERE controller = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountByPublicKey :one
SELECT * FROM accounts
WHERE public_key = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountByNumber :one
SELECT * FROM accounts
WHERE number = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountBySequence :one
SELECT * FROM accounts
WHERE sequence = ? AND deleted_at IS NULL
LIMIT 1;
-- name: GetAccountsByChainID :many
SELECT * FROM accounts
WHERE chain_id = ? AND deleted_at IS NULL
ORDER BY sequence DESC;
-- name: GetAccountsByController :many
SELECT * FROM accounts
WHERE controller = ? AND deleted_at IS NULL
ORDER BY created_at DESC;
-- name: GetAccountsByLabel :many
SELECT * FROM accounts
WHERE label = ? AND deleted_at IS NULL
ORDER BY created_at DESC;
-- name: UpdateAccountSequence :one
UPDATE accounts
SET
sequence = ?,
updated_at = CURRENT_TIMESTAMP
WHERE address = ?
AND deleted_at IS NULL
RETURNING *;
-- name: UpdateAccountLabel :one
UPDATE accounts
SET
label = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
AND deleted_at IS NULL
RETURNING *;
-- name: SoftDeleteAccount :exec
UPDATE accounts
SET deleted_at = CURRENT_TIMESTAMP
WHERE id = ?;
-- name: ListValidatorAccounts :many
SELECT * FROM accounts
WHERE is_validator = 1
AND deleted_at IS NULL
ORDER BY created_at DESC;
-- name: ListDelegatorAccounts :many
SELECT * FROM accounts
WHERE is_delegator = 1
AND deleted_at IS NULL
ORDER BY created_at DESC;
-- CREDENTIAL QUERIES
-- name: InsertCredential :one
INSERT INTO credentials (
handle,
credential_id,
authenticator_attachment,
origin,
type,
transports
) VALUES (?, ?, ?, ?, ?, ?)
RETURNING *;
-- name: GetCredentialsByHandle :many
SELECT * FROM credentials
WHERE handle = ?
AND deleted_at IS NULL;
-- name: GetCredentialByID :one
SELECT * FROM credentials
WHERE credential_id = ?
AND deleted_at IS NULL
LIMIT 1;
-- name: SoftDeleteCredential :exec
UPDATE credentials
SET deleted_at = CURRENT_TIMESTAMP
WHERE credential_id = ?;

81
users/sink/schema.sql Normal file
View File

@@ -0,0 +1,81 @@
-- Credentials store WebAuthn credentials
CREATE TABLE credentials (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
handle TEXT NOT NULL,
credential_id TEXT NOT NULL UNIQUE,
authenticator_attachment TEXT NOT NULL,
origin TEXT NOT NULL,
type TEXT NOT NULL,
transports TEXT NOT NULL
);
-- Accounts represent blockchain accounts
CREATE TABLE accounts (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
number INTEGER NOT NULL,
sequence INTEGER NOT NULL DEFAULT 0,
address TEXT NOT NULL UNIQUE,
public_key TEXT NOT NULL CHECK(json_valid(public_key)),
chain_id TEXT NOT NULL,
block_created INTEGER NOT NULL,
controller TEXT NOT NULL,
label TEXT NOT NULL,
handle TEXT NOT NULL,
is_subsidiary BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_subsidiary IN (0,1)),
is_validator BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_validator IN (0,1)),
is_delegator BOOLEAN NOT NULL DEFAULT FALSE CHECK(is_delegator IN (0,1)),
is_accountable BOOLEAN NOT NULL DEFAULT TRUE CHECK(is_accountable IN (0,1))
);
-- Profiles represent user identities
CREATE TABLE profiles (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
address TEXT NOT NULL,
handle TEXT NOT NULL UNIQUE,
origin TEXT NOT NULL,
name TEXT NOT NULL,
UNIQUE(address, origin)
);
-- Vaults store encrypted data
CREATE TABLE vaults (
id TEXT PRIMARY KEY,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
handle TEXT NOT NULL,
origin TEXT NOT NULL,
address TEXT NOT NULL,
cid TEXT NOT NULL UNIQUE,
config TEXT NOT NULL CHECK(json_valid(config)),
session_id TEXT NOT NULL,
redirect_uri TEXT NOT NULL
);
CREATE INDEX idx_credentials_handle ON credentials(handle);
CREATE INDEX idx_credentials_origin ON credentials(origin);
CREATE INDEX idx_credentials_deleted_at ON credentials(deleted_at);
CREATE INDEX idx_accounts_address ON accounts(address);
CREATE INDEX idx_accounts_chain_id ON accounts(chain_id);
CREATE INDEX idx_accounts_block_created ON accounts(block_created);
CREATE INDEX idx_accounts_label ON accounts(label);
CREATE INDEX idx_accounts_controller ON accounts(controller);
CREATE INDEX idx_accounts_deleted_at ON accounts(deleted_at);
CREATE INDEX idx_profiles_handle ON profiles(handle);
CREATE INDEX idx_profiles_address ON profiles(address);
CREATE INDEX idx_profiles_deleted_at ON profiles(deleted_at);
CREATE INDEX idx_vaults_handle ON vaults(handle);
CREATE INDEX idx_vaults_session_id ON vaults(session_id);
CREATE INDEX idx_vaults_deleted_at ON vaults(deleted_at);