apps/keeper/src/jobs/common.ts

What the vault owes, from the ledger. Shown whole, as it was in the repository when this site was built. Line numbers link: add #L12 to the address.

apps/keeper/src/jobs/common.ts99 lines
1// Helpers shared by the Solana jobs: the coins this keeper handles and typed settings.2import { eq, sql } from 'drizzle-orm';3import { coins, collections } from '@solary/db';4import { getSetting, type JobCtx } from '../ctx';5import type { CoinRef } from '../solana/adapter';6 7export interface CoinRow {8  mint: string;9  name: string;10  symbol: string;11  collection: string | null;12  collectionName: string | null;13  collectionStatus: string | null;14  deployer: string;15  holdersBps: number;16  creatorBps: number;17  buybackBps: number;18  platformBps: number;19  official: boolean;20  status: 'active' | 'paused' | 'alert';21  graduated: boolean;22  accIndex: string;23  holdersLamports: bigint;24  holdersPaidLamports: bigint;25  lastClaimAt: Date | null;26  lastPayoutAt: Date | null;27  marketAt: Date | null;28  createdAt: Date;29}30 31/** Fake mode handles demo coins (FAKE… mints) only; real mode never touches them. */32export const handles = (ctx: Pick<JobCtx, 'sol'>, mint: string) => (ctx.sol.mode === 'fake') === mint.startsWith('FAKE');33 34export async function keeperCoins(ctx: JobCtx): Promise<CoinRow[]> {35  const rows = await ctx.db36    .select({37      mint: coins.mint,38      name: coins.name,39      symbol: coins.symbol,40      collection: coins.collection,41      collectionName: collections.name,42      collectionStatus: collections.status,43      deployer: coins.deployer,44      holdersBps: coins.holdersBps,45      creatorBps: coins.creatorBps,46      buybackBps: coins.buybackBps,47      platformBps: coins.platformBps,48      official: coins.official,49      status: coins.status,50      graduated: coins.graduated,51      accIndex: coins.accIndex,52      holdersLamports: coins.holdersLamports,53      holdersPaidLamports: coins.holdersPaidLamports,54      lastClaimAt: coins.lastClaimAt,55      lastPayoutAt: coins.lastPayoutAt,56      marketAt: coins.marketAt,57      createdAt: coins.createdAt,58    })59    .from(coins)60    .leftJoin(collections, eq(collections.address, coins.collection))61    .orderBy(coins.createdAt);62  return rows.filter((r) => handles(ctx, r.mint));63}64 65export const coinRef = (c: CoinRow): CoinRef => ({ mint: c.mint, creatorBps: c.creatorBps, deployer: c.deployer, graduated: c.graduated });66 67export async function settingBig(ctx: JobCtx, key: string, fallback: bigint): Promise<bigint> {68  const v = await getSetting<string | number | null>(ctx.db, key, null);69  try {70    return v === null ? fallback : BigInt(v);71  } catch {72    return fallback;73  }74}75 76export async function settingNum(ctx: JobCtx, key: string, fallback: number): Promise<number> {77  const v = await getSetting<string | number | null>(ctx.db, key, null);78  const n = Number(v);79  return v === null || !Number.isFinite(n) ? fallback : n;80}81 82/** What the vault owes, from the ledger: unpaid holder rewards, unspent buyback, unswept platform income. */83export async function vaultObligations(ctx: JobCtx): Promise<{ holders: bigint; buyback: bigint; platform: bigint; total: bigint }> {84  const fake = ctx.sol.mode === 'fake';85  const like = fake ? sql`LIKE 'FAKE%'` : sql`NOT LIKE 'FAKE%'`;86  const [r] = (await ctx.db.execute(sql`87    SELECT88      COALESCE((SELECT SUM(holders_lamports - holders_paid_lamports) FROM coins WHERE mint ${like}), 0)::text AS holders,89      COALESCE((SELECT SUM(buyback_lamports) FROM coins WHERE mint ${like}), 0)::text AS buyback_in,90      COALESCE((SELECT SUM(lamports) FROM buybacks WHERE COALESCE(buy_tx, '') ${like}), 0)::text AS buyback_out,91      COALESCE((SELECT SUM(platform_lamports) FROM coins WHERE mint ${like}), 0)::text AS platform_in,92      COALESCE((SELECT SUM(lamports) FROM sweeps WHERE COALESCE(tx, '') ${like}), 0)::text AS platform_out93  `)) as unknown as Array<Record<string, string>>;94  const holders = BigInt(r!.holders!);95  const buyback = BigInt(r!.buyback_in!) - BigInt(r!.buyback_out!);96  const platform = BigInt(r!.platform_in!) - BigInt(r!.platform_out!);97  const pos = (x: bigint) => (x > 0n ? x : 0n);98  return { holders: pos(holders), buyback: pos(buyback), platform: pos(platform), total: pos(holders) + pos(buyback) + pos(platform) };99}