UNPKG

@ton3/liteclient

Version:
296 lines (253 loc) 8.15 kB
import EventEmitter from 'events' import { ADNLClient } from 'adnl' import Debug from 'debug' import { StreamReader, StreamWriter, uintToBytes } from '../../tl/stream' import * as dataTypes from '../../dataTypes' import { AccountId, AccountState, AllShardsInfo, BlockData, BlockHeader, BlockTransactions, MasterchainInfo, RunMethodResult, SendMsgStatus, TransactionId3, TransactionList, TransactionInfo, Version, } from '../../dataTypes/liteServer'; import * as functions from '../../functions'; import { BlockIdExt } from '../../dataTypes/tonNode'; import { LookupBlockInput } from '../../functions/liteServer'; import { Utils } from 'ton3-core'; export interface WaitOptions { seqno: number timeout?: number } const debug = Debug('tonkite:lite-api:client'); let counter = 0; const nextQueryId = () => { const buffer = Utils.Helpers.hexToBytes('0000000095d6fecb497dfd0aa5f031e7d412986b5ce720496db512052e8f2d10') buffer.set(uintToBytes(counter++, 4).reverse(), 0) return buffer } const createADNLQuery = (queryId: Uint8Array, query: Uint8Array) => { const queryPadding = 4 - (((query.length + 1) % 4) || 4); const buffer = new Uint8Array(4 + 32 + 1 + query.length + queryPadding); // crc32('adnl.message.query query_id:int256 query:bytes = adnl.Message') // Write 4 bytes number in LE order buffer.set(uintToBytes(0xb48bf97a, 4).reverse(), 0) // query_id:int256 buffer.set(queryId, 4) // query:bytes buffer.set([ query.length ], 4 + 32); buffer.set(query, 4 + 32 + 1); return buffer; }; class LiteApiError extends Error { constructor(message: string, readonly code: number) { super(message); } } export class LiteApi { #events = new EventEmitter(); constructor(private readonly adnlClient: ADNLClient) { adnlClient.on('data', (data: Buffer) => { const dataReader = new StreamReader(data); const adnlAnswer = dataTypes.adnl.answerMessage.read(dataReader); const [tag, answer] = this.parseLiteServerAnswer(adnlAnswer.answer); this.#events.emit(Utils.Helpers.bytesToHex(adnlAnswer.queryId), { tag, answer, }); }); } /** * Parses an answer with a parser (chosen according to a tag) * @param dataReader * @private */ private parseLiteServerAnswer(dataReader: StreamReader) { const tag = dataReader.readInt32LE(); const codecs = Object.values(dataTypes.liteServer) as any[]; const codec = codecs.find((codec) => codec.tag === tag); if (!codec) { throw new Error(`Data type 0x${tag.toString(16).padStart(8, '0')} is not supported.`); } return [tag, codec.read(dataReader)]; } protected query<T>(dataWriterFn: (dataWriter: StreamWriter) => void, wait?: WaitOptions): Promise<T> { const dataWriter = new StreamWriter(); const queryWriter = new StreamWriter(); if (wait) { const { seqno, timeout = 10000 } = wait dataTypes.liteServer.waitMasterchainSeqno.write(dataWriter, seqno, timeout); } dataWriterFn(dataWriter); dataTypes.liteServer.query.write(queryWriter, dataWriter.buffer); const queryId = nextQueryId(); this.adnlClient.write(createADNLQuery(queryId, queryWriter.buffer)); return new Promise<T>((resolve, reject) => { this.#events.once(Utils.Helpers.bytesToHex(queryId), ({ tag, answer }) => { if (tag === dataTypes.liteServer.error.tag) { return reject(new LiteApiError(answer.message, answer.code)); } resolve(answer); }); }); } getMasterchainInfo(wait?: WaitOptions) { debug('query getMasterchainInfo()'); return this.query<MasterchainInfo>((dataWriter) => { functions.liteServer.getMasterchainInfo(dataWriter); }, wait); } getTime(wait?: WaitOptions) { debug('query getTime()'); return this.query<string>((dataWriter) => { functions.liteServer.getTime(dataWriter); }, wait); } getVersion(wait?: WaitOptions) { debug('query getVersion()'); return this.query<Version>((dataWriter) => { functions.liteServer.getVersion(dataWriter); }, wait); } getBlock(blockId: BlockIdExt, wait?: WaitOptions) { debug('query getBlock()'); return this.query<BlockData>((dataWriter) => { functions.liteServer.getBlock(dataWriter, blockId); }, wait); } getAllShardsInfo(blockId: BlockIdExt, wait?: WaitOptions) { debug('query getAllShardsInfo()'); return this.query<AllShardsInfo>((dataWriter) => { functions.liteServer.getAllShardsInfo(dataWriter, blockId); }, wait); } getState(blockId: BlockIdExt, wait?: WaitOptions) { debug('query getState()'); return this.query<unknown>((dataWriter) => { functions.liteServer.getState(dataWriter, blockId); }, wait); } getBlockHeader( data: { blockId: BlockIdExt, mode: number }, wait?: WaitOptions ) { debug('query getBlockHeader()'); return this.query<BlockHeader>((dataWriter) => { functions.liteServer.getBlockHeader(dataWriter, data.blockId, data.mode); }, wait); } lookupBlock(input: LookupBlockInput, wait?: WaitOptions) { debug('query lookupBlock()'); return this.query<BlockHeader>((dataWriter) => { functions.liteServer.lookupBlock(dataWriter, input); }, wait); } getAccountState( data: { blockId: BlockIdExt, account: AccountId }, wait?: WaitOptions ) { debug( 'query getAccountState(%d, %d, %s)', data.blockId.workchain, data.blockId.seqno, `${data.account.workchain}:${Utils.Helpers.bytesToHex(data.account.id)}`, ); return this.query<AccountState>((dataWriter) => { functions.liteServer.getAccountState(dataWriter, data.blockId, data.account); }, wait); } listBlockTransactions( data: { blockId: BlockIdExt, count: number, after?: TransactionId3 | null, options?: { reverseOrder?: boolean; wantProof?: boolean; }, }, wait?: WaitOptions ) { return this.query<BlockTransactions>((dataWriter) => { debug('query listBlockTransactions()'); functions.liteServer.listBlockTransactions( dataWriter, data.blockId, data.count || 20, data.after || null, data?.options?.reverseOrder || false, data?.options?.wantProof || false, ); }, wait); } getTransactions( data: { account: AccountId, lt: bigint, hash: Uint8Array, count?: number }, wait?: WaitOptions ) { debug( 'query getTransactions(%s, %s, %s)', `${data.account.workchain}:${Utils.Helpers.bytesToHex(data.account.id)}`, data.lt.toString(10), Utils.Helpers.bytesToHex(data.hash), ); return this.query<TransactionList>((dataWriter) => { functions.liteServer.getTransactions(dataWriter, data.count || 20, data.account, data.lt, data.hash); }, wait); } getOneTransaction( data: { blockId: BlockIdExt, accountId: AccountId, lt: bigint }, wait?: WaitOptions ) { debug( 'query getOneTransaction(%s, %s, %s)', `${data.blockId.workchain},${data.blockId.shard.toString(16)},${data.blockId.seqno}`, `${data.accountId.workchain}:${Utils.Helpers.bytesToHex(data.accountId.id)}`, data.lt.toString(10), ); return this.query<TransactionInfo>((dataWriter) => { functions.liteServer.getOneTransaction(dataWriter, data.blockId, data.accountId, data.lt); }, wait); } runSmcMethod( data: { blockId: BlockIdExt, accountId: AccountId, methodId: bigint params: Uint8Array }, wait?: WaitOptions ) { debug('query runSmcMethod()'); return this.query<RunMethodResult>((dataWriter) => { functions.liteServer.runSmcMethod(dataWriter, data.blockId, data.accountId, data.methodId, data.params); }, wait); } sendMessage(body: Uint8Array, wait?: WaitOptions) { debug('query sendMessage()'); return this.query<SendMsgStatus>((dataWriter) => { functions.liteServer.sendMessage(dataWriter, body); }, wait); } }