@mentaproject/client 0.0.10 → 0.1.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.
@@ -1,13 +1,14 @@
1
- import type { CoreClient, CoreClientConfig } from "@mentaproject/core/types";
1
+ import type { CoreClient } from "@mentaproject/core/types";
2
2
  import { BlockManager } from "../managers/BlockManager";
3
3
  import { TransactionManager } from "../managers/TransactionManager";
4
4
  import { ContractManager } from "../managers/ContractManager";
5
5
  import { AccountManager } from "../managers/AccountManager";
6
+ import { MentaClientConfig } from "src/types/MentaClient";
6
7
  export declare class MentaClient {
7
8
  rpcClient: CoreClient;
8
9
  blocks: BlockManager;
9
10
  transactions: TransactionManager;
10
11
  contracts: ContractManager;
11
12
  accounts: AccountManager;
12
- constructor(params: CoreClientConfig);
13
+ constructor(params: MentaClientConfig);
13
14
  }
@@ -3,9 +3,12 @@ import { BlockManager } from "../managers/BlockManager";
3
3
  import { TransactionManager } from "../managers/TransactionManager";
4
4
  import { ContractManager } from "../managers/ContractManager";
5
5
  import { AccountManager } from "../managers/AccountManager";
6
+ import { withCache } from "src/utils/withCache";
6
7
  export class MentaClient {
7
8
  constructor(params) {
8
9
  this.rpcClient = createClient(params);
10
+ if (params.cache)
11
+ this.rpcClient = withCache(this.rpcClient, params.cache);
9
12
  this.blocks = new BlockManager(this.rpcClient);
10
13
  this.transactions = new TransactionManager(this.rpcClient);
11
14
  this.contracts = new ContractManager(this.rpcClient);
@@ -0,0 +1,17 @@
1
+ import { Methods } from "./JsonRPC";
2
+ export interface ICacheEntry<T> {
3
+ value: T;
4
+ expiry: bigint;
5
+ }
6
+ export interface ICacheConfig {
7
+ maxSize: number;
8
+ defaultTtl: number;
9
+ ttlPolicies: Partial<Record<Methods, number>>;
10
+ }
11
+ export interface ICache {
12
+ config: ICacheConfig;
13
+ setBlockNumber(blockNumber: bigint): void;
14
+ get<T>(key: string): Promise<T | undefined>;
15
+ set<T>(key: string, value: T, ttl?: number): Promise<void>;
16
+ has(key: string): Promise<boolean>;
17
+ }
@@ -0,0 +1,2 @@
1
+ ;
2
+ export {};
@@ -0,0 +1,5 @@
1
+ export type StandardMethods = "eth_accounts" | "eth_blobBaseFee" | "eth_blockNumber" | "eth_call" | "eth_chainId" | "eth_createAccessList" | "eth_estimateGas" | "eth_feeHistory" | "eth_gasPrice" | "eth_getBalance" | "eth_getBlockByHash" | "eth_getBlockByNumber" | "eth_getBlockReceipts" | "eth_getBlockTransactionCountByHash" | "eth_getBlockTransactionCountByNumber" | "eth_getCode" | "eth_getLogs" | "eth_getProof" | "eth_getStorageAt" | "eth_getTransactionByBlockHashAndIndex" | "eth_getTransactionByBlockNumberAndIndex" | "eth_getTransactionByHash" | "eth_getTransactionCount" | "eth_getTransactionReceipt" | "eth_getUncleByBlockHashAndIndex" | "eth_getUncleByBlockNumberAndIndex" | "eth_getUncleCountByBlockHash" | "eth_getUncleCountByBlockNumber" | "eth_getWork" | "eth_hashrate" | "eth_maxPriorityFeePerGas" | "eth_mining" | "eth_protocolVersion" | "eth_sendRawTransaction" | "eth_simulateV1" | "eth_submitWork" | "eth_syncing";
2
+ export type filterMethods = "eth_getFilterChanges" | "eth_getFilterLogs" | "eth_newBlockFilter" | "eth_newFilter" | "eth_uninstallFilter";
3
+ export type subscriptionMethods = "eth_subscribe" | "eth_unsubscribe";
4
+ export type traceMethods = "trace_block" | "trace_call" | "trace_callMany" | "trace_filter" | "trace_transaction";
5
+ export type Methods = StandardMethods | filterMethods | subscriptionMethods | traceMethods;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ import { CoreClientConfig } from "src/structures";
2
+ import { ICache } from "./Cache";
3
+ export interface CacheConfig {
4
+ ttl_policies: Record<string, number>;
5
+ }
6
+ export interface MentaClientConfig extends CoreClientConfig {
7
+ cache: ICache;
8
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ import { ICache, ICacheConfig } from '../types/Cache';
2
+ export declare class Cache implements ICache {
3
+ private cache;
4
+ config: ICacheConfig;
5
+ private blockNumber;
6
+ constructor(blockNumber: bigint, options?: Partial<ICacheConfig>);
7
+ protected isExpired(expiry: bigint): boolean;
8
+ setBlockNumber(blockNumber: bigint): void;
9
+ set<T>(key: string, value: T, ttl?: number): Promise<void>;
10
+ get<T>(key: string): Promise<T | undefined>;
11
+ has(key: string): Promise<boolean>;
12
+ private evict;
13
+ }
@@ -0,0 +1,57 @@
1
+ export class Cache {
2
+ constructor(blockNumber, options) {
3
+ this.cache = new Map();
4
+ this.blockNumber = blockNumber;
5
+ this.config = {
6
+ maxSize: 500,
7
+ defaultTtl: 1,
8
+ ttlPolicies: options?.ttlPolicies || {},
9
+ ...options,
10
+ };
11
+ }
12
+ ;
13
+ isExpired(expiry) {
14
+ return this.blockNumber > expiry;
15
+ }
16
+ ;
17
+ setBlockNumber(blockNumber) {
18
+ this.blockNumber = blockNumber;
19
+ }
20
+ async set(key, value, ttl = this.config.defaultTtl) {
21
+ if (this.cache.has(key)) {
22
+ this.cache.delete(key);
23
+ }
24
+ if (this.cache.size >= this.config.maxSize)
25
+ this.evict();
26
+ const expiry = this.blockNumber + BigInt(ttl);
27
+ this.cache.set(key, { value, expiry });
28
+ }
29
+ async get(key) {
30
+ const entry = this.cache.get(key);
31
+ if (!entry)
32
+ return undefined;
33
+ if (this.isExpired(entry.expiry)) {
34
+ this.cache.delete(key);
35
+ return undefined;
36
+ }
37
+ this.cache.delete(key);
38
+ this.cache.set(key, entry);
39
+ return entry.value;
40
+ }
41
+ async has(key) {
42
+ const entry = this.cache.get(key);
43
+ if (!entry)
44
+ return false;
45
+ if (this.isExpired(entry.expiry)) {
46
+ this.cache.delete(key);
47
+ return false;
48
+ }
49
+ return true;
50
+ }
51
+ evict() {
52
+ const oldestKey = this.cache.keys().next().value;
53
+ if (!oldestKey)
54
+ return;
55
+ this.cache.delete(oldestKey);
56
+ }
57
+ }
@@ -0,0 +1,3 @@
1
+ import { CoreClient } from "src/types";
2
+ import { ICache } from "src/types/Cache";
3
+ export declare function withCache(client: CoreClient, cache: ICache): CoreClient;
@@ -0,0 +1,23 @@
1
+ import { watchBlockNumber } from "@mentaproject/core/actions";
2
+ export function withCache(client, cache) {
3
+ watchBlockNumber(client, {
4
+ onBlockNumber: (blockNumber) => {
5
+ cache.setBlockNumber(blockNumber);
6
+ }
7
+ });
8
+ // 2. On garde une référence à la méthode request originale
9
+ const originalRequest = client.request;
10
+ // 3. On remplace la méthode `request` du client
11
+ client.request = (async (args) => {
12
+ const { method, params } = args;
13
+ const cacheKey = `${client.chain.id}:${method}:${JSON.stringify(params || [])}`;
14
+ if (await cache.has(cacheKey) && method !== "eth_blockNumber")
15
+ return cache.get(cacheKey);
16
+ const result = await originalRequest(args);
17
+ if (result !== null && result !== undefined)
18
+ await cache.set(cacheKey, result, cache.config.defaultTtl);
19
+ return result;
20
+ });
21
+ return client;
22
+ }
23
+ ;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentaproject/client",
3
- "version": "0.0.10",
3
+ "version": "0.1.1",
4
4
  "description": "High level EVM library used into the Menta App to facilitate Blockchain interactions. ",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -30,7 +30,7 @@
30
30
  "license": "ISC",
31
31
  "dependencies": {
32
32
  "@mentaproject/contracts": "^0.0.9",
33
- "@mentaproject/core": "^0.0.4",
33
+ "@mentaproject/core": "^0.0.5",
34
34
  "@shazow/whatsabi": "^0.21.1"
35
35
  },
36
36
  "devDependencies": {
package/test.ts CHANGED
@@ -2,26 +2,30 @@ import { http } from "viem";
2
2
  import { MentaClient } from "./src";
3
3
  import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
4
4
  import { mainnet } from "@mentaproject/core/chains";
5
+ import { withCache } from "./src/utils/withCache";
6
+ import { MemoryCache } from "./src/utils/Cache";
5
7
 
6
8
  import { writeFileSync } from "fs";
7
9
 
8
10
  const viemAccount = privateKeyToAccount(generatePrivateKey());
9
11
 
10
12
  let time = Date.now();
11
-
13
+ let counter = 0;
12
14
  const client = new MentaClient({
13
15
  account: viemAccount,
14
16
  transport: http("https://red-nameless-panorama.quiknode.pro/0f30278c4c66afdc9f8679975e13cd5a92c7d937/", {
15
17
  batch: {
16
- batchSize: 6,
18
+ batchSize: 5,
17
19
  wait: 500,
18
20
  waitAsRateLimit: true
19
21
  },
20
22
  onFetchRequest: async (r) => {
21
- const delay = Date.now() - time;
22
- time = Date.now();
23
+ const req = await r.json();
24
+
25
+ counter = counter + req.length
26
+ // console.log(`>> ${counter} requests`);
23
27
 
24
- console.log(`>> ${r.url}\n>> ${(await r.json()).map(j => j.method).join(', ')}\n>> ${delay / 1000}s\n`)
28
+ // console.log(`>> ${r.url}\n>> ${req.map(j => j.method).join(', ')}\n>> ${delay / 1000}s\n`)
25
29
  },
26
30
  // onFetchResponse: (r) => console.log(`<<: ${r.url}`),
27
31
  }),
@@ -29,8 +33,18 @@ const client = new MentaClient({
29
33
  });
30
34
 
31
35
  (async () => {
36
+ const lastBlock = await client.blocks.get({ blockTag: "latest" });
37
+
38
+ const cache = new MemoryCache(lastBlock.number!, { maxSize: 1000, defaultTtl: 1 });
39
+ client.rpcClient = withCache(client.rpcClient, cache);
40
+
32
41
  const firstTx = await client.transactions.get({ hash: "0x48e6f527c659c6e2c5a504864fc269599047e151dd35fc509387599fe4b82370" });
33
42
  const json = await firstTx.toJSON(1);
34
43
 
44
+ const elapsed = Date.now() - time;
45
+
46
+ console.log(`Elapsed time: ${elapsed / 1000}s`);
47
+ console.log(`Effective RPC calls: ${counter}`);
48
+
35
49
  writeFileSync("./test.json", JSON.stringify(json, null, 2));
36
50
  })()