synapsecns/sanguine

View on GitHub
packages/rfq-indexer/api/src/graphql/resolvers.ts

Summary

Maintainability
F
1 wk
Test Coverage
import { sql } from 'kysely'

import { db } from '../db'

// typical fields to return for a BridgeRequest
const qDeposits = () => {
  return db
    .selectFrom('BridgeRequestEvents')
    .select([
      'BridgeRequestEvents.transactionId as transactionId_deposit',
      'BridgeRequestEvents.blockNumber as blockNumber_deposit',
      'BridgeRequestEvents.blockTimestamp as blockTimestamp_deposit',
      'BridgeRequestEvents.transactionHash as transactionHash_deposit',
      'BridgeRequestEvents.originChain',
      'BridgeRequestEvents.destChain',
      'BridgeRequestEvents.originChainId',
      'BridgeRequestEvents.destChainId',
      'BridgeRequestEvents.originToken',
      'BridgeRequestEvents.destToken',
      'BridgeRequestEvents.originAmountFormatted',
      'BridgeRequestEvents.destAmountFormatted',
      'BridgeRequestEvents.sender',
      'BridgeRequestEvents.request',
      'BridgeRequestEvents.sendChainGas',
    ])
    .where('BridgeRequestEvents.blockTimestamp', '>', 1722729600)
  // if index is partially loaded, we must limit lookback or will have various data issues from relays
  // that happened to be in flight at the point of the index's start.
  // may also improve query performance
}

// typical fields to return for a BridgeRelayed event when it is joined to a BridgeRequest
const qRelays = () => {
  return db
    .selectFrom('BridgeRelayedEvents')
    .select([
      'BridgeRelayedEvents.transactionId as transactionId_relay',
      'BridgeRelayedEvents.blockNumber as blockNumber_relay',
      'BridgeRelayedEvents.blockTimestamp as blockTimestamp_relay',
      'BridgeRelayedEvents.transactionHash as transactionHash_relay',

      'BridgeRelayedEvents.relayer as relayer_relay',
      'BridgeRelayedEvents.to as to_relay',
    ])
}

// typical fields to return for a BridgeProofProvided event when it is joined to a BridgeRequest
const qProofs = () => {
  return db
    .selectFrom('BridgeProofProvidedEvents')
    .select([
      'BridgeProofProvidedEvents.transactionId as transactionId_proof',
      'BridgeProofProvidedEvents.blockNumber as blockNumber_proof',
      'BridgeProofProvidedEvents.blockTimestamp as blockTimestamp_proof',
      'BridgeProofProvidedEvents.transactionHash as transactionHash_proof',

      'BridgeProofProvidedEvents.relayer as relayer_proof',
    ])
}

// typical fields to return for a BridgeDepositClaimed event when it is joined to a BridgeRequest
const qClaims = () => {
  return db
    .selectFrom('BridgeDepositClaimedEvents')
    .select([
      'BridgeDepositClaimedEvents.transactionId as transactionId_claim',
      'BridgeDepositClaimedEvents.blockNumber as blockNumber_claim',
      'BridgeDepositClaimedEvents.blockTimestamp as blockTimestamp_claim',
      'BridgeDepositClaimedEvents.transactionHash as transactionHash_claim',

      'BridgeDepositClaimedEvents.to as to_claim',
      'BridgeDepositClaimedEvents.relayer as relayer_claim',
      'BridgeDepositClaimedEvents.amountFormatted as amountFormatted_claim',
    ])
}

// typical fields to return for a BridgeDepositRefunded event when it is joined to a BridgeRequest
const qRefunds = () => {
  return db
    .selectFrom('BridgeDepositRefundedEvents')
    .select([
      'BridgeDepositRefundedEvents.transactionId as transactionId_refund',
      'BridgeDepositRefundedEvents.blockNumber as blockNumber_refund',
      'BridgeDepositRefundedEvents.blockTimestamp as blockTimestamp_refund',
      'BridgeDepositRefundedEvents.transactionHash as transactionHash_refund',

      'BridgeDepositRefundedEvents.to as to_refund',
      'BridgeDepositRefundedEvents.amountFormatted as amountFormatted_refund',
    ])
}

