Solana Geyser gRPC - 필터

Solana Stream SDK
Solana Stream SDK는 오픈 소스 소프트웨어로 제공됩니다. 자세한 내용은 아래 GitHub 저장소를 방문해 주세요.

gRPC 필터 개요

Solana Geyser gRPC는 필터를 사용하여 특정 계정, 프로그램, 트랜잭션, slot, 블록 등 관심 있는 데이터만 효율적으로 가져옵니다.
아래에서는 Solana Stream SDK를 활용한 TypeScript 예제를 통해 각 필터의 구체적인 역할을 명확하게 설명합니다. Rust를 사용하는 경우에도 필터의 구조와 의미는 동일합니다.

각 필터의 역할과 예제

계정 구독

특정 계정의 실시간 업데이트를 구독합니다. 다음 예제는 Confirmed commitment 레벨에서 SOL-USDC OpenBook 계정을 구독합니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: { slots: {} },
  accounts: {
    'wsol/usdc': {
      account: ['8BnEgHoWFysVcuFFX7QztDmzuH8r5ZFvyP3sYwn1XTh6'],
    },
  },
  transactions: {},
  blocks: {},
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.CONFIRMED,
}
  • "wsol/usdc"는 클라이언트가 정의한 라벨입니다.
  • 계정, 프로그램, 블록, slot에 대한 여러 필터를 하나의 JSON 요청에 결합할 수 있습니다.

account_data_slice를 사용한 계정 구독

이 예제는 계정 데이터의 특정 부분만 가져오는 방법을 보여줍니다. USDC 토큰 계정의 전체 데이터(165바이트)를 가져오는 대신 offset 32에서 시작하여 40바이트를 가져옵니다. 이 범위에는 소유자 및 lamports 잔액과 같은 정보가 포함됩니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {},
  accounts: {
    usdc: {
      owner: ['TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'],
      filters: [
        {
          tokenAccountState: true,
        },
        {
          memcmp: {
            offset: 0,
            data: {
              base58: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
            },
          },
        },
      ],
    },
  },
  transactions: {},
  blocks: {},
  blocksMeta: {},
  entry: {},
  commitment: CommitmentLevel.CONFIRMED,
  accountsDataSlice: [{ offset: 32, length: 40 }],
}

프로그램 구독

이 예제는 특정 프로그램과 연관된 계정 업데이트를 구독하는 방법을 보여줍니다.
아래에서는 Processed commitment 레벨에서 Solend 프로그램이 소유한 계정의 업데이트를 구독합니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {
    slots: {},
  },
  accounts: {
    solend: {
      owner: ['So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo'],
    },
  },
  transactions: {},
  blocks: {},
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}
  • "solend"는 클라이언트가 자유롭게 설정할 수 있는 사용자 정의 라벨입니다.
  • 여러 프로그램을 구독하려면 다음 섹션인 "여러 프로그램 구독"을 참조하세요.

여러 프로그램 구독

이 예제는 여러 프로그램과 연관된 계정 업데이트를 한 번에 구독하는 방법을 보여줍니다.
아래 예제는 Solend와 Serum 프로그램이 모두 소유한 계정의 업데이트를 구독합니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {
    slots: {},
  },
  accounts: {
    programs: {
      owner: [
        'So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo',
        '9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin',
      ],
    },
  },
  transactions: {},
  blocks: {},
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}
각 프로그램에 개별 라벨을 할당하는 것을 선호한다면 다음 방식을 사용하세요.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {
    slots: {},
  },
  accounts: {
    solend: {
      owner: ['So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo'],
    },
    serum: {
      owner: ['9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin'],
    },
  },
  transactions: {},
  blocks: {},
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}

모든 finalized된 비투표 및 실패하지 않은 트랜잭션 구독

이 예제는 투표 트랜잭션과 실패한 트랜잭션을 제외하고 Finalized commitment 레벨의 모든 트랜잭션을 구독하는 방법을 보여줍니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {
    slots: {},
  },
  accounts: {},
  transactions: {
    alltxs: {
      vote: false,
      failed: false,
    },
  },
  blocks: {},
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.FINALIZED,
}
  • vote: false는 투표 트랜잭션을 제외합니다.
  • failed: false는 실패한 트랜잭션을 제외합니다.
  • 필드를 비워두면 모든 트랜잭션을 가져옵니다.
  • 여러 필드를 지정하면 AND 조건으로 동작합니다.

계정을 언급하는 비투표 트랜잭션 구독

이 예제는 특정 계정이 관련된 트랜잭션을 구독하되 투표 트랜잭션은 제외하는 방법을 보여줍니다.
아래 예제는 Serum 프로그램과 연관된 계정을 언급하는 비투표 트랜잭션을 구독합니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {
    slots: {},
  },
  accounts: {},
  transactions: {
    serum: {
      vote: false,
      accountInclude: ['9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin'],
    },
  },
  blocks: {},
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}

계정을 제외한 트랜잭션 구독

이 예제는 특정 계정이 관련된 트랜잭션을 제외하고 구독하는 방법을 보여줍니다.
아래 예제는 Serum 및 Tokenkeg 프로그램이 소유한 모든 계정을 제외한 트랜잭션을 가져옵니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {
    slots: {},
  },
  accounts: {},
  transactions: {
    serum: {
      accountExclude: [
        '9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin',
        'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
      ],
    },
  },
  blocks: {},
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}

