이전에는 환위험 관리 시스템을 구성했고, 이번 기사에서는 거래소 지갑이 솔라나 체인에 접근할 수 있도록 했습니다.솔라나의 계정 모델, 로그 저장 및 확인 메커니즘은 이더 체인과는 매우 다르며 이더 공식을 따르다 보면 여전히 구덩이를 밟기 쉽습니다. 여기에서는 솔라나 기록에 대한 전반적인 아이디어를 정리해 보겠습니다.
솔라나 고유의 이해
솔라나 계정 모델
솔라나는 프로그램과 데이터가 분리된 모델을 사용합니다. 모델에서는 프로그램이 공유되고 프로그램 데이터는 PDA(프로그램 파생 주소) 계정을 통해 별도로 저장됩니다. 프로그램이 공유되므로 토큰을 구분하기 위해 토큰 민트가 필요합니다. 토큰 민트 계정에는 토큰의 글로벌 메타데이터가 저장되며, 예를 들어 mint_authority,,총 공급량, 숫자,등.
각 토큰에는 고유한 Mint Account 주소를 식별자로 사용합니다(예: 솔라나 메인넷에서 USD 코인(USDC)의 민트 주소는 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v입니다.
솔라나의 토큰 프로그램은 SPL 토큰과 SPL 토큰-2022용 두 가지로, 각 SPL 토큰에는 사용자의 잔액을 보유하기 위한 별도의 ATA(연관 토큰 계정)가 있습니다. 토큰이 전송되면 실제로 자체 절차를 호출하여 ATA 계정 간에 토큰을 전송합니다.
솔라나 로그 제한
이더에서 토큰 전송은 과거 전송 로그를 파싱하여 얻지만, 솔라나의 실행 로그는 기본적으로 영구적으로 보관되지 않습니다. 솔라나의 로그는 원장의 상태가 아니며(로그에 대한 블룸 필터가 없음), 실행 중에 출력이 잘릴 수 있습니다.
따라서 "로그를 스캔"하여 재충전 조정을 수행할 수 없고 대신 getBlock 또는 getSignaturesForAddress 또는 getSignaturesForAddress를 사용해야 합니다. 코드> 를 입력하여 명령을 구문 분석합니다.
솔라나 승인 및 재구성
솔라나는 블록에서 벗어나는 데 400ms가 걸리며 32번의 승인(약 12초) 후에 최종 승인되므로 실시간 요구 사항이 너무 높지 않은 경우 유용하게 사용할 수 있습니다. 실시간 요구 사항이 높지 않다면 최종 확정된 블록만 신뢰하는 간단한 방법이 있습니다.
더 높은 실시간 성능을 원한다면 발생 가능성은 낮지만 블록 재조립 가능성을 고려해야 합니다. 그러나 솔라나 합의는 체인 구조를 형성하기 위해 부모 블록해시에 의존하지 않으며, 이더의 경우처럼 부모 블록해시가 데이터베이스의 블록해시와 다르다고 해서 포크를 결정할 수 없습니다. 그렇다면 블록이 재구성되었는지 알 수 있는 가장 좋은 방법은 무엇일까요?
블록을 로컬에서 스윕할 때 슬롯의 <코드>블록해시를 기록하고, 동일한 슬롯의 <코드>블록해시가 변경되면 롤백이 발생한 것입니다.
솔라나의 차이점을 이해했으면, 다음 단계는 데이터베이스의 첫 번째 모습을 구현하여 수정하는 방법입니다.
데이터베이스 테이블 디자인
데이터베이스 테이블은 데이터베이스에서 사용하도록 설계됩니다. 왼쪽;">솔라나에는 두 가지 유형의 토큰이 있으므로, tokens 테이블에 token_type을 추가하여 spl-token과 spl-token-2022를 구분해야 합니다. code>spl-token-2022
솔라나 주소도 이더와 동일하지는 않지만 BIP32와 BIP44에서 파생할 수 있으며, 파생 경로만 다르므로 원래 wallets만 사용하면 됩니다. code>wallets 테이블을 사용해야 하지만, ATA 주소 매핑을 지원하기 위해 Solana는 블록 추적을 스윕하여 다음 세 개의 테이블을 추가해야 합니다.
테이블 이름 | 키 필드 | 설명 |
thead>solana_slots
| 슬롯 , block_hash, status, parent_slot | 포크 감지 및 롤백 트리거를 용이하게 하는 중복 슬롯 정보 |
solana_transactions
| < td>tx_hash
, slot, to_addr, token_mint, 금액, 유형충전/출금 등 스토어 거래 내역,< code>tx_hash 고유, 이중 서명 추적에 사용 |
solana_token_accounts
| wallet_id,wallet_address,token_mint,ata_mint, solana_token_accounts | ata_address사용자 ATA 매핑을 기록하려면 스캔 모듈을 누르고 ata_address를 누를 수 있습니다; 내부 계정 카운터체크 |
위치:
solana_slots는 확정/완료/스킵를 기록하며, 스캐너는 상태에 따라 라이브러리를 삭제할지 롤백할지 결정합니다.
solana_transactions는 가장 작은 단위인 램프포트 또는 램프가 있는 토큰 또는 램프가 있는 토큰으로 보관됩니다. 입출금 및 기타 비즈니스 시나리오를 구분하기 위한 유형 필드에 민감한 기록은 여전히 바람 제어에 의해 서명되어야 합니다.
solana_token_accounts 와 wallets/users외래키를 설정합니다. ATA의 고유성을 보장하고 스캔 로직의 핵심 인덱스가 되는 외래 키 관계(wallet_address + token_mint 고유성)를 설정합니다.
자세한 테이블 정의는 db_gateway/database.md에서 확인할 수 있습니다
사용자 충전 처리
사용자 충전을 처리하려면 Solana 체인에서 데이터를 지속적으로 스캔해야 하며, 일반적으로 두 가지 방법으로 수행됩니다.
서명 스캔: getSignaturesForAddress()
< li>블록 스윕: getBlock()
방법 1: 주소의 서명을 스윕하여, < code>getSignaturesForAddress(address, { before, until, limit })를 호출하여 관심 있는 주소를 파라미터로 전달하며, 이는 사용자를 위해 생성한 ATA 주소이거나 프로그램ID일 수 있습니다(spl-token의 전송 명령 호출에는 민트 주소가 포함되지 않음), 그리고 매개변수가 지속적으로 증분 서명을 가져올 때까지 이전을 제어한 다음 getTransaction(서명)으로 트랜잭션 정보 데이터를 가져옵니다.
이 방법은 데이터의 양이나 계정 번호가 적은 경우에 적합할 수 있으며, 계정 수가 매우 많은 경우에는 여기서 사용하는 것과 같은 스윕 블록을 사용하는 것이 더 적합합니다.
방법 2: 스윕 블록 방법은 지속적으로 최신 슬롯을 가져오고, getBlock(slot)를 호출하여 전체 거래 내역을 가져오는 방식입니다, 서명과 계좌를 입력한 다음 명령어와 계좌를 기준으로 필요한 데이터를 필터링하여 가져옵니다.
주: Solana의 대규모 트랜잭션 흐름과 높은 TPS로 인해 운영 환경에서는 파싱 및 필터링 속도가 Solana의 블록 속도를 따라가지 못할 수 있으며, 이 경우 메시지 큐를 사용해야 합니다. 큐에 모든 토큰 전송을 필터링하고, 와 같은 메시지 큐에 푸시된 가능한 "잠재적 재충전 이벤트"를 필터링한 다음, 후속 큐 소비자 모듈에서 정확한 필터링과 데이터베이스에 쓰기, 필터링 효율을 높이기 위해 일부 핫 데이터는 Redis에 저장해야 큐 스택을 피할 수 있습니다. 사용자 주소가 매우 큰 경우 ATA 주소별로 슬라이싱할 수 있으며, 여러 소비자가 서로 다른 슬라이스를 수신하여 효율성을 향상시킬 수 있습니다.
블록 스위핑을 직접 수행하지 않으려는 경우, 또 다른 방법은 웹훅, 계정 계정 수신, 고차 필터링 지원과 같은 추가 인덱서 서비스를 제공하는 타사 RPC 제공업체를 사용하여 대량의 데이터를 파싱하는 부담을 덜어주는 것입니다.
블록 스캔 프로세스
블록 스캔 모듈 아래의 scan/solana-scan 모듈에서 사용할 수 있는 방법 2를 사용했습니다. )blockScanner.ts 및 txParser.ts의 주요 프로세스는 다음과 같습니다.
1. 초기 동기화 단계, 기록 블록의 보충 (scan/solana-scan ). strong>(performInitialSync)
마지막 스캔한 슬롯부터 시작하여 최신 슬롯까지 하나씩 슬롯 스캔
100개 슬롯마다 새 슬롯이 생성되었는지 확인하고 대상을 동적으로 업데이트
confirmed 약속을 사용하여 블록을 실시간으로 가져오기
100개 슬롯마다 새 슬롯이 생성되었는지 확인함
블록을 실시간으로 가져오기. 블록을 실시간으로 확인
2. 스캐닝 단계 (scanNewSlots)
새 슬롯 생성 지속 확인
최근 확인된 슬롯 재검증(롤백 감지)
< strong>3. 블록 구문 분석 (txParser.parseBlock)
콜 getBlock(slot, {. commitment: "confirmed", encoding: "jsonParsed" })
각 트랜잭션의 transaction을 반복합니다. message.instructions 및 meta.innerInstructions
성공한 트랜잭션만 처리합니다( tx.meta.err === null)
4. 명령 구문 분석 (txParser.parseInstruction)
SOL 전송: match 시스템 프로그램(11111...) 전송 유형, 주소가 모니터링되는 주소 목록에 대상자 주소가 있는지 여부와 직접 일치합니다
SPL 토큰 전송: 토큰 프로그램 또는 Token-2022 프로그램과 일치합니다. <코드>전송코드>/<코드>전송확인코드>, <코드>목적지코드>가 ATA 주소와 일치하면 데이터베이스를 통해 지갑 주소와 토큰민트 주소에 매핑됩니다.
롤백 특정 처리:
프로그램은 finalisedSlot를 계속 가져와서 슬롯이 finalisedSlot 이하이면 finalizedSlot>으로 표시합니다. finalizedSlot은 finalized로 표시되며, 아직 확정 상태인 블록의 경우 블록해시를 변경하여 롤백 여부를 결정합니다.
핵심 코드 샘플은 다음과 같습니다:
// blockScanner.ts - 단일 슬롯 스캔
async . scanSingleSlot(slot: number) {
const block = await solanaClient.getBlock(slot);
if (!block) {
  await insertSlot({ slot, status: 'skipped' });
return;
}
const& nbsp;finalisedSlot = await getCachedFinalizedSlot();
const status = slot <= finalizedSlot ? 'finalised' : 'confirmed';
await processBlock(slot, block, status);
}
// txParser.ts - 전송 명령어 파싱
for . (const tx of block.transactions) {
if (tx.meta?.err) continue; // 실패한 트랜잭션 건너뛰기
const . instructions = [
. .tx.transaction.message.instructions,
... (tx.meta.innerInstructions ???? []).flatMap(i => i.instructions)
];
for (const ix of instructions) {
// SOL 전송
... br> if (ix.programmeId === SYSTEM_PROGRAM_ID && ix.parsed?.type === 'transfer') {
if (monitoredAddresses.has(ix.parsed.info.destination)) {
// ...
}
}
// 토큰 전송
if (ix.programmeId === TOKEN_PROGRAM _ID || ix.programmeId === TOKEN_2022_PROGRAM_ID) {
if (ix.parsed?.type === 'transfer' || ix. .parsed?.type === 'transferChecked')) {
const ataAddress = ix.parsed.info.destination // ATA 주소
const walletAddress = ataToWalletMap.get(ataAddress); // 지갑 주소로 지도
const ataAddress = ix.parsed.info. if (walletAddress && monitoredAddresses.has(walletAddress)) {
 . nbsp; // ...
}
}
}
}
}
충전 거래를 스캔한 후 DB 게이트웨이 + 윈드 컨트롤의 이중 서명 보안과 함께 검증을 거쳐 자금 흐름 테이블 크레딧에 데이터가 기록됩니다.
출금
솔라나의 출금 프로세스는 EVM 체인과 유사하지만 거래 구성에 차이가 있습니다.
.
솔라나에는 일반 SPL 토큰과 SPL 토큰 2022의 두 가지 토큰이 있으며, 두 토큰의 프로그램 ID가 다르기 때문에 트랜잭션 명령어를 구성할 때 이를 구분해야 합니다. (현재 SPL-Token 2022는 거의 없거나 토큰 2022를 지원하지 않도록 선택할 수 있습니다)
솔라나의 트랜잭션은 서명(ed25519 서명 집합)과 메시지(헤더, 헤더, 서명 집합)의 두 부분으로 구성됩니다. 그리고 메시지(헤더, 계정키, 최근블록해시, 지침 포함) 메시지 내용은 해시되고 서명되어 서명에 배치됩니다. 솔라나 트랜잭션에는 논스가 없으며, 대신 최근블록해시가 사용됩니다. 솔라나 트랜잭션에는 논스가 없지만 최근 블록해시를 사용하여 트랜잭션의 유효 기간을 제한하고, 최근 블록해시는 150블록(약 1분) 동안만 유효하므로 트랜잭션이 시작될 때마다 최근 블록해시는 실시간으로 체인에서 최신 최근 블록해시를 가져와야 하고, 출금 트랜잭션을 수동으로 감사해야 하는 경우 트랜잭션 구조에 다시 서명할 수 있을 만큼 최근 블록해시를 다시 가져와야 합니다. 트랜잭션 구조가 서명을 다시 요청합니다.
출금 과정
<그림>

