import type { ColumnType } from 'kysely' import * as Id from '../../internal/Id.js' import type * as Db from '../Db.js' import type * as db_Schema from '../Schema.js' /** A Better Auth `Date` written as canonical ISO text. */ type Timestamp = ColumnType /** Columns of Better Auth's pinned `account` model. */ export type Table = Omit< db_Schema.AuthAccount, 'accessTokenExpiresAt' | 'createdAt' | 'refreshTokenExpiresAt' | 'updatedAt' > & { accessTokenExpiresAt: Timestamp | null createdAt: Timestamp refreshTokenExpiresAt: Timestamp | null updatedAt: Timestamp } const walletIssuer = 'tempo:siwe' /** * Reads the user linked to a SIWE wallet. * * @param db - The database. * @param address - The wallet address. * @returns The linked user id, or `undefined` when absent. */ export async function getWalletUserId(db: Db.Db, address: string): Promise { const account = await db.kysely .selectFrom('auth_accounts') .select('userId') .where('accountId', '=', address.toLowerCase()) .where('issuer', '=', walletIssuer) .executeTakeFirst() return account?.userId } /** * Reads the wallet linked to a user. * * @param db - The database. * @param userId - The user id. * @returns The linked wallet address, or `undefined` when absent. */ export async function getWalletAddress(db: Db.Db, userId: string): Promise { const account = await db.kysely .selectFrom('auth_accounts') .select('accountId') .where('issuer', '=', walletIssuer) .where('userId', '=', userId) .orderBy('createdAt', 'asc') .executeTakeFirst() return account?.accountId } /** * Sets a SIWE wallet's stable user and returns the stored user id. * * @param db - The database. * @param options - Wallet link fields. * @returns The stored user id. */ export async function setWalletUser(db: Db.Db, options: setWalletUser.Options): Promise { const accountId = options.address.toLowerCase() const now = new Date().toISOString() const account = await db.kysely .insertInto('auth_accounts') .values({ accessToken: null, accessTokenExpiresAt: null, accountId, createdAt: now, id: Id.generate('acc'), idToken: null, issuer: walletIssuer, password: null, providerId: 'siwe', refreshToken: null, refreshTokenExpiresAt: null, scope: null, updatedAt: now, userId: options.userId, }) .onConflict((oc) => oc.columns(['issuer', 'accountId']).doUpdateSet({ updatedAt: now, userId: options.userId, }), ) .returning('userId') .executeTakeFirstOrThrow() return account.userId } export declare namespace setWalletUser { /** Fields accepted when setting a SIWE wallet user. */ type Options = { /** Wallet address to link. */ address: string /** Stable owning user id. */ userId: string } }