export { bodyHash, buildSignMessage, type SignedEnvelope } from '@trumpsignal/contracts/signing' import { buildSignMessage, type SignedEnvelope } from '@trumpsignal/contracts/signing' /** * Produce {timestamp, signature} for a given action + optional body. * Caller supplies wagmi's `signMessageAsync`. */ /** * Get-or-create a cached "view" envelope for a wallet. Reused across page loads * within a short window below the server's 5-minute skew allowance. * Backend's replay-guard is disabled for the view action so the same sig can * be used multiple times while it remains fresh. */ const VIEW_TTL_MS = 4 * 60 * 1000 // 4 min — stays safely inside backend's 5 min skew window /** * Read-only: returns a cached, not-yet-expired view envelope if one exists. * Never signs — never triggers a wallet popup. Returns null if no fresh cache. */ export function getCachedViewEnvelope(action: string, wallet: string): SignedEnvelope | null { if (typeof window === 'undefined') return null const cacheKey = `ts:view:${action}:${wallet.toLowerCase()}` const raw = sessionStorage.getItem(cacheKey) if (!raw) return null try { const env = JSON.parse(raw) as SignedEnvelope if (Date.now() - env.timestamp < VIEW_TTL_MS) return env sessionStorage.removeItem(cacheKey) } catch (err) { console.warn('[signedRequest] corrupt cached envelope, ignoring', err) sessionStorage.removeItem(cacheKey) } return null } export async function getOrCreateViewEnvelope(params: { action: string wallet: string signMessageAsync: (args: { message: string }) => Promise }): Promise { if (typeof window === 'undefined') throw new Error('view envelope requires browser') const wallet = params.wallet.toLowerCase() const cacheKey = `ts:view:${params.action}:${wallet}` const raw = sessionStorage.getItem(cacheKey) if (raw) { try { const env = JSON.parse(raw) as SignedEnvelope if (Date.now() - env.timestamp < VIEW_TTL_MS) return env } catch (err) { console.warn('[signedRequest] corrupt cached envelope, ignoring', err) sessionStorage.removeItem(cacheKey) } } const env = await signRequest({ action: params.action, wallet, body: null, signMessageAsync: params.signMessageAsync, }) sessionStorage.setItem(cacheKey, JSON.stringify(env)) return env } export async function signRequest(params: { action: string wallet: string body: unknown | null signMessageAsync: (args: { message: string }) => Promise }): Promise { const timestamp = Date.now() const message = await buildSignMessage(params.action, params.wallet, timestamp, params.body) const signature = await params.signMessageAsync({ message }) return { wallet: params.wallet.toLowerCase(), timestamp, signature } }