import { type Selectable, sql } from 'kysely'
import * as Id from '../../internal/Id.js'
import type * as Db from '../Db.js'
import type * as db_Schema from '../Schema.js'
/** Columns of the `verified_token_requests` table. */
export type Table = db_Schema.VerifiedTokenRequest
/** A stored verification request. */
export type Record = Selectable
/** Review status of a request. */
export type Status = Record['status']
/**
* Inserts a pending request; `undefined` when a pending request for
* (chainId, address) already exists.
*/
export function create(db: Db.Db, input: create.Input): Promise {
const now = new Date().toISOString()
return db.kysely
.insertInto('verified_token_requests')
.values({
address: input.address,
chainId: input.chainId,
createdAt: now,
currency: input.currency,
decimals: input.decimals,
id: Id.generate('vtr'),
logo: input.logo ?? null,
logoUri: input.logoUri ?? null,
name: input.name,
note: input.note ?? null,
orgId: input.orgId,
requestedBy: input.requestedBy,
reviewNote: null,
reviewedAt: null,
reviewedBy: null,
status: 'pending',
symbol: input.symbol,
updatedAt: now,
})
.onConflict((oc) =>
oc
.columns(['chainId', 'address'])
// `sql.lit` keeps the predicate literal; a bound parameter would
// defeat partial-index inference for the conflict target.
.where('status', '=', sql.lit('pending'))
.doNothing(),
)
.returningAll()
.executeTakeFirst()
}
export declare namespace create {
/** Fields accepted when inserting a request. */
type Input = {
/** Requested token contract address (lowercase). */
address: string
/** Chain the token belongs to. */
chainId: number
/** Display currency, e.g. `USD`. */
currency: string
/** Decimal precision. */
decimals: number
/** Inline SVG logo markup uploaded by the requester. */
logo?: string | undefined
/** HTTPS logo URL from chain metadata. */
logoUri?: string | undefined
/** Display name. */
name: string
/** Requester note to reviewers. */
note?: string | undefined
/** Requesting organization id (`org_…`). */
orgId: string
/** User id that submitted the request, or `super_admin`. */
requestedBy: string
/** Ticker symbol. */
symbol: string
}
}
/** Lists an organization's requests, newest first. */
export function list(db: Db.Db, orgId: string): Promise {
return db.kysely
.selectFrom('verified_token_requests')
.selectAll()
.where('orgId', '=', orgId)
.orderBy('createdAt', 'desc')
.execute()
}
/**
* Lists requests in one status globally, oldest first (review-queue order),
* with the requesting organization's current name when it still exists.
*/
export function listByStatus(
db: Db.Db,
status: Status,
): Promise<(Record & { orgName: string | null })[]> {
return db.kysely
.selectFrom('verified_token_requests')
.leftJoin('organizations', 'organizations.id', 'verified_token_requests.orgId')
.selectAll('verified_token_requests')
.select('organizations.name as orgName')
.where('status', '=', status)
.orderBy('verified_token_requests.createdAt', 'asc')
.execute()
}
/** Gets a request by id. */
export function get(db: Db.Db, id: string): Promise {
return db.kysely
.selectFrom('verified_token_requests')
.selectAll()
.where('id', '=', id)
.executeTakeFirst()
}
/** Counts an organization's pending requests. */
export async function countPending(db: Db.Db, orgId: string): Promise {
const row = await db.kysely
.selectFrom('verified_token_requests')
.select(({ fn }) => fn.countAll().as('count'))
.where('orgId', '=', orgId)
.where('status', '=', 'pending')
.executeTakeFirstOrThrow()
return Number(row.count)
}
/** Hard-deletes an org-scoped request while pending; `undefined` otherwise. */
export function remove(db: Db.Db, orgId: string, id: string): Promise {
return db.kysely
.deleteFrom('verified_token_requests')
.where('id', '=', id)
.where('orgId', '=', orgId)
.where('status', '=', 'pending')
.returningAll()
.executeTakeFirst()
}
/**
* Marks a pending request approved or denied with reviewer identity;
* `undefined` when absent or already reviewed.
*/
export function review(db: Db.Db, id: string, input: review.Input): Promise {
const now = new Date().toISOString()
return db.kysely
.updateTable('verified_token_requests')
.set({
reviewNote: input.reviewNote ?? null,
reviewedAt: now,
reviewedBy: input.reviewedBy,
status: input.status,
updatedAt: now,
})
.where('id', '=', id)
.where('status', '=', 'pending')
.returningAll()
.executeTakeFirst()
}
export declare namespace review {
/** Review outcome fields. */
type Input = {
/** Reviewer note shown on denial. */
reviewNote?: string | undefined
/** Reviewing admin email. */
reviewedBy: string
/** Review outcome. */
status: 'approved' | 'denied'
}
}
/** Maps a stored row to the wire shape (null `logo`/`logoUri` map to absent). */
export function toRequest(record: Record) {
const { logo, logoUri, ...rest } = record
return {
...rest,
...(logo === null ? {} : { logo }),
...(logoUri === null ? {} : { logoUri }),
}
}