실제로는 리스크 확인 후 트랜잭션 블록해시 가져오기를 여기에 넣는 것이 더 나을 것입니다
서명자 모듈이 트랜잭션에 서명하는 핵심 코드는 다음과 같습니다:
거래 유형에 따라 다른 명령어를 작성합니다:
// SOL 이체 지침
const instruction = getTransferSolInstruction({
source: hotWalletSigner,
destination: solanaAddress(to),
amount. BigInt(amount)
});
// 2. 토큰 전송 인스트럭션 생성
const instruction = getTransferInstruction({
source: sourceAta,
destination. destAta,
authority: hotWalletSigner,
amount: BigInt(amount)
});
거래 메시지 구성 및 서명하기:
<>< pre>
// 다음을 사용하여 트랜잭션 빌드 @solana/kit
const transactionMessage = pipe(
createTransactionMessage( { version: 0 }),
tx => setTransactionMessageFeePayerSigner(hotWalletSigner, tx),
tx => setTransactionMessageLifetimeUsingBlockhash({
블록해시: 블록해시,
lastValidBlockHeight. BigInt(lastValidBlockHeight)
}, tx),
tx => appendTransactionMessageInstruction(instruction, tx)
);
// 시그니처 트랜잭션< br>const signedTx = await signTransactionMessageWithSigners(transactionMessage);
// 두 가지 인코딩 유형을 반환합니다:
// 1. Base64로 인코딩된 완전한 트랜잭션( 웹으로 전송용)
const signedTransaction = getBase64EncodedWireTransaction(signedTx);
< strong>월렛 모듈이 트랜잭션을 웹으로 전송
// @solana/web3.js를 사용하여 트랜잭션 전송
const solanaRpc = chainConfigManager.getSolanaRpc();
const txSignature = await solanaRpc.sendTransaction(
signedTransaction, // Base64 인코딩된 트랜잭션
...
);
출금 구현 코드 전문은 다음 위치에 있습니다:
Wallet 모듈: walletBusinessService.ts:405-754
서명자 모듈: solanaSigner.ts:29-122
테스트 스크립트: requestWithdrawOnSolana.ts
여기에서 구현해야 할 두 가지 최적화가 있습니다.
ATA 사전 확인: 돈을 인출하기 전에 목적지 주소의 ATA 계정이 생성되었는지 확인해야 하며, 그렇지 않으면 추가 비용으로 ATA를 생성해야 합니다
우선 수수료: 다음과 같은 경우 네트워크 혼잡을 computeUnitPrice로 설정하여 거래 우선순위를 높일 수 있습니다
요약
. 솔라나 체인에 대한 거래소의 접근은 전체 아키텍처 측면에서 변경되지 않으며, 핵심은 고유한 계정 모델, 거래 구조 및 합의 확인 메커니즘을 적용하는 것입니다.
충전 처리 시에는 토큰 전송 식별, 블록 재구성을 감지하기 위한 블록해시 변경 통합 모니터링, 거래 상태의 동적 업데이트(확정→확정) 등에 사용되는 ATA에서 지갑 주소로의 매핑 테이블을 미리 설정하고 유지합니다.
자금 출금 시에는 getLatestBlockhash()를 사용하여 트랜잭션 파라미터를 가져오고, Sol, SPL 토큰, Token-2022를 구분하여 다른 트랜잭션을 구성할 수 있습니다.