// typical fields to return for a BridgeProofDisputed event when it is joined to a BridgeRequest
const qDisputes = () => {
  return db
    .selectFrom('BridgeProofDisputedEvents')
    .select([
      'BridgeProofDisputedEvents.transactionId as transactionId_dispute',
      'BridgeProofDisputedEvents.blockNumber as blockNumber_dispute',
      'BridgeProofDisputedEvents.blockTimestamp as blockTimestamp_dispute',
      'BridgeProofDisputedEvents.transactionHash as transactionHash_dispute',
      'BridgeProofDisputedEvents.originChainId as originChainId_dispute',
      'BridgeProofDisputedEvents.originChain as originChain_dispute',
    ])
}

// using the suffix of a field, move it into a nested sub-object. This is a cleaner final resultset
// example: transactionHash_deposit:0xyz would get moved into BridgeRequest{transactionHash:0xyz}
//
// also note that transactionId_xxxx will just convert into a single "transactionId"
const nest_results = (sqlResults: any[]) => {
  return sqlResults.map((transaction: any) => {
    const bridgeRequest: { [key: string]: any } = {}
    const bridgeRelay: { [key: string]: any } = {}
    const bridgeProof: { [key: string]: any } = {}
    const bridgeClaim: { [key: string]: any } = {}
    const bridgeRefund: { [key: string]: any } = {}
    const bridgeDispute: { [key: string]: any } = {}
    const transactionFields: { [key: string]: any } = {}

    let transactionIdSet = false

    for (const [key, value] of Object.entries(transaction)) {
      if (key.startsWith('transactionId')) {
        if (!transactionIdSet) {
          transactionFields[key.replace(/_.+$/, '')] = value
          transactionIdSet = true
        }
        // Ignore other transactionId fields
      } else if (key.endsWith('_deposit')) {
        bridgeRequest[key.replace('_deposit', '')] = value
      } else if (key.endsWith('_relay')) {
        bridgeRelay[key.replace('_relay', '')] = value
      } else if (key.endsWith('_proof')) {
        bridgeProof[key.replace('_proof', '')] = value
      } else if (key.endsWith('_claim')) {
        bridgeClaim[key.replace('_claim', '')] = value
      } else if (key.endsWith('_refund')) {
        bridgeRefund[key.replace('_refund', '')] = value
      } else if (key.endsWith('_dispute')) {
        bridgeDispute[key.replace('_dispute', '')] = value
      } else {
        transactionFields[key] = value
      }
    }

    const result: { [key: string]: any } = { Bridge: transactionFields }
    if (Object.keys(bridgeRequest).length) {
      result.BridgeRequest = bridgeRequest
    }
    if (Object.keys(bridgeRelay).length) {
      result.BridgeRelay = bridgeRelay
    }
    if (Object.keys(bridgeProof).length) {
      result.BridgeProof = bridgeProof
    }
    if (Object.keys(bridgeClaim).length) {
      result.BridgeClaim = bridgeClaim
    }
    if (Object.keys(bridgeRefund).length) {
      result.BridgeRefund = bridgeRefund
    }
    if (Object.keys(bridgeDispute).length) {
      result.BridgeDispute = bridgeDispute
    }
    return result
  })
}

