@mentaproject/client 0.0.9 → 0.1.0
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.
- package/dist/structures/Block.js +3 -1
- package/dist/types/Cache.d.ts +17 -0
- package/dist/types/Cache.js +2 -0
- package/dist/types/JsonRPC.d.ts +5 -0
- package/dist/types/JsonRPC.js +1 -0
- package/dist/types/MentaClient.d.ts +8 -0
- package/dist/types/MentaClient.js +1 -0
- package/dist/utils/Cache.d.ts +13 -0
- package/dist/utils/Cache.js +57 -0
- package/dist/utils/withCache.d.ts +3 -0
- package/dist/utils/withCache.js +23 -0
- package/package.json +3 -3
- package/test.ts +27 -5
package/dist/structures/Block.js
CHANGED
|
@@ -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,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 @@
|
|
|
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,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
|
|
3
|
+
"version": "0.1.0",
|
|
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",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"author": "@mentaproject",
|
|
30
30
|
"license": "ISC",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@mentaproject/contracts": "^0.0.
|
|
33
|
-
"@mentaproject/core": "^0.0.
|
|
32
|
+
"@mentaproject/contracts": "^0.0.9",
|
|
33
|
+
"@mentaproject/core": "^0.0.5",
|
|
34
34
|
"@shazow/whatsabi": "^0.21.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
package/test.ts
CHANGED
|
@@ -2,27 +2,49 @@ 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
|
|
|
12
|
+
let time = Date.now();
|
|
13
|
+
let counter = 0;
|
|
10
14
|
const client = new MentaClient({
|
|
11
15
|
account: viemAccount,
|
|
12
16
|
transport: http("https://red-nameless-panorama.quiknode.pro/0f30278c4c66afdc9f8679975e13cd5a92c7d937/", {
|
|
13
17
|
batch: {
|
|
14
|
-
batchSize:
|
|
15
|
-
wait:
|
|
18
|
+
batchSize: 5,
|
|
19
|
+
wait: 500,
|
|
20
|
+
waitAsRateLimit: true
|
|
21
|
+
},
|
|
22
|
+
onFetchRequest: async (r) => {
|
|
23
|
+
const req = await r.json();
|
|
24
|
+
|
|
25
|
+
counter = counter + req.length
|
|
26
|
+
// console.log(`>> ${counter} requests`);
|
|
27
|
+
|
|
28
|
+
// console.log(`>> ${r.url}\n>> ${req.map(j => j.method).join(', ')}\n>> ${delay / 1000}s\n`)
|
|
16
29
|
},
|
|
17
|
-
onFetchRequest: (r) => console.log(`>>: ${r.url}`),
|
|
18
30
|
// onFetchResponse: (r) => console.log(`<<: ${r.url}`),
|
|
19
31
|
}),
|
|
20
32
|
chain: mainnet
|
|
21
33
|
});
|
|
22
34
|
|
|
23
35
|
(async () => {
|
|
24
|
-
const
|
|
25
|
-
|
|
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
|
+
|
|
41
|
+
const firstTx = await client.transactions.get({ hash: "0x48e6f527c659c6e2c5a504864fc269599047e151dd35fc509387599fe4b82370" });
|
|
42
|
+
const json = await firstTx.toJSON(1);
|
|
43
|
+
|
|
44
|
+
const elapsed = Date.now() - time;
|
|
45
|
+
|
|
46
|
+
console.log(`Elapsed time: ${elapsed / 1000}s`);
|
|
47
|
+
console.log(`Effective RPC calls: ${counter}`);
|
|
26
48
|
|
|
27
49
|
writeFileSync("./test.json", JSON.stringify(json, null, 2));
|
|
28
50
|
})()
|