import { calculateSponsoringSplit } from './platformCommissions' import { PLATFORM_BITCOIN_ADDRESS } from './platformConfig' import type { MempoolTransaction, TransactionVerificationResult } from './mempoolSpaceTypes' import { getTransaction, getConfirmations } from './mempoolSpaceApi' export function findTransactionOutputs( transaction: MempoolTransaction, authorMainnetAddress: string, expectedAuthorAmount: number, expectedPlatformAmount: number ): { authorOutput?: MempoolTransaction['vout'][0]; platformOutput?: MempoolTransaction['vout'][0] } { const authorOutput = transaction.vout.find( (output) => output.scriptpubkey_address === authorMainnetAddress && output.value === expectedAuthorAmount ) const platformOutput = transaction.vout.find( (output) => output.scriptpubkey_address === PLATFORM_BITCOIN_ADDRESS && output.value === expectedPlatformAmount ) const result: { authorOutput?: MempoolTransaction['vout'][0]; platformOutput?: MempoolTransaction['vout'][0] } = {} if (authorOutput) { result.authorOutput = authorOutput } if (platformOutput) { result.platformOutput = platformOutput } return result } export function buildVerificationResult( valid: boolean, confirmed: boolean, confirmations: number, authorOutput?: MempoolTransaction['vout'][0], platformOutput?: MempoolTransaction['vout'][0] ): TransactionVerificationResult { const result: TransactionVerificationResult = { valid, confirmed, confirmations, } if (!valid) { result.error = 'Transaction outputs do not match expected split' } if (authorOutput) { result.authorOutput = { address: authorOutput.scriptpubkey_address, amount: authorOutput.value, } } if (platformOutput) { result.platformOutput = { address: platformOutput.scriptpubkey_address, amount: platformOutput.value, } } return result } export async function validateTransactionOutputs( transaction: MempoolTransaction, authorMainnetAddress: string, split: { authorSats: number; platformSats: number } ): Promise<{ valid: boolean; confirmed: boolean; confirmations: number; authorOutput?: MempoolTransaction['vout'][0]; platformOutput?: MempoolTransaction['vout'][0] }> { const { authorOutput, platformOutput } = findTransactionOutputs( transaction, authorMainnetAddress, split.authorSats, split.platformSats ) const valid = Boolean(authorOutput && platformOutput) const confirmed = transaction.status.confirmed const confirmations = confirmed && transaction.status.block_height ? await getConfirmations(transaction.status.block_height) : 0 const result: { valid: boolean; confirmed: boolean; confirmations: number; authorOutput?: MempoolTransaction['vout'][0]; platformOutput?: MempoolTransaction['vout'][0] } = { valid, confirmed, confirmations, } if (authorOutput) { result.authorOutput = authorOutput } if (platformOutput) { result.platformOutput = platformOutput } return result } export function logVerificationFailure( txid: string, authorMainnetAddress: string, split: { authorSats: number; platformSats: number }, transaction: MempoolTransaction ): void { console.error('Transaction verification failed', { txid, authorAddress: authorMainnetAddress, platformAddress: PLATFORM_BITCOIN_ADDRESS, expectedAuthorAmount: split.authorSats, expectedPlatformAmount: split.platformSats, actualOutputs: transaction.vout.map((o) => ({ address: o.scriptpubkey_address, amount: o.value, })), timestamp: new Date().toISOString(), }) } export function handleVerificationError(txid: string, authorMainnetAddress: string, error: unknown): TransactionVerificationResult { const errorMessage = error instanceof Error ? error.message : 'Unknown error' console.error('Error verifying sponsoring transaction', { txid, authorAddress: authorMainnetAddress, error: errorMessage, timestamp: new Date().toISOString(), }) return { valid: false, confirmed: false, confirmations: 0, error: errorMessage, } } export async function verifySponsoringTransaction( txid: string, authorMainnetAddress: string ): Promise { try { const transaction = await getTransaction(txid) if (!transaction) { return { valid: false, confirmed: false, confirmations: 0, error: 'Transaction not found', } } const split = calculateSponsoringSplit() const validation = await validateTransactionOutputs(transaction, authorMainnetAddress, split) if (!validation.valid) { logVerificationFailure(txid, authorMainnetAddress, split, transaction) } return buildVerificationResult( validation.valid, validation.confirmed, validation.confirmations, validation.authorOutput, validation.platformOutput ) } catch (error) { return handleVerificationError(txid, authorMainnetAddress, error) } }