계정을 언급하면서 특정 계정을 제외하는 트랜잭션 구독

이 예제는 특정 계정이 관련된 트랜잭션을 구독하면서 다른 지정된 계정은 명시적으로 제외하는 방법을 보여줍니다.
아래 예제는 Serum의 계정을 언급하는 트랜잭션을 구독하되 지정된 계정은 제외합니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {
    slots: {},
  },
  accounts: {},
  transactions: {
    serum: {
      accountInclude: ['9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin'],
      accountExclude: ['9wFFyRfZBsuAha4YcuxcXLKwMxJR43S7fPfQLusDBzvT'],
    },
  },
  blocks: {},
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}

트랜잭션 서명 구독

이 예제는 특정 트랜잭션 서명이 Confirmed 또는 Finalized 상태에 도달할 때까지 해당 서명의 실시간 업데이트를 구독하는 방법을 보여줍니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {},
  accounts: {},
  transactions: {
    sign: {
      signature:
        '5rp2hL9b6kexex11Mugfs3vfU9GhieKruj4CkFFSnu52WLxiGn4VcLLwsB62XURhMmT1j4CZiXT6FFtYbXsLq2Zs',
    },
  },
  blocks: {},
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}

slot 구독

이 예제는 들어오는 slot 알림을 구독하는 방법을 보여줍니다. 사용자 정의 태그 이름을 할당하는 것 외에 추가 세부 정보는 필요하지 않습니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {
    incoming_slots: {},
  },
  accounts: {},
  transactions: {},
  blocks: {},
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}

블록 구독

생성된 모든 블록의 실시간 업데이트를 구독합니다. 기본적으로 블록 내의 모든 트랜잭션을 가져옵니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {},
  accounts: {},
  transactions: {},
  blocks: {
    blocks: {},
  },
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}

트랜잭션을 제외하고 업데이트된 계정 정보만 가져오려면:

typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {},
  accounts: {},
  transactions: {},
  blocks: {
    blocks: {
      includeTransactions: false,
      includeAccounts: true,
    },
  },
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}

특정 계정이 관련된 트랜잭션/계정만 가져오려면:

typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {},
  accounts: {},
  transactions: {},
  blocks: {
    blocks: {
      accountInclude: ['So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo'],
    },
  },
  blocksMeta: {},
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}

블록 메타데이터 구독

블록이 처리될 때 블록 메타데이터 알림만 구독합니다. 상세한 트랜잭션 데이터는 포함되지 않습니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

const request = {
  slots: {},
  accounts: {},
  transactions: {},
  blocks: {},
  blocksMeta: {
    blockmetadata: {},
  },
  accountsDataSlice: [],
  commitment: CommitmentLevel.PROCESSED,
}

commitment 레벨 관리

Solana Geyser gRPC 스트림은 기본적으로 Processed commitment 레벨을 사용합니다.
ConfirmedFinalized 같은 더 높은 commitment 레벨을 지정할 수 있습니다. 이 경우 Geyser는 데이터를 버퍼링했다가 지정된 commitment 레벨에 도달하면 알림을 전송합니다.
최대 성능을 위해서는 commitment 레벨 관리를 클라이언트 측에서 처리하는 것이 좋습니다.
commitment 레벨을 지정하는 방법은 다음과 같습니다.
typescript
import { CommitmentLevel } from '@validators-dao/solana-stream-sdk'

enum CommitmentLevel {
  PROCESSED = 0,
  CONFIRMED = 1,
  FINALIZED = 2,
}
  • PROCESSED: 처리 직후의 즉각적인 데이터입니다. 빠르게 가져올 수 있지만 아직 확인되지 않은 상태입니다.
  • CONFIRMED: 클러스터에 의해 확인된 데이터로, 더 높은 확실성을 제공합니다.
  • FINALIZED: 재구성(reorganization) 위험이 없는 완전히 finalized된 데이터입니다.

Processed에서 작업하는 이점

Processed commitment 레벨의 주요 장점은 트랜잭션을 즉시 가져올 수 있어 클라이언트 측에서 신속하게 처리할 수 있다는 점입니다. 이후 클라이언트는 ConfirmedFinalized로의 전환을 감지할 수 있어 빠른 응답성을 제공합니다.

ConfirmedFinalized 관리 방법

ConfirmedFinalized 레벨을 사용할 때는 이벤트(트랜잭션 또는 계정 업데이트)를 slot 단위로 버퍼링해야 합니다.
slot별로 이벤트를 버퍼링하고 slot 알림을 구독한 뒤, 특정 slot이 원하는 commitment(Confirmed 또는 Finalized)에 도달하면 버퍼에서 이벤트를 해제하세요.
이벤트는 slot이 ConfirmedFinalized에 도달하기 전에 먼저 수신됩니다.

Finalized의 특별한 점

Solana Geyser 사양으로 인해 모든 slot이 명시적인 finalized 알림을 받는 것은 아닙니다. 따라서 어떤 slot에 대한 finalized 알림을 받으면, 명시적인 알림을 받지 않았더라도 모든 상위(ancestor) slot을 finalized된 것으로 취급해야 합니다.
구체적으로, finalized 알림을 받으면 모든 상위 slot을 소급하여 finalized된 것으로 취급하세요.