@eclesia/indexer-engine 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,16 @@
1
+ import { WithHeightAndUUID } from "../types";
2
+ export declare class EclesiaEmitter {
3
+ private emitter;
4
+ handled: Map<string, number>;
5
+ constructor();
6
+ emit<EType extends keyof (WithHeightAndUUID<EventMap>) & string>(eventName: EType, eventArg: WithHeightAndUUID<EventMap>[EType] & {
7
+ uuid?: string;
8
+ }): void;
9
+ on<TEventName extends keyof WithHeightAndUUID<EventMap> & string | "_unhandled">(eventName: TEventName, handler: TEventName extends "_unhandled" ? (eventArg: {
10
+ type: string;
11
+ event: unknown;
12
+ uuid: string;
13
+ }) => void : (eventArg: TEventName extends keyof WithHeightAndUUID<EventMap> ? WithHeightAndUUID<EventMap>[TEventName] : never) => void): void;
14
+ off<TEventName extends keyof WithHeightAndUUID<EventMap> & string>(eventName: TEventName, handler: (eventArg: WithHeightAndUUID<EventMap>[TEventName]) => void): void;
15
+ }
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/emitter/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAsB;IAE9B,OAAO,sBAA6B;;IAM3C,IAAI,CAAC,KAAK,SAAS,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,EAC7D,SAAS,EAAE,KAAK,EAChB,QAAQ,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;IAYlE,EAAE,CAAC,UAAU,SAAS,MAAM,iBAAiB,CAAC,QAAQ,CAAC,GAAG,MAAM,GAAG,YAAY,EAAE,SAAS,EAAE,UAAU,EACpG,OAAO,EAAE,UAAU,SAAS,YAAY,GACpC,CAAC,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAC1B,KAAK,EAAE,OAAO,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KAAE,KAAK,IAAI,GACxB,CAAC,QAAQ,EAAE,UAAU,SAAS,MAAM,iBAAiB,CAAC,QAAQ,CAAC,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,GAAG,KAAK,KAAK,IAAI;IAwBhI,GAAG,CAAC,UAAU,SAAS,MAAM,iBAAiB,CAAC,QAAQ,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,UAAU,EACtF,OAAO,EAAE,CAAC,QAAQ,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,KAAK,IAAI;CASvE"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.EclesiaEmitter = void 0;
7
+ const events_1 = __importDefault(require("events"));
8
+ class EclesiaEmitter {
9
+ constructor() {
10
+ this.emitter = new events_1.default();
11
+ this.handled = new Map();
12
+ this.emitter.setMaxListeners(0);
13
+ }
14
+ emit(eventName, eventArg) {
15
+ if (this.handled.has(eventName)) {
16
+ this.emitter.emit(eventName, eventArg);
17
+ }
18
+ else {
19
+ this.emitter.emit("_unhandled", {
20
+ type: eventName,
21
+ event: eventArg,
22
+ uuid: eventArg.uuid
23
+ });
24
+ }
25
+ }
26
+ on(eventName, handler) {
27
+ const count = this.handled.get(eventName);
28
+ if (count) {
29
+ this.handled.set(eventName, count + 1);
30
+ }
31
+ else {
32
+ this.handled.set(eventName, 1);
33
+ }
34
+ this.emitter.on(eventName, async (eventData) => {
35
+ try {
36
+ await handler(eventData);
37
+ if (eventName !== "uuid" && eventName !== "_unhandled" && eventData.uuid) {
38
+ this.emit("uuid", { status: true,
39
+ uuid: eventData.uuid });
40
+ }
41
+ }
42
+ catch (error) {
43
+ if (eventName !== "uuid" && eventName !== "_unhandled" && eventData.uuid) {
44
+ this.emit("uuid", { status: false,
45
+ error: error,
46
+ uuid: eventData.uuid });
47
+ }
48
+ }
49
+ });
50
+ }
51
+ off(eventName, handler) {
52
+ const count = this.handled.get(eventName);
53
+ if (count && count > 1) {
54
+ this.handled.set(eventName, count - 1);
55
+ }
56
+ else {
57
+ this.handled.delete(eventName);
58
+ }
59
+ this.emitter.off(eventName, handler);
60
+ }
61
+ }
62
+ exports.EclesiaEmitter = EclesiaEmitter;
@@ -0,0 +1,6 @@
1
+ export * from "./emitter";
2
+ export * from "./indexer";
3
+ export * from "./promise-queue";
4
+ export * as Types from "./types";
5
+ export * as Utils from "./utils";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.Utils = exports.Types = void 0;
40
+ __exportStar(require("./emitter"), exports);
41
+ __exportStar(require("./indexer"), exports);
42
+ __exportStar(require("./promise-queue"), exports);
43
+ exports.Types = __importStar(require("./types"));
44
+ exports.Utils = __importStar(require("./utils"));
@@ -0,0 +1,50 @@
1
+ import { CometClient } from "@cosmjs/tendermint-rpc";
2
+ import winston from "winston";
3
+ import { EclesiaEmitter } from "../emitter";
4
+ import { EcleciaIndexerConfig, EmitFunc, WithHeightAndUUID } from "../types";
5
+ export declare const defaultIndexerConfig: {
6
+ startHeight: number;
7
+ batchSize: number;
8
+ modules: never[];
9
+ getNextHeight: () => number;
10
+ logLevel: EcleciaIndexerConfig["logLevel"];
11
+ usePolling: boolean;
12
+ pollingInterval: number;
13
+ processGenesis: boolean;
14
+ minimal: boolean;
15
+ init: () => Promise<void>;
16
+ beginTransaction: () => Promise<void>;
17
+ endTransaction: (_status: boolean) => Promise<void>;
18
+ };
19
+ export declare class EcleciaIndexer extends EclesiaEmitter {
20
+ private config;
21
+ private fastify;
22
+ private blockQueue;
23
+ private latestHeight;
24
+ private heightToProcess;
25
+ private initialized;
26
+ private retryCount;
27
+ client: CometClient;
28
+ log: winston.Logger;
29
+ private debugTime;
30
+ private healthCheck;
31
+ private subscription;
32
+ constructor(config: EcleciaIndexerConfig);
33
+ private setStatus;
34
+ private blockListener;
35
+ private isMinimal;
36
+ connect(): Promise<boolean>;
37
+ private debugTimes;
38
+ start(): Promise<void>;
39
+ asyncEmit: EmitFunc<keyof WithHeightAndUUID<EventMap>>;
40
+ private processBlock;
41
+ private fetcher;
42
+ callABCI(path: string, data: Uint8Array, height?: number): Promise<Uint8Array<ArrayBufferLike>>;
43
+ private newBlockReceived;
44
+ private pollForBlock;
45
+ private readGenesis;
46
+ private setArrayReader;
47
+ private setValueReader;
48
+ private parseGenesis;
49
+ }
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuC,WAAW,EAA4B,MAAM,wBAAwB,CAAC;AAkBpH,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAc,oBAAoB,EAAE,QAAQ,EAAqB,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAI5G,eAAO,MAAM,oBAAoB;;;;;cAKX,oBAAoB,CAAC,UAAU,CAAC;;;;;;;8BAO1B,OAAO;CAClC,CAAC;AAEF,qBAAa,cAAe,SAAQ,cAAc;IAChD,OAAO,CAAC,MAAM,CAAuB;IAErC,OAAO,CAAC,OAAO,CAAkB;IAEjC,OAAO,CAAC,UAAU,CAAa;IAE/B,OAAO,CAAC,YAAY,CAAU;IAE9B,OAAO,CAAC,eAAe,CAAU;IAEjC,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,UAAU,CAAK;IAEhB,MAAM,EAAG,WAAW,CAAC;IAErB,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC;IAE3B,OAAO,CAAC,SAAS,CAAiC;IAElD,OAAO,CAAC,WAAW,CAEjB;IAEF,OAAO,CAAC,YAAY,CAA6D;gBAErE,MAAM,EAAE,oBAAoB;IAyDxC,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,aAAa,CAInB;IAEF,OAAO,CAAC,SAAS;IAQJ,OAAO;IAYpB,OAAO,CAAC,UAAU;IAUL,KAAK;IAyIX,SAAS,EAAE,QAAQ,CAAC,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAoC3D;YAEY,YAAY;YA6HZ,OAAO;IA2CR,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,MAAM;IAoBrE,OAAO,CAAC,gBAAgB;YAqCV,YAAY;IAY1B,OAAO,CAAC,WAAW;YAQL,cAAc;YAyBd,cAAc;YAuBd,YAAY;CAiD3B"}
@@ -0,0 +1,629 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.EcleciaIndexer = exports.defaultIndexerConfig = void 0;
7
+ const node_crypto_1 = require("node:crypto");
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const tendermint_rpc_1 = require("@cosmjs/tendermint-rpc");
10
+ const tendermint_rpc_2 = require("@cosmjs/tendermint-rpc");
11
+ const tx_1 = require("cosmjs-types/cosmos/authz/v1beta1/tx");
12
+ const query_1 = require("cosmjs-types/cosmos/staking/v1beta1/query");
13
+ const tx_2 = require("cosmjs-types/cosmos/tx/v1beta1/tx");
14
+ const fastify_1 = __importDefault(require("fastify"));
15
+ const stream_chain_1 = require("stream-chain");
16
+ const stream_json_1 = require("stream-json");
17
+ const Pick_1 = require("stream-json/filters/Pick");
18
+ const StreamArray_1 = require("stream-json/streamers/StreamArray");
19
+ const StreamValues_1 = require("stream-json/streamers/StreamValues");
20
+ const Batch_1 = require("stream-json/utils/Batch");
21
+ const uuid_1 = require("uuid");
22
+ const winston_1 = __importDefault(require("winston"));
23
+ const emitter_1 = require("../emitter");
24
+ const promise_queue_1 = require("../promise-queue");
25
+ const utils_1 = require("../utils");
26
+ exports.defaultIndexerConfig = {
27
+ startHeight: 1,
28
+ batchSize: 500,
29
+ modules: [],
30
+ getNextHeight: () => 1,
31
+ logLevel: "info",
32
+ usePolling: false,
33
+ pollingInterval: 5000,
34
+ processGenesis: false,
35
+ minimal: true,
36
+ init: () => Promise.resolve(),
37
+ beginTransaction: () => Promise.resolve(),
38
+ endTransaction: (_status) => Promise.resolve()
39
+ };
40
+ class EcleciaIndexer extends emitter_1.EclesiaEmitter {
41
+ constructor(config) {
42
+ super();
43
+ this.initialized = false;
44
+ this.retryCount = 0;
45
+ this.debugTime = null;
46
+ this.healthCheck = {
47
+ status: "CONNECTING"
48
+ };
49
+ this.subscription = null;
50
+ this.blockListener = {
51
+ next: (data) => {
52
+ this.newBlockReceived(data.header.height);
53
+ }
54
+ };
55
+ this.asyncEmit = async (type, event) => {
56
+ event.uuid = (0, uuid_1.v4)();
57
+ this.debugTimes();
58
+ // More than 1 listener can be registered for an event type
59
+ // Fortunately these are all set up during module init() so we have a consistent count
60
+ // so we can count responses to resolve when complete
61
+ // values are irrelevant as promise resolution is only used for flow control
62
+ let listenerCount = this.handled.get(type);
63
+ if (!listenerCount) {
64
+ // Setting listenerCount to 1 (the unhandled listener)
65
+ listenerCount = 1;
66
+ }
67
+ let listenersResponded = 0;
68
+ const prom = new Promise((resolve, reject) => {
69
+ const returnFunc = (ev) => {
70
+ if (ev.uuid == event.uuid) {
71
+ if (ev.status) {
72
+ listenersResponded++;
73
+ if (listenersResponded == listenerCount) {
74
+ // All listeners have done their thing so we can remove listener, resolve and continue execution
75
+ this.off("uuid", returnFunc);
76
+ this.debugTimes(type);
77
+ resolve();
78
+ }
79
+ }
80
+ else {
81
+ // At least 1 listener is reporting an error. Reject and handle exception at the original asyncEmit location
82
+ reject(ev.error);
83
+ }
84
+ }
85
+ };
86
+ this.on("uuid", returnFunc);
87
+ });
88
+ this.emit(type, event);
89
+ return prom;
90
+ };
91
+ this.config = {
92
+ ...exports.defaultIndexerConfig,
93
+ ...config
94
+ };
95
+ if (!this.config.minimal) {
96
+ this.blockQueue = new promise_queue_1.PromiseQueue(this.config.batchSize);
97
+ }
98
+ else {
99
+ this.blockQueue = new promise_queue_1.PromiseQueue(this.config.batchSize);
100
+ }
101
+ const { printf } = winston_1.default.format;
102
+ const eclesiaFormat = printf(({ level, message, timestamp }) => {
103
+ return `${timestamp} [${level.toUpperCase()}]:\t${message}`;
104
+ });
105
+ this.log = winston_1.default.createLogger({
106
+ level: this.config.logLevel,
107
+ defaultMeta: { service: "Eclesia Indexer" },
108
+ transports: [
109
+ new winston_1.default.transports.File({
110
+ filename: "error.log",
111
+ level: "error"
112
+ }),
113
+ new winston_1.default.transports.File({ filename: "combined.log" }),
114
+ new winston_1.default.transports.Console({
115
+ format: winston_1.default.format.combine(winston_1.default.format.splat(), winston_1.default.format.timestamp(), eclesiaFormat, winston_1.default.format.colorize({ all: true }))
116
+ })
117
+ ]
118
+ });
119
+ this.fastify = (0, fastify_1.default)({
120
+ logger: false
121
+ });
122
+ this.on("_unhandled", (msg) => {
123
+ if (msg.uuid) {
124
+ this.log.verbose("Unhandled event: " + msg.type);
125
+ this.emit("uuid", {
126
+ status: true,
127
+ uuid: msg.uuid
128
+ });
129
+ }
130
+ });
131
+ this.fastify.get("/health", async (_request, reply) => {
132
+ const code = this.healthCheck.status == "OK" ? 200 : 503;
133
+ reply.code(code).send(this.healthCheck);
134
+ });
135
+ this.fastify.listen({
136
+ port: 80,
137
+ host: "0.0.0.0"
138
+ }, (err) => {
139
+ if (err) {
140
+ this.log.error(err);
141
+ process.exit(1);
142
+ }
143
+ });
144
+ }
145
+ setStatus(status) {
146
+ this.healthCheck.status = status;
147
+ }
148
+ isMinimal(_blockqueue) {
149
+ if (this.config.minimal) {
150
+ return true;
151
+ }
152
+ else {
153
+ return false;
154
+ }
155
+ }
156
+ async connect() {
157
+ try {
158
+ this.client = await (0, tendermint_rpc_2.connectComet)(this.config.rpcUrl);
159
+ this.log.info("Connected to RPC");
160
+ return true;
161
+ }
162
+ catch (error) {
163
+ this.log.error(error);
164
+ return false;
165
+ }
166
+ }
167
+ debugTimes(label) {
168
+ if (this.debugTime && this.config.logLevel == "debug") {
169
+ const hrTime = process.hrtime(this.debugTime);
170
+ this.log.debug("Process time for " + label + ": " + Number(hrTime[0] * 1000 + hrTime[1] / 1000000) + "ms");
171
+ this.debugTime = null;
172
+ }
173
+ else {
174
+ this.debugTime = process.hrtime();
175
+ }
176
+ }
177
+ async start() {
178
+ this.debugTime = null;
179
+ if (!this.initialized) {
180
+ try {
181
+ if (this.config.init) {
182
+ await this.config.init();
183
+ }
184
+ }
185
+ catch (e) {
186
+ this.log.error("Failed to initialize indexer: " + e);
187
+ this.setStatus("FAILED");
188
+ throw e;
189
+ }
190
+ if (this.config.processGenesis) {
191
+ try {
192
+ if (this.config.genesisPath) {
193
+ await this.parseGenesis();
194
+ }
195
+ }
196
+ catch (e) {
197
+ this.log.error("Failed to parse genesis: " + e);
198
+ this.setStatus("FAILED");
199
+ throw e;
200
+ }
201
+ }
202
+ this.initialized = true;
203
+ }
204
+ this.subscription = this.client.subscribeNewBlock ? this.client.subscribeNewBlock() : null;
205
+ try {
206
+ await this.connect();
207
+ const status = await this.client.status();
208
+ this.latestHeight = status.syncInfo.latestBlockHeight;
209
+ this.blockQueue.clear();
210
+ this.log.info("Current chain height: " + this.latestHeight);
211
+ this.heightToProcess = await this.config.getNextHeight();
212
+ if (this.config.usePolling) {
213
+ this.pollForBlock();
214
+ }
215
+ else {
216
+ if (this.subscription) {
217
+ this.subscription.addListener(this.blockListener);
218
+ }
219
+ else {
220
+ throw new Error("Could not subscribe to new blocks");
221
+ }
222
+ }
223
+ }
224
+ catch (e) {
225
+ this.log.error("Failed to set up block listening: " + e);
226
+ this.setStatus("FAILED");
227
+ throw e;
228
+ }
229
+ this.fetcher().catch((e) => {
230
+ throw new Error("Error in fetching service: " + e);
231
+ });
232
+ const hrTime = process.hrtime();
233
+ let ms = hrTime[0] * 1000000 + hrTime[1] / 1000;
234
+ while (this.blockQueue.size() > 0) {
235
+ // await the dequeued promise is essentially awaiting fetched data for that block
236
+ try {
237
+ // Index block inside a db transaction to ensure data consistency
238
+ await this.config.beginTransaction();
239
+ this.log.silly("Started db tx");
240
+ let height, timestamp;
241
+ if (this.isMinimal(this.blockQueue)) {
242
+ const toProcess = await this.blockQueue.dequeue();
243
+ this.log.silly("Retrieved block data");
244
+ if (!toProcess) {
245
+ throw new Error("Could not fetch block");
246
+ }
247
+ height = toProcess[0].block.header.height;
248
+ timestamp = (0, tendermint_rpc_1.toRfc3339WithNanoseconds)(toProcess[0].block.header.time);
249
+ await this.processBlock(toProcess[0], toProcess[1]);
250
+ }
251
+ else {
252
+ const toProcess = await this.blockQueue.dequeue();
253
+ this.log.silly("Retrieved block data");
254
+ if (!toProcess) {
255
+ throw new Error("Could not fetch block");
256
+ }
257
+ this.log.silly("Decoded block");
258
+ height = toProcess[0].block.header.height;
259
+ timestamp = (0, tendermint_rpc_1.toRfc3339WithNanoseconds)(toProcess[0].block.header.time);
260
+ await this.processBlock(toProcess[0], toProcess[1], query_1.QueryValidatorsResponse.decode(toProcess[2]).validators);
261
+ }
262
+ // Emit events to trigger periodic operations every 50, 100 and 1000 blocks
263
+ if (height % 1000 == 0) {
264
+ const hrTime = process.hrtime();
265
+ const newms = hrTime[0] * 1000000 + hrTime[1] / 1000;
266
+ const duration = newms - ms;
267
+ ms = newms;
268
+ const rate = 1000000000 / duration;
269
+ this.log.info("Processing:" + rate.toFixed(2) + "blocks/sec");
270
+ await this.asyncEmit("periodic/1000", {
271
+ value: null,
272
+ height,
273
+ timestamp
274
+ });
275
+ }
276
+ if (height % 100 == 0) {
277
+ await this.asyncEmit("periodic/100", {
278
+ value: null,
279
+ height,
280
+ timestamp
281
+ });
282
+ }
283
+ if (height % 50 == 0) {
284
+ await this.asyncEmit("periodic/50", {
285
+ value: null,
286
+ height,
287
+ timestamp
288
+ });
289
+ }
290
+ this.log.silly("Handled periodic events");
291
+ await this.config.endTransaction(true);
292
+ this.log.silly("Committed db tx");
293
+ }
294
+ catch (e) {
295
+ this.log.error("" + e);
296
+ this.setStatus("FAILED");
297
+ await this.config.endTransaction(false);
298
+ if (this.subscription) {
299
+ this.subscription.removeListener(this.blockListener);
300
+ }
301
+ this.retryCount++;
302
+ break;
303
+ }
304
+ this.retryCount = 0;
305
+ this.setStatus("OK");
306
+ }
307
+ if (this.retryCount < 3) {
308
+ this.log.debug("Indexer retryCount: " + this.retryCount);
309
+ this.log.info("Indexer is restarting");
310
+ this.start();
311
+ }
312
+ }
313
+ async processBlock(block, block_results, validators) {
314
+ const height = block.block.header.height;
315
+ this.log.debug("Processing block: %d", height);
316
+ this.log.silly("Started db tx");
317
+ // Initialize height & timestamp to be used for this block-processing run
318
+ const timestamp = (0, tendermint_rpc_1.toRfc3339WithNanoseconds)(block.block.header.time);
319
+ // Use & await asyncEmit to ensure db insertions in order
320
+ // Emit block information to any interested modules.
321
+ // Primarily the required block module listens to this
322
+ await this.asyncEmit("block", {
323
+ value: {
324
+ block,
325
+ block_results
326
+ },
327
+ height,
328
+ timestamp
329
+ });
330
+ this.log.silly("Modules handled block event");
331
+ // Deal with begin_block events first
332
+ await this.asyncEmit("begin_block", {
333
+ value: {
334
+ events: block_results.beginBlockEvents,
335
+ validators
336
+ },
337
+ height,
338
+ timestamp
339
+ });
340
+ this.log.silly("Modules handled begin_block events");
341
+ // Then individual tx_events
342
+ await this.asyncEmit("tx_events", {
343
+ value: block_results.results,
344
+ height,
345
+ timestamp
346
+ });
347
+ this.log.silly("Modules handled tx events");
348
+ // Emit details and result for each tx msg separately
349
+ for (let t = 0; t < block.block.txs.length; t++) {
350
+ const tx = tx_2.Tx.decode(block.block.txs[t]);
351
+ const result = block_results.results[t].code;
352
+ const txlog = block_results.results[t].log;
353
+ if (result != 0) {
354
+ // Tx failed. Ignore
355
+ continue;
356
+ }
357
+ if (tx.body && tx.body.memo != "") {
358
+ const txHash = (0, node_crypto_1.createHash)("sha256").update(block.block.txs[t]).digest("hex");
359
+ await this.asyncEmit("tx_memo", {
360
+ value: {
361
+ txHash,
362
+ txBody: tx.body
363
+ },
364
+ height,
365
+ timestamp
366
+ });
367
+ }
368
+ // parsing log rather than using events directly in order to have msg_index available to filter appropriate events for each msg
369
+ const events = txlog
370
+ ? JSON.parse(txlog)
371
+ : [];
372
+ const msgs = tx.body?.messages;
373
+ if (msgs) {
374
+ for (let i = 0; i < msgs.length; i++) {
375
+ this.log.silly("Indexer broadcasting msg for handling: " + msgs[i].typeUrl);
376
+ const msgevents = msgs.length > 1
377
+ ? events.find((x) => x.msg_index == i)?.events
378
+ : events[0].events;
379
+ await this.asyncEmit(msgs[i].typeUrl, {
380
+ value: {
381
+ tx: msgs[i].value,
382
+ events: msgevents
383
+ },
384
+ height,
385
+ timestamp
386
+ });
387
+ if (msgs[i].typeUrl == "/cosmos.authz.v1beta1.MsgExec") {
388
+ const authzMsgs = tx_1.MsgExec.decode(msgs[i].value).msgs;
389
+ if (authzMsgs) {
390
+ for (let r = 0; r < authzMsgs.length; r++) {
391
+ this.log.silly("Indexer broadcasting msg for handling: " + authzMsgs[i].typeUrl);
392
+ const authzMsgEvents = msgevents?.reduce((events, evt) => {
393
+ if (evt.attributes.filter(x => (0, utils_1.decodeAttr)(x.key) == "authz_msg_index" && (0, utils_1.decodeAttr)(x.value) == "" + r).length > 0) {
394
+ events.push(evt);
395
+ }
396
+ return events;
397
+ }, []);
398
+ await this.asyncEmit(authzMsgs[i].typeUrl, {
399
+ value: {
400
+ tx: authzMsgs[i].value,
401
+ events: authzMsgEvents
402
+ },
403
+ height,
404
+ timestamp
405
+ });
406
+ }
407
+ }
408
+ }
409
+ }
410
+ }
411
+ }
412
+ this.log.silly("Modules handled msg events");
413
+ // Then deal with end_block events
414
+ await this.asyncEmit("end_block", {
415
+ value: block_results.endBlockEvents,
416
+ height,
417
+ timestamp
418
+ });
419
+ this.log.silly("Modules handled end_block events");
420
+ }
421
+ async fetcher() {
422
+ let error = false;
423
+ for (let i = this.heightToProcess; i <= this.latestHeight; i++) {
424
+ this.log.debug("Fetching: " + i);
425
+ try {
426
+ if (this.isMinimal(this.blockQueue)) {
427
+ const toIndex = Promise.all([
428
+ this.client.block(i),
429
+ this.client.blockResults(i)
430
+ ]).catch((_e) => {
431
+ this.log.error("Error fetching block: " + i);
432
+ return Promise.resolve([]);
433
+ });
434
+ this.blockQueue.enqueue(toIndex);
435
+ }
436
+ else {
437
+ const q = query_1.QueryValidatorsRequest.fromPartial({ pagination: { limit: 1000n } });
438
+ const vals = query_1.QueryValidatorsRequest.encode(q).finish();
439
+ const toIndex = Promise.all([
440
+ this.client.block(i),
441
+ this.client.blockResults(i),
442
+ this.callABCI("/cosmos.staking.v1beta1.Query/Validators", vals, i)
443
+ ]).catch((_e) => {
444
+ this.log.error("Error fetching block: " + i);
445
+ return Promise.resolve([]);
446
+ });
447
+ this.blockQueue.enqueue(toIndex);
448
+ }
449
+ await this.blockQueue.continue();
450
+ }
451
+ catch (e) {
452
+ this.log.error(e);
453
+ error = true;
454
+ break;
455
+ }
456
+ }
457
+ if (!error) {
458
+ this.blockQueue.setSynced();
459
+ this.log.info("Synced to latest height");
460
+ }
461
+ }
462
+ async callABCI(path, data, height) {
463
+ const timeout = new Promise((resolve) => {
464
+ setTimeout(resolve, 30000);
465
+ });
466
+ const abciq = await Promise.race([
467
+ this.client.abciQuery({
468
+ path,
469
+ data,
470
+ height: height
471
+ }),
472
+ timeout
473
+ ]);
474
+ if (abciq) {
475
+ return abciq.value;
476
+ }
477
+ else {
478
+ this.setStatus("FAILED");
479
+ throw new Error("RPC not responding. Query at: " + path);
480
+ }
481
+ }
482
+ newBlockReceived(height) {
483
+ this.log.info("Received new block: %d", height);
484
+ // If we are synced, add to end of queue
485
+ if (this.blockQueue.synced) {
486
+ try {
487
+ if (this.isMinimal(this.blockQueue)) {
488
+ this.blockQueue.enqueue(Promise.all([
489
+ this.client.block(height),
490
+ this.client.blockResults(height)
491
+ ]).catch((_e) => {
492
+ this.log.error("Error fetching block: " + height);
493
+ return Promise.resolve([]);
494
+ }));
495
+ }
496
+ else {
497
+ const q = query_1.QueryValidatorsRequest.fromPartial({ pagination: { limit: 1000n } });
498
+ const vals = query_1.QueryValidatorsRequest.encode(q).finish();
499
+ this.blockQueue.enqueue(Promise.all([
500
+ this.client.block(height),
501
+ this.client.blockResults(height),
502
+ this.callABCI("/cosmos.staking.v1beta1.Query/Validators", vals, height)
503
+ ]).catch((_e) => {
504
+ this.log.error("Error fetching block: " + height);
505
+ return Promise.resolve([]);
506
+ }));
507
+ }
508
+ }
509
+ catch (e) {
510
+ this.log.error("" + e);
511
+ }
512
+ }
513
+ else {
514
+ this.latestHeight = height;
515
+ }
516
+ }
517
+ async pollForBlock() {
518
+ const status = await this.client.status();
519
+ if (status.syncInfo.latestBlockHeight > this.latestHeight) {
520
+ while (this.latestHeight < status.syncInfo.latestBlockHeight) {
521
+ this.newBlockReceived(this.latestHeight + 1);
522
+ }
523
+ }
524
+ setTimeout(() => {
525
+ this.pollForBlock();
526
+ }, this.config.pollingInterval);
527
+ }
528
+ readGenesis() {
529
+ if (this.config.genesisPath) {
530
+ return node_fs_1.default.createReadStream(this.config.genesisPath).pipe((0, stream_json_1.parser)());
531
+ }
532
+ else {
533
+ throw new Error("Genesis path not set");
534
+ }
535
+ }
536
+ async setArrayReader(path, processor) {
537
+ const readPromise = new Promise((resolve, reject) => {
538
+ try {
539
+ const filters = path.split(".");
540
+ const pickers = filters.map((filter) => (0, Pick_1.pick)({ filter }));
541
+ let counter = 0;
542
+ (0, stream_chain_1.chain)([this.readGenesis(), ...pickers, (0, StreamArray_1.streamArray)(), (0, Batch_1.batch)({ batchSize: 1000 }), processor])
543
+ .on("data", (data) => {
544
+ if (data && Array.isArray(data)) {
545
+ counter = counter + data.length;
546
+ }
547
+ })
548
+ .on("end", () => {
549
+ this.log.info(`Processed ${counter} entries`);
550
+ resolve(true);
551
+ });
552
+ }
553
+ catch (_e) {
554
+ this.log.verbose("Error in setArrayReader: " + _e);
555
+ reject();
556
+ }
557
+ });
558
+ return readPromise;
559
+ }
560
+ async setValueReader(path, processor) {
561
+ const readPromise = new Promise((resolve, reject) => {
562
+ try {
563
+ const filters = path.split(".");
564
+ const pickers = filters.map((filter) => (0, Pick_1.pick)({ filter }));
565
+ let counter = 0;
566
+ (0, stream_chain_1.chain)([this.readGenesis(), ...pickers, (0, StreamValues_1.streamValues)(), processor])
567
+ .on("data", (_data) => {
568
+ counter++;
569
+ })
570
+ .on("end", () => {
571
+ this.log.info(`Processed ${counter} entries`);
572
+ resolve(true);
573
+ });
574
+ }
575
+ catch (_e) {
576
+ reject();
577
+ }
578
+ });
579
+ return readPromise;
580
+ }
581
+ async parseGenesis() {
582
+ this.log.info("Parsing genesis");
583
+ await this.config.beginTransaction();
584
+ try {
585
+ this.log.info("Starting genesis import");
586
+ this.log.debug("Importing genesis file...");
587
+ for (const [key, _value] of this.handled) {
588
+ if (key.startsWith("genesis/")) {
589
+ const genesisEntry = key.split("/");
590
+ this.log.verbose("Importing " + key + "...");
591
+ if (genesisEntry[1] == "array") {
592
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
593
+ await this.setArrayReader(genesisEntry[2], async (data) => {
594
+ await this.asyncEmit(key, { value: data.map((x) => x.value) });
595
+ return data;
596
+ });
597
+ }
598
+ else {
599
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
600
+ await this.setValueReader(genesisEntry[2], async (data) => {
601
+ await this.asyncEmit(key, { value: data.value });
602
+ return data;
603
+ });
604
+ }
605
+ }
606
+ }
607
+ this.log.info("Importing gen TXs...");
608
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
609
+ await this.setArrayReader("app_state.genutil.gen_txs", async (data) => {
610
+ for (let j = 0; j < data.length; j++) {
611
+ const gentx = data[j].value;
612
+ for (let i = 0; i < gentx.body.messages.length; i++) {
613
+ const msg = gentx.body.messages[i];
614
+ await this.asyncEmit(("gentx" + msg["@type"]), { value: msg });
615
+ }
616
+ }
617
+ return data;
618
+ });
619
+ await this.config.endTransaction(true);
620
+ this.log.info("Finished importing");
621
+ }
622
+ catch (e) {
623
+ await this.config.endTransaction(false);
624
+ this.log.error("Failed to import genesis");
625
+ throw e;
626
+ }
627
+ }
628
+ }
629
+ exports.EcleciaIndexer = EcleciaIndexer;
@@ -0,0 +1,17 @@
1
+ export declare class PromiseQueue<T> {
2
+ private items;
3
+ private enqueuer;
4
+ private batcher;
5
+ synced: boolean;
6
+ private batchSize;
7
+ private continuePromise;
8
+ constructor(batchSize: number);
9
+ enqueue(item: T | PromiseLike<T>): void;
10
+ clear(): void;
11
+ dequeue(): Promise<T>;
12
+ continue(): Promise<boolean>;
13
+ setSynced(): void;
14
+ isEmpty(): boolean;
15
+ size(): number;
16
+ }
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/promise-queue/index.ts"],"names":[],"mappings":"AAOA,qBAAa,YAAY,CAAC,CAAC;IACzB,OAAO,CAAC,KAAK,CAAoB;IAEjC,OAAO,CAAC,QAAQ,CAAqC;IAErD,OAAO,CAAC,OAAO,CAA0B;IAElC,MAAM,UAAS;IAEtB,OAAO,CAAC,SAAS,CAAS;IAE1B,OAAO,CAAC,eAAe,CAAmB;gBAE9B,SAAS,EAAE,MAAM;IAY7B,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;IAiBhC,KAAK;IAWL,OAAO,IAMU,OAAO,CAAC,CAAC,CAAC;IAG3B,QAAQ;IAIR,SAAS;IAIT,OAAO;IAQP,IAAI;CAGL"}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /*
3
+ This implements an "infinite" FIFO queue of fixed size.
4
+ await `continue()` before enqueing items to ensure fixed size (as it only resolves when space available)
5
+ await `dequeue()` to pop an item as it will only resolve if the next item is available
6
+ size() is always at minimum 1 item which is the promise that will resolve to the next item whenever it is enqueued
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.PromiseQueue = void 0;
10
+ class PromiseQueue {
11
+ constructor(batchSize) {
12
+ this.synced = false;
13
+ const nextVal = new Promise((resolve, _reject) => {
14
+ this.enqueuer = resolve;
15
+ });
16
+ this.batchSize = batchSize;
17
+ this.continuePromise = new Promise((resolve, _reject) => {
18
+ this.batcher = resolve;
19
+ });
20
+ this.batcher(true);
21
+ this.items = [nextVal];
22
+ }
23
+ enqueue(item) {
24
+ try {
25
+ this.enqueuer(item);
26
+ const nextVal = new Promise((resolve, _reject) => {
27
+ this.enqueuer = resolve;
28
+ });
29
+ this.items.unshift(nextVal);
30
+ if (this.size() > this.batchSize) {
31
+ this.continuePromise = new Promise((resolve, _reject) => {
32
+ this.batcher = resolve;
33
+ });
34
+ }
35
+ }
36
+ catch (e) {
37
+ console.error("Enqueing rejected data: " + e);
38
+ }
39
+ }
40
+ clear() {
41
+ const nextVal = new Promise((resolve, _reject) => {
42
+ this.enqueuer = resolve;
43
+ });
44
+ this.continuePromise = new Promise((resolve, _reject) => {
45
+ this.batcher = resolve;
46
+ });
47
+ this.batcher(true);
48
+ this.items = [nextVal];
49
+ }
50
+ dequeue() {
51
+ const item = this.items.pop();
52
+ if (this.size() <= this.batchSize) {
53
+ this.batcher(true);
54
+ }
55
+ return item;
56
+ }
57
+ continue() {
58
+ return this.continuePromise;
59
+ }
60
+ setSynced() {
61
+ this.synced = true;
62
+ }
63
+ isEmpty() {
64
+ if (this.items.length == 0) {
65
+ return true;
66
+ }
67
+ else {
68
+ return false;
69
+ }
70
+ }
71
+ size() {
72
+ return this.items.length;
73
+ }
74
+ }
75
+ exports.PromiseQueue = PromiseQueue;
@@ -0,0 +1,97 @@
1
+ import { BlockResponse, BlockResultsResponse } from "@cosmjs/tendermint-rpc";
2
+ import { Event } from "@cosmjs/tendermint-rpc/build/comet38/responses";
3
+ import { Validator } from "cosmjs-types/cosmos/staking/v1beta1/staking";
4
+ import { TxBody } from "cosmjs-types/cosmos/tx/v1beta1/tx";
5
+ import { EcleciaIndexer } from "../indexer";
6
+ import { PromiseQueue } from "../promise-queue";
7
+ export type EcleciaIndexerConfig = {
8
+ startHeight?: number;
9
+ endHeight?: number;
10
+ batchSize: number;
11
+ modules: string[];
12
+ getNextHeight: () => number | PromiseLike<number>;
13
+ logLevel: "error" | "warn" | "info" | "http" | "verbose" | "debug" | "silly";
14
+ rpcUrl: string;
15
+ processGenesis?: boolean;
16
+ genesisPath?: string;
17
+ usePolling?: boolean;
18
+ pollingInterval?: number;
19
+ minimal?: boolean;
20
+ init?: () => Promise<void>;
21
+ beginTransaction: () => Promise<void>;
22
+ endTransaction: (status: boolean) => Promise<void>;
23
+ };
24
+ export type FullBlockQueue = PromiseQueue<[BlockResponse, BlockResultsResponse, Uint8Array]>;
25
+ export type MinimalBlockQueue = PromiseQueue<[BlockResponse, BlockResultsResponse]>;
26
+ export type BlockQueue = FullBlockQueue | MinimalBlockQueue;
27
+ export type WithHeightAndUUID<T> = {
28
+ [K in keyof T]: T[K] & {
29
+ uuid?: string;
30
+ height?: number;
31
+ timestamp?: string;
32
+ };
33
+ };
34
+ export type EmitFunc<K extends keyof WithHeightAndUUID<EventMap>> = (t: K, e: WithHeightAndUUID<EventMap>[K]) => Promise<void | void[]>;
35
+ export type LogEvent = {
36
+ type: "log" | "info" | "warning" | "error" | "verbose" | "transient";
37
+ message: string;
38
+ };
39
+ export type UUIDEvent = {
40
+ uuid: string;
41
+ error?: string;
42
+ status: boolean;
43
+ };
44
+ export type Events = {
45
+ log: LogEvent;
46
+ uuid: UUIDEvent;
47
+ begin_block: {
48
+ value: {
49
+ events: BlockResultsResponse["beginBlockEvents"];
50
+ validators: Validator[] | undefined;
51
+ };
52
+ };
53
+ block: {
54
+ value: {
55
+ block: BlockResponse;
56
+ block_results: BlockResultsResponse;
57
+ };
58
+ };
59
+ end_block: {
60
+ value: BlockResultsResponse["endBlockEvents"];
61
+ };
62
+ tx_events: {
63
+ value: BlockResultsResponse["results"];
64
+ };
65
+ tx_memo: {
66
+ value: {
67
+ txHash: string;
68
+ txBody: TxBody;
69
+ };
70
+ };
71
+ _unhandled: {
72
+ type: string;
73
+ event: unknown;
74
+ };
75
+ "periodic/50": {
76
+ value: null;
77
+ };
78
+ "periodic/100": {
79
+ value: null;
80
+ };
81
+ "periodic/1000": {
82
+ value: null;
83
+ };
84
+ };
85
+ export type TxResult<T> = {
86
+ tx: T;
87
+ events: Event[];
88
+ };
89
+ export interface IndexingModule {
90
+ indexer: EcleciaIndexer;
91
+ name: string;
92
+ depends: string[];
93
+ provides: string[];
94
+ setup: () => Promise<void>;
95
+ init: (...args: any[]) => void;
96
+ }
97
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ declare function toHexString(byteArray: number[]): string;
2
+ declare function keyHashfromAddress(address: string): string;
3
+ declare function chainAddressfromKeyhash(prefix: string, keyhash: string): string;
4
+ export { chainAddressfromKeyhash, keyHashfromAddress, toHexString };
5
+ //# sourceMappingURL=bech32.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bech32.d.ts","sourceRoot":"","sources":["../../src/utils/bech32.ts"],"names":[],"mappings":"AAEA,iBAAS,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,UAMvC;AACD,iBAAS,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMnD;AACD,iBAAS,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAI/D;AAED,OAAO,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.chainAddressfromKeyhash = chainAddressfromKeyhash;
4
+ exports.keyHashfromAddress = keyHashfromAddress;
5
+ exports.toHexString = toHexString;
6
+ const bech32_1 = require("bech32");
7
+ function toHexString(byteArray) {
8
+ return Array.prototype.map
9
+ .call(byteArray, (byte) => {
10
+ return ("0" + (byte & 0xff).toString(16)).slice(-2);
11
+ })
12
+ .join("");
13
+ }
14
+ function keyHashfromAddress(address) {
15
+ try {
16
+ return toHexString(bech32_1.bech32.fromWords(bech32_1.bech32.decode(address).words));
17
+ }
18
+ catch (_e) {
19
+ throw new Error("Could not decode address");
20
+ }
21
+ }
22
+ function chainAddressfromKeyhash(prefix, keyhash) {
23
+ const words = bech32_1.bech32.toWords(Buffer.from(keyhash, "hex"));
24
+ return keyhash !== "" ? bech32_1.bech32.encode(prefix, words) : "";
25
+ }
@@ -0,0 +1,3 @@
1
+ declare const toPlainObject: (obj: unknown) => any;
2
+ export { toPlainObject };
3
+ //# sourceMappingURL=bigint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bigint.d.ts","sourceRoot":"","sources":["../../src/utils/bigint.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,aAAa,GAAI,KAAK,OAAO,QAGlC,CAAC;AACF,OAAO,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toPlainObject = void 0;
4
+ const toPlainObject = (obj) => {
5
+ return JSON.parse(JSON.stringify(obj, (key, value) => typeof value === "bigint" ? value.toString() : value // return everything else unchanged
6
+ ));
7
+ };
8
+ exports.toPlainObject = toPlainObject;
@@ -0,0 +1,4 @@
1
+ export * from "./bech32";
2
+ export * from "./bigint";
3
+ export * from "./text";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./bech32"), exports);
18
+ __exportStar(require("./bigint"), exports);
19
+ __exportStar(require("./text"), exports);
@@ -0,0 +1,2 @@
1
+ export declare const decodeAttr: (x: Uint8Array | string) => string | undefined;
2
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/utils/text.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,GAAI,GAAG,UAAU,GAAG,MAAM,uBAOhD,CAAC"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decodeAttr = void 0;
4
+ const decodeAttr = (x) => {
5
+ if (typeof x === "string") {
6
+ return x;
7
+ }
8
+ if (x instanceof Uint8Array) {
9
+ return Buffer.from(x).toString();
10
+ }
11
+ };
12
+ exports.decodeAttr = decodeAttr;
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@eclesia/indexer-engine",
3
+ "version": "2.3.1",
4
+ "description": "Core eclesia indexer engine",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "lint": "eslint",
10
+ "lint:fix": "eslint --fix",
11
+ "test": "vitest"
12
+ },
13
+ "files": [
14
+ "dist/",
15
+ "scripts/",
16
+ "templates"
17
+ ],
18
+ "keywords": [],
19
+ "contributors": [
20
+ "Alex M.<alex.megalokonomos@tendermint.com>"
21
+ ],
22
+ "license": "Apache-2.0",
23
+ "devDependencies": {
24
+ "@eslint/js": "^9.24.0",
25
+ "@stylistic/eslint-plugin-ts": "^4.2.0",
26
+ "@types/node": "^22.14.0",
27
+ "@types/stream-chain": "^2.1.0",
28
+ "@types/stream-json": "^1.7.8",
29
+ "@types/uuid": "^10.0.0",
30
+ "eslint": "^9.24.0",
31
+ "eslint-plugin-simple-import-sort": "^12.1.1",
32
+ "typescript": "^5.8.3",
33
+ "typescript-eslint": "^8.29.0",
34
+ "vitest": "^3.1.1"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "dependencies": {
40
+ "@cosmjs/tendermint-rpc": "^0.33.1",
41
+ "bech32": "^2.0.0",
42
+ "cosmjs-types": "^0.9.0",
43
+ "dayjs": "^1.11.13",
44
+ "fastify": "^5.2.2",
45
+ "stream-chain": "^3.4.0",
46
+ "stream-json": "^1.9.1",
47
+ "uuid": "^11.1.0",
48
+ "winston": "^3.17.0"
49
+ }
50
+ }