UNPKG

@keplr-ewallet/ewallet-sdk-cosmos

Version:
320 lines 11.3 kB
import { Buffer } from "buffer"; var WsReadyState; (function (WsReadyState) { WsReadyState[WsReadyState["CONNECTING"] = 0] = "CONNECTING"; WsReadyState[WsReadyState["OPEN"] = 1] = "OPEN"; WsReadyState[WsReadyState["CLOSING"] = 2] = "CLOSING"; WsReadyState[WsReadyState["CLOSED"] = 3] = "CLOSED"; WsReadyState[WsReadyState["NONE"] = 4] = "NONE"; })(WsReadyState || (WsReadyState = {})); export class TendermintTxTracer { constructor(url, wsEndpoint, options = {}) { this.url = url; this.wsEndpoint = wsEndpoint; this.options = options; this.newBlockSubscribes = []; this.txSubscribes = new Map(); this.pendingQueries = new Map(); this.listeners = {}; this.onOpen = (e) => { if (this.newBlockSubscribes.length > 0) { this.sendSubscribeBlockRpc(); } for (const [id, tx] of this.txSubscribes) { this.sendSubscribeTxRpc(id, tx.params); } for (const [id, query] of this.pendingQueries) { this.sendQueryRpc(id, query.method, query.params); } for (const listener of this.listeners.open ?? []) { listener(e); } }; this.onMessage = (e) => { for (const listener of this.listeners.message ?? []) { listener(e); } if (e.data) { try { const obj = JSON.parse(e.data); if (obj?.id) { if (this.pendingQueries.has(obj.id)) { if (obj.error) { this.pendingQueries .get(obj.id) .rejector(new Error(obj.error.data || obj.error.message)); } else { if (obj.result?.tx_result) { this.pendingQueries.get(obj.id).resolver(obj.result.tx_result); } else { this.pendingQueries.get(obj.id).resolver(obj.result); } } this.pendingQueries.delete(obj.id); } } if (obj?.result?.data?.type === "tendermint/event/NewBlock") { for (const handler of this.newBlockSubscribes) { handler.handler(obj.result.data.value); } } if (obj?.result?.data?.type === "tendermint/event/Tx") { if (obj?.id) { if (this.txSubscribes.has(obj.id)) { if (obj.error) { this.txSubscribes .get(obj.id) .rejector(new Error(obj.error.data || obj.error.message)); } else { this.txSubscribes .get(obj.id) .resolver(obj.result.data.value.TxResult.result); } this.txSubscribes.delete(obj.id); } } } } catch { console.log("Tendermint websocket jsonrpc response is not JSON"); } } }; this.onClose = (e) => { for (const listener of this.listeners.close ?? []) { listener(e); } }; this.onError = (e) => { for (const listener of this.listeners.error ?? []) { listener(e); } this.close(); }; this.ws = this.options.wsObject ? new this.options.wsObject(this.getWsEndpoint()) : new WebSocket(this.getWsEndpoint()); this.ws.onopen = this.onOpen; this.ws.onmessage = this.onMessage; this.ws.onclose = this.onClose; this.ws.onerror = this.onError; } getWsEndpoint() { let url = this.url; if (url.startsWith("http")) { url = url.replace("http", "ws"); } if (!url.endsWith(this.wsEndpoint)) { const wsEndpoint = this.wsEndpoint.startsWith("/") ? this.wsEndpoint : "/" + this.wsEndpoint; url = url.endsWith("/") ? url + wsEndpoint.slice(1) : url + wsEndpoint; } return url; } close() { this.ws.close(); } get readyState() { switch (this.ws.readyState) { case 0: return WsReadyState.CONNECTING; case 1: return WsReadyState.OPEN; case 2: return WsReadyState.CLOSING; case 3: return WsReadyState.CLOSED; default: return WsReadyState.NONE; } } addEventListener(type, listener) { if (!this.listeners[type]) { this.listeners[type] = []; } this.listeners[type].push(listener); } subscribeBlock(handler) { this.newBlockSubscribes.push({ handler, }); if (this.newBlockSubscribes.length === 1) { this.sendSubscribeBlockRpc(); } return () => { this.newBlockSubscribes = this.newBlockSubscribes.filter((s) => s.handler !== handler); }; } sendSubscribeBlockRpc() { if (this.readyState === WsReadyState.OPEN) { this.ws.send(JSON.stringify({ jsonrpc: "2.0", method: "subscribe", params: ["tm.event='NewBlock'"], id: 1, })); } } traceTx(query) { let resolved = false; return new Promise((resolve) => { this.queryTx(query) .then((result) => { if (query instanceof Uint8Array) { resolve(result); return; } if (result?.total_count !== "0") { resolve(result); return; } }) .catch(() => { // noop }); (async () => { while (true) { if (resolved || this.readyState === WsReadyState.CLOSED || this.readyState === WsReadyState.CLOSING) { break; } await new Promise((resolve) => setTimeout(resolve, 10000)); this.queryTx(query) .then((result) => { if (query instanceof Uint8Array) { resolve(result); return; } if (result?.total_count !== "0") { resolve(result); return; } }) .catch(() => { // noop }); } })(); this.subscribeTx(query).then(resolve); }).then((tx) => { resolved = true; return new Promise((resolve) => { setTimeout(() => resolve(tx), 100); }); }); } subscribeTx(query) { if (query instanceof Uint8Array) { const id = this.createRandomId(); const params = { query: `tm.event='Tx' AND tx.hash='${Buffer.from(query) .toString("hex") .toUpperCase()}'`, }; return new Promise((resolve, reject) => { this.txSubscribes.set(id, { params, resolver: resolve, rejector: reject, }); this.sendSubscribeTxRpc(id, params); }); } else { const id = this.createRandomId(); const params = { query: `tm.event='Tx' AND ` + Object.keys(query) .map((key) => { return { key, value: query[key], }; }) .map((obj) => { return `${obj.key}=${typeof obj.value === "string" ? `'${obj.value}'` : obj.value}`; }) .join(" AND "), page: "1", per_page: "1", order_by: "asc", }; return new Promise((resolve, reject) => { this.txSubscribes.set(id, { params, resolver: resolve, rejector: reject, }); this.sendSubscribeTxRpc(id, params); }); } } sendSubscribeTxRpc(id, params) { if (this.readyState === WsReadyState.OPEN) { this.ws.send(JSON.stringify({ jsonrpc: "2.0", method: "subscribe", params: params, id, })); } } queryTx(query) { if (query instanceof Uint8Array) { return this.query("tx", { hash: Buffer.from(query).toString("base64"), prove: false, }); } else { const params = { query: Object.keys(query) .map((key) => { return { key, value: query[key], }; }) .map((obj) => { return `${obj.key}=${typeof obj.value === "string" ? `'${obj.value}'` : obj.value}`; }) .join(" AND "), page: "1", per_page: "1", order_by: "asc", }; return this.query("tx_search", params); } } query(method, params) { const id = this.createRandomId(); return new Promise((resolve, reject) => { this.pendingQueries.set(id, { method, params, resolver: resolve, rejector: reject, }); this.sendQueryRpc(id, method, params); }); } sendQueryRpc(id, method, params) { if (this.readyState === WsReadyState.OPEN) { this.ws.send(JSON.stringify({ jsonrpc: "2.0", method, params, id, })); } } createRandomId() { return Math.floor(Math.random() * 1000000); } } //# sourceMappingURL=tendermint_tx_tracer.js.map