@mentaproject/client 0.1.23 → 0.1.25

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.
Files changed (37) hide show
  1. package/dist/managers/PersistenceManager.d.ts +3 -3
  2. package/dist/managers/TransactionManager.d.ts +1 -1
  3. package/dist/structures/Account.d.ts +1 -1
  4. package/dist/structures/Account.js +21 -19
  5. package/dist/structures/MentaClient.d.ts +5 -2
  6. package/dist/structures/MentaClient.js +24 -5
  7. package/dist/types/MentaClient.d.ts +10 -2
  8. package/dist/types/MentaClient.js +0 -2
  9. package/dist/types/PersistenceAdapter.d.ts +3 -2
  10. package/package.json +4 -4
  11. package/src/index.ts +21 -0
  12. package/src/managers/AccountManager.ts +51 -0
  13. package/src/managers/BlockManager.ts +51 -0
  14. package/src/managers/ContractManager.ts +60 -0
  15. package/src/managers/PersistenceManager.ts +59 -0
  16. package/src/managers/TransactionManager.ts +92 -0
  17. package/src/managers/index.ts +4 -0
  18. package/src/structures/Account.ts +251 -0
  19. package/src/structures/Block.ts +185 -0
  20. package/src/structures/Cache.ts +126 -0
  21. package/src/structures/MentaClient.ts +152 -0
  22. package/src/structures/Transaction.ts +220 -0
  23. package/src/structures/index.ts +5 -0
  24. package/src/types/Account.ts +44 -0
  25. package/src/types/Block.ts +14 -0
  26. package/src/types/Cache.ts +55 -0
  27. package/src/types/JsonRPC.ts +126 -0
  28. package/src/types/MentaClient.ts +79 -0
  29. package/src/types/PersistenceAdapter.ts +44 -0
  30. package/src/types/Transaction.ts +52 -0
  31. package/src/types/Utils.ts +30 -0
  32. package/src/types/index.ts +8 -0
  33. package/src/utils/bigint.ts +59 -0
  34. package/src/utils/toJSON.ts +123 -0
  35. package/src/utils/withCache.ts +50 -0
  36. package/test.ts +0 -89
  37. package/transactions.json +0 -1302
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @module MentaClientTypes
3
+ */
4
+ import {
5
+ CoreClientConfig,
6
+ PasskeySigner,
7
+ Transport,
8
+ } from "@mentaproject/core/types";
9
+ import { ICache } from "./Cache";
10
+ import { watchBlockNumber, watchBlocks } from "@mentaproject/core/actions";
11
+ import { IPersistenceAdapter } from "./PersistenceAdapter";
12
+
13
+ /**
14
+ * Configuration for the client's cache.
15
+ * @interface CacheConfig
16
+ */
17
+ export interface CacheConfig {
18
+ /**
19
+ * Time-to-live policies for different RPC methods.
20
+ * Keys are RPC method names (e.g., "eth_getBlockByNumber"), and values are
21
+ * the TTL in milliseconds for caching responses from those methods.
22
+ */
23
+ ttl_policies: Record<string, number>;
24
+ }
25
+
26
+ /**
27
+ * Configuration options for the MentaClient.
28
+ * Extends {@link CoreClientConfig} from `@mentaproject/core`.
29
+ * @interface MentaClientConfig
30
+ */
31
+ export interface MentaClientConfig extends CoreClientConfig {
32
+ /**
33
+ * Optional cache implementation for the client.
34
+ * If provided, the client will use this cache for RPC responses.
35
+ */
36
+ cache?: ICache;
37
+ /**
38
+ * Optional persistence adapter for local data storage.
39
+ * If provided, the client will use this adapter to persist and retrieve data.
40
+ */
41
+ persistenceAdapter?: IPersistenceAdapter;
42
+ /**
43
+ * Bundler transport used for userOperations
44
+ */
45
+ bundlerTransport: Transport;
46
+ /**
47
+ * Passkey compatible signer used for signing user operations
48
+ */
49
+ signer: PasskeySigner;
50
+ }
51
+
52
+ /**
53
+ * Defines the available client events and their corresponding watch functions.
54
+ * These functions are used to subscribe to real-time updates from the client.
55
+ */
56
+ export const ClientEvents = {
57
+ /**
58
+ * Event for new blocks.
59
+ * Uses the {@link watchBlocks} function from `@mentaproject/core`.
60
+ */
61
+ block: watchBlocks,
62
+ /**
63
+ * Event for new block numbers.
64
+ * Uses the {@link watchBlockNumber} function from `@mentaproject/core`.
65
+ */
66
+ blockNumber: watchBlockNumber,
67
+ };
68
+
69
+ /**
70
+ * Utility type to extract the callback function type for a given event.
71
+ * This type dynamically determines the expected callback signature for a specific client event.
72
+ * @template TEvent The name of the event (e.g., 'block' or 'blockNumber') for which to get the listener callback type.
73
+ */
74
+ export type GetListenerCallback<TEvent extends keyof typeof ClientEvents> =
75
+ Parameters<(typeof ClientEvents)[TEvent]>[1] extends infer P
76
+ ? P extends { [K in `on${Capitalize<TEvent>}`]: infer TCallback }
77
+ ? TCallback
78
+ : never
79
+ : never;
@@ -0,0 +1,44 @@
1
+ import { Address } from '@mentaproject/core/types';
2
+
3
+ import { Transaction } from '../structures/Transaction';
4
+ import { GetTransactionsParams } from './Account';
5
+ import { JSONTransaction } from './Transaction';
6
+
7
+ /**
8
+ * @interface IPersistenceAdapter
9
+ * @description Defines the contract for persistence layers, ensuring consistent data storage and retrieval operations.
10
+ */
11
+ export interface IPersistenceAdapter {
12
+ /**
13
+ * @method upsertTransactions
14
+ * @description Inserts or updates a list of transactions. The operation must be atomic.
15
+ * @param {Transaction[]} transactions - An array of transaction data, including associated account addresses and timestamps.
16
+ * @returns {Promise<void>} A Promise that resolves when the transactions have been successfully inserted or updated.
17
+ */
18
+ upsertTransactions(transactions: Transaction[]): Promise<void>;
19
+
20
+ /**
21
+ * @method getTransactions
22
+ * @description Retrieves a paginated list of transactions for a specific account based on provided filters.
23
+ * @param {GetTransactionsFilters} filters - An object containing filters for retrieving transactions, such as account address, block range, pagination, and sort order.
24
+ * @returns {Promise<JSONTransaction[]>} A Promise that resolves with transactions objects.
25
+ */
26
+ getTransactions(address: Address, params: GetTransactionsParams): Promise<JSONTransaction<0>[]>;
27
+
28
+ /**
29
+ * @method getLastSyncedBlock
30
+ * @description Retrieves the last synced block number for a given account. This is used to track the synchronization progress of an account's transactions.
31
+ * @param {string} accountAddress - The address of the account for which to retrieve the last synced block number.
32
+ * @returns {Promise<number | null>} A Promise that resolves with the last synced block number, or `null` if no block has been synced for the account.
33
+ */
34
+ getLastSyncedBlock(accountAddress: string): Promise<bigint | null>;
35
+
36
+ /**
37
+ * @method setLastSyncedBlock
38
+ * @description Saves the last synced block number for a given account. This updates the synchronization progress for an account.
39
+ * @param {string} accountAddress - The address of the account for which to save the last synced block number.
40
+ * @param {bigint} blockNumber - The block number to save as the last synced block for the account.
41
+ * @returns {Promise<void>} A Promise that resolves when the last synced block number has been successfully saved.
42
+ */
43
+ setLastSyncedBlock(accountAddress: string, blockNumber: bigint): Promise<void>;
44
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @module TransactionTypes
3
+ */
4
+
5
+ import { Hex, TransactionReceipt, Transaction as RawTransaction } from "@mentaproject/core";
6
+ import { Account } from "../structures";
7
+ import { JSONBlock } from "./Block";
8
+ import { JSONAccount } from "./Account";
9
+ import { WithBigintStringified } from "./Utils";
10
+
11
+ /**
12
+ * Defines the direction of a transaction relative to an account.
13
+ * "in" for incoming transactions, "out" for outgoing transactions.
14
+ * Defines the direction of a transaction relative to an account.
15
+ * "in" for incoming transactions, "out" for outgoing transactions.
16
+ */
17
+ export type TransactionDirection = "in" | "out";
18
+
19
+ /**
20
+ /**
21
+ * Parameters for retrieving a transaction's block.
22
+ * @interface IGetTransactionBlockParams
23
+ */
24
+ export interface IGetTransactionBlockParams {
25
+ /**
26
+ * Whether to include full transaction objects in the block.
27
+ * @type {boolean}
28
+ */
29
+ includeTransactions?: boolean;
30
+ };
31
+
32
+ export type TransactionCall = {
33
+ from: Account;
34
+ to: Account;
35
+ value: bigint;
36
+ input: Hex;
37
+ gas: bigint;
38
+ callType: string;
39
+ };
40
+
41
+ export type JSONTransaction<depth extends number> = depth extends 0
42
+ ? WithBigintStringified<Omit<RawTransaction, 'from' | 'to'>> & {
43
+ from: JSONAccount<depth>;
44
+ to?: JSONAccount<depth>;
45
+ }
46
+ : WithBigintStringified<Omit<RawTransaction, 'from' | 'to'>> & {
47
+ from: JSONAccount<depth>;
48
+ to?: JSONAccount<depth>;
49
+ block: JSONBlock<depth>;
50
+ receipt: WithBigintStringified<TransactionReceipt>;
51
+ calls?: WithBigintStringified<TransactionCall>[];
52
+ };
@@ -0,0 +1,30 @@
1
+ export type BigintStringified = `${number}n`;
2
+
3
+ // -- TRANSFORMATION EN CHAÎNE (AVEC RÉCURSIVITÉ) --
4
+
5
+ export type WithBigintStringified<T> = T extends bigint
6
+ // Cas de base : c'est un bigint, on le transforme
7
+ ? BigintStringified
8
+ // Cas récursif pour les tableaux : on applique la transformation à chaque élément
9
+ : T extends (infer E)[]
10
+ ? Array<WithBigintStringified<E>>
11
+ // Cas récursif pour les objets : on parcourt les propriétés et on ré-applique le type
12
+ : T extends object
13
+ ? { [K in keyof T]: WithBigintStringified<T[K]> }
14
+ // Cas de base : ce n'est ni un bigint, ni un objet, ni un tableau, on ne touche à rien
15
+ : T;
16
+
17
+
18
+ // -- TRANSFORMATION EN BIGINT (AVEC RÉCURSIVITÉ) --
19
+
20
+ export type WithoutBigintStringified<T> = T extends BigintStringified
21
+ // Cas de base : c'est une chaîne bigint, on la transforme
22
+ ? bigint
23
+ // Cas récursif pour les tableaux
24
+ : T extends (infer E)[]
25
+ ? Array<WithoutBigintStringified<E>>
26
+ // Cas récursif pour les objets
27
+ : T extends object
28
+ ? { [K in keyof T]: WithoutBigintStringified<T[K]> }
29
+ // Cas de base : on ne touche à rien
30
+ : T;
@@ -0,0 +1,8 @@
1
+ export * from './Account';
2
+ export * from './Block';
3
+ export * from './Transaction';
4
+ export * from './Cache';
5
+ export * from './PersistenceAdapter';
6
+ export * from './Utils';
7
+
8
+ export * from "@mentaproject/core/types";
@@ -0,0 +1,59 @@
1
+ import { WithoutBigintStringified } from "../types/Utils";
2
+
3
+ export const bigIntMax = (...args: bigint[]) => args.reduce((m, e) => e > m ? e : m);
4
+ export const bigIntMin = (...args: bigint[]) => args.reduce((m, e) => e < m ? e : m);
5
+
6
+ export function bigIntReviver(key: string, value: any) {
7
+ if (typeof value === 'string') {
8
+ if (value.endsWith('n')) {
9
+ try {
10
+ return BigInt(value.slice(0, -1));
11
+ } catch (e) {
12
+ return value;
13
+ }
14
+ };
15
+ };
16
+
17
+ return value;
18
+ };
19
+
20
+ export function stringifyBingints(obj: any) {
21
+ if (typeof obj === 'bigint') {
22
+ return obj.toString() + "n";
23
+ }
24
+
25
+ if (typeof obj === 'object') {
26
+ for (const key in obj) {
27
+ obj[key] = stringifyBingints(obj[key]);
28
+ }
29
+ }
30
+
31
+ if (Array.isArray(obj)) {
32
+ for (let i = 0; i < obj.length; i++) {
33
+ obj[i] = stringifyBingints(obj[i]);
34
+ }
35
+ }
36
+
37
+ return obj;
38
+ };
39
+
40
+ export function parseBingints<T extends { [key: string]: any }>(obj: T): WithoutBigintStringified<T> {
41
+ if (typeof obj !== 'object' || obj === null) {
42
+ return obj;
43
+ }
44
+
45
+ if (Array.isArray(obj)) {
46
+ return obj.map((item, index) => bigIntReviver(index.toString(), parseBingints(item))) as WithoutBigintStringified<T>;
47
+ }
48
+
49
+ const newObj: { [key: string]: any } = {};
50
+ for (const key in obj) {
51
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
52
+ const value = obj[key];
53
+ const processedValue = parseBingints(value);
54
+ newObj[key] = bigIntReviver(key, processedValue);
55
+ }
56
+ };
57
+
58
+ return newObj as WithoutBigintStringified<T>;
59
+ };
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @module toJSON
3
+ */
4
+
5
+ /**
6
+ * Interface for parameters passed to the toJSON function.
7
+ * @template T The type of the object to be converted to JSON.
8
+ */
9
+ export interface IToJSONParams<T extends { [key: string]: any } = { [key: string]: any }> {
10
+ /** The object to convert. */
11
+ obj: T;
12
+ /** The depth for recursive conversion. Defaults to 0. */
13
+ depth?: number;
14
+ }
15
+
16
+ /**
17
+ * Checks if an object is a class instance (not a native JavaScript object like Array, Date, etc.).
18
+ * @param {any} obj The object to check.
19
+ * @returns {boolean} True if the object is a class instance, false otherwise.
20
+ */
21
+ function isClassInstance(obj: any): boolean {
22
+ if (!obj) return false;
23
+
24
+ const constructor = obj.constructor;
25
+ const nativeConstructors = [Array, Date, RegExp, Map, Set, Promise, Function, Number, String, Boolean, Error, Object];
26
+
27
+ if (nativeConstructors.includes(constructor)) {
28
+ return false;
29
+ }
30
+
31
+ return true
32
+ };
33
+
34
+ /**
35
+ * Recursively converts BigInt values in an object to strings.
36
+ * @param {any} obj The object to convert.
37
+ * @returns {any} The object with BigInts converted to strings.
38
+ */
39
+ function convertBigintsToStrings(obj: any): any {
40
+ if (typeof obj === 'bigint') {
41
+ return obj.toString() + "n";
42
+ }
43
+
44
+ if (typeof obj === 'object') {
45
+ for (const key in obj) {
46
+ obj[key] = convertBigintsToStrings(obj[key]);
47
+ }
48
+ }
49
+
50
+ if (Array.isArray(obj)) {
51
+ for (let i = 0; i < obj.length; i++) {
52
+ obj[i] = convertBigintsToStrings(obj[i]);
53
+ }
54
+ }
55
+
56
+ return obj;
57
+ }
58
+
59
+ /**
60
+ * Converts an object, including its nested class instances and getter methods, into a plain JSON object.
61
+ * BigInt values are converted to strings.
62
+ *
63
+ * @param params - The parameters for conversion.
64
+ * @param params.obj - The object to convert.
65
+ * @param [params.depth=0] - The depth for recursive conversion. Defaults to 0.
66
+ * @returns A promise that resolves to the JSON representation of the object.
67
+ */
68
+ export async function toJSON({ obj, depth = 0 }: IToJSONParams) {
69
+ const data: any = {};
70
+ const promises: Promise<any>[] = [];
71
+
72
+ for (const key in obj) {
73
+ // skip class related properties
74
+ if (key === "toJSON" || key === "constructor" || key === "client" || key === "persistenceManager") continue;
75
+
76
+ const prop: any = obj[key];
77
+
78
+ if (typeof prop === "function") {
79
+ // do not fetch getters if depth is 0
80
+ if (depth === 0) continue;
81
+
82
+ const promise = async () => {
83
+ const value = await obj[key]();
84
+
85
+ // if the value is a class instance, convert it to JSON recursively
86
+ if (typeof value === "object" && isClassInstance(value)) return data[key] = await toJSON({ obj: value, depth: depth - 1 });
87
+
88
+ data[key] = value;
89
+ };
90
+
91
+ promises.push(promise());
92
+ };
93
+
94
+ if (typeof prop === "object" && isClassInstance(prop)) {
95
+ // If depth is 0, just flatten the object using recursion
96
+ if (depth === 0) {
97
+ const promise = async () => {
98
+ data[key] = await toJSON({
99
+ obj: prop,
100
+ depth: depth - 1
101
+ });
102
+ };
103
+ promises.push(promise());
104
+
105
+ continue;
106
+ };
107
+
108
+ // If depth is not 0, fetch the object
109
+ const promise = async () => {
110
+ data[key] = await prop.toJSON({ depth: depth - 1 });
111
+ };
112
+ promises.push(promise());
113
+
114
+ continue;
115
+ };
116
+
117
+ data[key] = prop;
118
+ };
119
+
120
+ await Promise.all(promises);
121
+
122
+ return convertBigintsToStrings(data);
123
+ };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @module withCache
3
+ */
4
+ import { watchBlockNumber } from "@mentaproject/core/actions";
5
+ import { CoreClient } from "../types";
6
+ import { ICache } from "../types/Cache";
7
+ import { Methods } from "../types/JsonRPC";
8
+
9
+ /**
10
+ * A higher-order function that enhances a class method with caching capabilities.
11
+ * It intercepts RPC requests made through the client and caches their responses
12
+ * based on the provided cache implementation.
13
+ *
14
+ * @param client The CoreClient instance to enhance with caching.
15
+ * @param cache The cache implementation (ICache) to use for storing and retrieving responses.
16
+ * @returns The enhanced CoreClient instance with caching enabled.
17
+ */
18
+ export function withCache(client: CoreClient, cache: ICache) {
19
+ watchBlockNumber(client, {
20
+ onBlockNumber: (blockNumber) => {
21
+ cache.setBlockNumber(blockNumber);
22
+ },
23
+ pollingInterval: 1000,
24
+ });
25
+ // 2. We keep a reference to the original request method
26
+ const originalRequest = client.request;
27
+
28
+ // 3. We replace the client's `request` method
29
+ client.request = (async (args: { method: Methods, params: any }) => {
30
+ const { method, params } = args;
31
+
32
+ const cacheKey = `${client.chain!.id}:${method}:${JSON.stringify(params || [], (_, v) => typeof v === 'bigint' ? v.toString() : v)}`;
33
+
34
+ let result;
35
+ if (method !== "eth_blockNumber") result = cache.get(cacheKey);
36
+
37
+ if (!result) {
38
+ result = await originalRequest(args as any);
39
+ };
40
+
41
+ if (result !== null && result !== undefined && method !== "eth_blockNumber") {
42
+ const ttl = cache.config.ttlPolicies[method] || cache.config.defaultTtl;
43
+ await cache.set(cacheKey, result, ttl);
44
+ }
45
+
46
+ return result;
47
+ }) as any;
48
+
49
+ return client;
50
+ };
package/test.ts DELETED
@@ -1,89 +0,0 @@
1
- import { Address, checksumAddress, http } from "@mentaproject/core";
2
- import { MentaClient, Transaction } from "./src";
3
- import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
4
- import { mainnet } from "@mentaproject/core/chains";
5
- import { MemoryCache } from "./src/structures/Cache";
6
- import { writeFileSync } from "fs";
7
-
8
- const viemAccount = privateKeyToAccount(generatePrivateKey());
9
-
10
- let counter = 0;
11
- let lastSyncedBlock = new Map<Address, bigint>();
12
- let transactions = new Map<Address, Transaction[]>();
13
-
14
- const client = new MentaClient({
15
- account: viemAccount,
16
- transport: http("https://ethereum-rpc.publicnode.com", {
17
- batch: {
18
- batchSize: 50,
19
- wait: 20,
20
- },
21
- onFetchRequest: async (r) => {
22
- // const data = await r.json();
23
- // console.log(data.map(j => `${j.method}(${j.params?.map(p => JSON.stringify(p)).join(', ')})`).join('\n'));
24
- // console.log(`>> ${counter} requests`);
25
-
26
- // console.log(`>> ${r.url}\n>> ${req.map(j => j.method).join(', ')}\n>> ${delay / 1000}s\n`)
27
- },
28
- // onFetchResponse: (r) => console.log(`<<: ${r.url}`),
29
- }),
30
- cache: new MemoryCache(0n, { maxSize: 1000, defaultTtl: 1 }),
31
- persistenceAdapter: {
32
- getLastSyncedBlock: async (accountAddress: Address) => {
33
- return lastSyncedBlock.get(accountAddress) ?? null;
34
- },
35
- getTransactions: async (address: Address, params): Promise<Transaction[]> => {
36
- return transactions.get(address) ?? [];
37
- },
38
- upsertTransactions: async (txs: Transaction[]) => {
39
- console.log("upsertTransactions", txs.length);
40
- for (const transaction of txs) {
41
- const fromAddress = transaction.from?.address;
42
- const toAddress = transaction.to?.address;
43
-
44
- if (fromAddress) {
45
- const checksumed = checksumAddress(fromAddress);
46
-
47
- if (!transactions.has(checksumed)) {
48
- transactions.set(checksumed, []);
49
- };
50
-
51
- const jsonTransaction = await transaction.toJSON(0);
52
- const parsed = client.transactions.parse(jsonTransaction);
53
-
54
- transactions.get(checksumed)?.push(parsed);
55
- };
56
-
57
- if (toAddress) {
58
- const checksumed = checksumAddress(toAddress);
59
-
60
- if (!transactions.has(checksumed)) {
61
- transactions.set(checksumed, []);
62
- };
63
-
64
- const jsonTransaction = await transaction.toJSON(0);
65
- const parsed = client.transactions.parse(jsonTransaction);
66
-
67
- transactions.get(checksumed)?.push(parsed);
68
- }
69
- }
70
- },
71
- setLastSyncedBlock: async (accountAddress: Address, blockNumber: bigint) => {
72
- lastSyncedBlock.set(accountAddress, blockNumber);
73
- },
74
- },
75
- chain: mainnet
76
- });
77
-
78
- (async () => {
79
- console.log("Watching for new blocks and block numbers...");
80
-
81
- const account = client.accounts.get("0x53b9B72DC6f96Eb4B54143B211B22e2548e4cf5c");
82
- await account.syncTransactions(50)
83
- const res = await account.transactions({ limit: 50 });
84
- console.log(res.map(r => r.blockNumber));
85
- const json = await Promise.all(res.map(async t => await t.toJSON(0)));
86
-
87
- writeFileSync("transactions.json", JSON.stringify(json, null, 2));
88
- console.log("Done");
89
- })();