const resolvers = {
  Query: {
    events: async (
      _: any,
      { first = 10, after, filter }: { first?: any; after?: any; filter?: any }
    ) => {
      let query = db
        .selectFrom('BridgeRequestEvents')
        .select([
          'BridgeRequestEvents.id',
          'BridgeRequestEvents.transactionId',
          'BridgeRequestEvents.blockNumber',
          'BridgeRequestEvents.blockTimestamp',
          'BridgeRequestEvents.transactionHash',
          'BridgeRequestEvents.originChainId',
          'BridgeRequestEvents.originChain',
        ])
        .unionAll(
          db
            .selectFrom('BridgeRelayedEvents')
            .select([
              'BridgeRelayedEvents.id',
              'BridgeRelayedEvents.transactionId',
              'BridgeRelayedEvents.blockNumber',
              'BridgeRelayedEvents.blockTimestamp',
              'BridgeRelayedEvents.transactionHash',
              'BridgeRelayedEvents.originChainId',
              'BridgeRelayedEvents.originChain',
            ])
        )
        .unionAll(
          db
            .selectFrom('BridgeProofProvidedEvents')
            .select([
              'BridgeProofProvidedEvents.id',
              'BridgeProofProvidedEvents.transactionId',
              'BridgeProofProvidedEvents.blockNumber',
              'BridgeProofProvidedEvents.blockTimestamp',
              'BridgeProofProvidedEvents.transactionHash',
              'BridgeProofProvidedEvents.originChainId',
              'BridgeProofProvidedEvents.originChain',
            ])
        )
        .unionAll(
          db
            .selectFrom('BridgeDepositRefundedEvents')
            .select([
              'BridgeDepositRefundedEvents.id',
              'BridgeDepositRefundedEvents.transactionId',
              'BridgeDepositRefundedEvents.blockNumber',
              'BridgeDepositRefundedEvents.blockTimestamp',
              'BridgeDepositRefundedEvents.transactionHash',
              'BridgeDepositRefundedEvents.originChainId',
              'BridgeDepositRefundedEvents.originChain',
            ])
        )
        .unionAll(
          db
            .selectFrom('BridgeDepositClaimedEvents')
            .select([
              'BridgeDepositClaimedEvents.id',
              'BridgeDepositClaimedEvents.transactionId',
              'BridgeDepositClaimedEvents.blockNumber',
              'BridgeDepositClaimedEvents.blockTimestamp',
              'BridgeDepositClaimedEvents.transactionHash',
              'BridgeDepositClaimedEvents.originChainId',
              'BridgeDepositClaimedEvents.originChain',
            ])
        )
        .unionAll(
          db
            .selectFrom('BridgeProofDisputedEvents')
            .select([
              'BridgeProofDisputedEvents.id',
              'BridgeProofDisputedEvents.transactionId',
              'BridgeProofDisputedEvents.blockNumber',
              'BridgeProofDisputedEvents.blockTimestamp',
              'BridgeProofDisputedEvents.transactionHash',
              'BridgeProofDisputedEvents.originChainId',
              'BridgeProofDisputedEvents.originChain',
            ])
        )

      if (filter) {
        if (filter.transactionId) {
          query = query.where('transactionId', '=', filter.transactionId)
        }
        if (filter.originChainId) {
          query = query.where('originChainId', '=', filter.originChainId)
        }
        // Add more filters as needed
      }

      if (after) {
        query = query.where('id', '>', after)
      }

      const events = await query
        .orderBy('blockTimestamp', 'desc')
        .limit(first + 1)
        .execute()

      const hasNextPage = events.length > first
      const edges = events.slice(0, first).map((event: any) => ({
        node: event,
        cursor: event.id,
      }))

      return {
        edges,
        pageInfo: {
          hasNextPage,
          endCursor: edges.length > 0 ? edges[edges.length - 1]?.cursor : null,
        },
      }
    },
    pendingTransactionsMissingRelay: async () => {
      const query = db
        .with('deposits', () => qDeposits())
        .with('relays', () => qRelays())
        .with('refunds', () => qRefunds())
        .with(
          'combined',
          (qb) =>
            qb
              .selectFrom('deposits')
              .selectAll('deposits')
              .leftJoin(
                'relays',
                'transactionId_deposit',
                'transactionId_relay'
              )
              .leftJoin(
                'refunds',
                'transactionId_deposit',
                'transactionId_refund'
              )
              .where('transactionId_relay', 'is', null) // is not relayed
              .where('transactionId_refund', 'is', null) // is not refunded
        )
        .selectFrom('combined')
        .selectAll()
        .orderBy('blockTimestamp_deposit', 'desc')

      return nest_results(await query.execute())
    },
    pendingTransactionsMissingProof: async () => {
      const query = db
        .with('deposits', () => qDeposits())
        .with('relays', () => qRelays())
        .with('proofs', () => qProofs())
        .with('combined', (qb) =>
          qb
            .selectFrom('deposits')
            .innerJoin('relays', 'transactionId_deposit', 'transactionId_relay')
            .leftJoin('proofs', 'transactionId_deposit', 'transactionId_proof')
            .selectAll('deposits')
            .selectAll('relays')
            .where('transactionId_proof', 'is', null)
        )
        .selectFrom('combined')
        .selectAll()
        .orderBy('blockTimestamp_relay', 'desc')

      return nest_results(await query.execute())
    },
    pendingTransactionsMissingClaim: async () => {
      const query = db
        .with('deposits', () => qDeposits())
        .with('relays', () => qRelays())
        .with('proofs', () => qProofs())
        .with('claims', () => qClaims())
        .with('combined', (qb) =>
          qb
            .selectFrom('deposits')
            .innerJoin('relays', 'transactionId_deposit', 'transactionId_relay')
            .innerJoin('proofs', 'transactionId_deposit', 'transactionId_proof')
            .leftJoin('claims', 'transactionId_deposit', 'transactionId_claim')
            .selectAll('deposits')
            .selectAll('relays')
            .selectAll('proofs')
            .where('transactionId_claim', 'is', null)
        )
        .selectFrom('combined')
        .selectAll()
        .orderBy('blockTimestamp_proof', 'desc')

      return nest_results(await query.execute())
    },
    recentInvalidRelays: async () => {
      const query = db
        .selectFrom('BridgeRelayedEvents')
        .leftJoin(
          'BridgeRequestEvents',
          'BridgeRelayedEvents.transactionId',
          'BridgeRequestEvents.transactionId'
        )
        .select([
          'BridgeRelayedEvents.transactionId',
          'BridgeRelayedEvents.blockNumber',
          'BridgeRelayedEvents.blockTimestamp',
          'BridgeRelayedEvents.transactionHash',

          'BridgeRelayedEvents.originChain',
          'BridgeRelayedEvents.destChain',
          'BridgeRelayedEvents.originChainId',
          'BridgeRelayedEvents.destChainId',
          'BridgeRelayedEvents.originToken',
          'BridgeRelayedEvents.destToken',
          'BridgeRelayedEvents.originAmountFormatted',
          'BridgeRelayedEvents.destAmountFormatted',

          'BridgeRelayedEvents.to',
          'BridgeRelayedEvents.relayer',
        ])
        // lookback approx 2 weeks
        .where(
          'BridgeRelayedEvents.blockTimestamp',
          '>',
          Math.floor(Date.now() / 1000) - 2 * 7 * 24 * 60 * 60
        )
        .where('BridgeRequestEvents.transactionId', 'is', null)

      // intentionally do not nest - doesnt make sense w/ this dataset because the whole point is that no Deposit exists
      return query.execute()
    },
    refundedAndRelayedTransactions: async () => {
      const query = db
        .with('deposits', () => qDeposits())
        .with('relays', () => qRelays())
        .with('refunds', () => qRefunds())
        .with('combined', (qb) =>
          qb
            .selectFrom('deposits')
            .innerJoin('relays', 'transactionId_deposit', 'transactionId_relay')
            .innerJoin(
              'refunds',
              'transactionId_deposit',
              'transactionId_refund'
            )
            .selectAll('deposits')
            .selectAll('relays')
            .selectAll('refunds')
        )
        .selectFrom('combined')
        .selectAll()
        .orderBy('blockTimestamp_refund', 'desc')

      return nest_results(await query.execute())
    },
    transactionById: async (
      _: any,
      { transactionId }: { transactionId: string }
    ) => {
      const query = db
        .with('deposits', () =>
          qDeposits().where('transactionId', '=', transactionId)
        )
        .with('relays', () => qRelays())
        .with('proofs', () => qProofs())
        .with('claims', () => qClaims())
        .with('refunds', () => qRefunds())
        .with('combined', (qb) =>
          qb
            .selectFrom('deposits')
            .leftJoin('relays', 'transactionId_deposit', 'transactionId_relay')
            .leftJoin('proofs', 'transactionId_deposit', 'transactionId_proof')
            .leftJoin('claims', 'transactionId_deposit', 'transactionId_claim')
            .leftJoin(
              'refunds',
              'transactionId_deposit',
              'transactionId_refund'
            )
            .selectAll('deposits')
            .selectAll('relays')
            .selectAll('proofs')
            .selectAll('claims')
            .selectAll('refunds')
        )
        .selectFrom('combined')
        .selectAll()

      const nestedResult = nest_results(await query.execute())[0] || null

      if (nestedResult) {
        return Object.fromEntries(
          Object.entries(nestedResult).filter(([_, value]) => {
            if (value === null) {
              return false
            }
            if (typeof value !== 'object') {
              return true
            }
            return Object.values(value).some((v) => v !== null)
          })
        )
      }

      return null
    },
    conflictingProofs: async () => {
      const query = db
        .with('deposits', () => qDeposits())
        .with('relays', () => qRelays())
        .with('proofs', () => qProofs())
        .with('combined', (qb) =>
          qb
            .selectFrom('deposits')
            .leftJoin('relays', 'transactionId_deposit', 'transactionId_relay')
            .leftJoin('proofs', 'transactionId_deposit', 'transactionId_proof')
            .selectAll('deposits')
            .selectAll('relays')
            .selectAll('proofs')
        )
        .selectFrom('combined')
        .selectAll()
        .where('relayer_proof', 'is not', null)
        .where('relayer_relay', 'is not', null)
        .where(
          (eb) =>
            sql<boolean>`LOWER(${eb.ref('relayer_relay')}) != LOWER(${eb.ref(
              'relayer_proof'
            )})`
        )
        .orderBy('blockTimestamp_proof', 'desc')

      return nest_results(await query.execute())
    },
    disputedRelays: async () => {
      const query = db
        .with('deposits', () => qDeposits())
        .with('relays', () => qRelays())
        .with('proofs', () => qProofs())
        .with('disputes', () => qDisputes())
        .with('combined', (qb) =>
          qb
            .selectFrom('proofs')
            .leftJoin(
              'disputes',
              'transactionId_proof',
              'transactionId_dispute'
            )
            .selectAll('proofs')
            .selectAll('disputes')
        )
        .selectFrom('combined')
        .selectAll()
        .orderBy('blockTimestamp_proof', 'desc')

      return nest_results(await query.execute())
    },
  },
  BridgeEvent: {
    // eslint-disable-next-line prefer-arrow/prefer-arrow-functions
    __resolveType(obj: any) {
      // Implement logic to determine the event type based on the object properties
      // For example:
      if ('sender' in obj) {
        return 'BridgeRequestEvent'
      }
      if ('relayer' in obj && 'to' in obj) {
        return 'BridgeRelayedEvent'
      }
      if ('relayer' in obj && !('to' in obj)) {
        return 'BridgeProofProvidedEvent'
      }
      if ('to' in obj && 'token' in obj) {
        return 'BridgeDepositRefundedEvent'
      }
      if ('relayer' in obj && 'to' in obj && 'token' in obj) {
        return 'BridgeDepositClaimedEvent'
      }
      return null
    },
  },
}

export { resolvers }