@mentaproject/client 0.1.14 → 0.1.16
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/managers/PersistenceManager.d.ts +5 -4
- package/dist/managers/PersistenceManager.js +7 -5
- package/dist/structures/Account.d.ts +4 -3
- package/dist/structures/Account.js +15 -17
- package/dist/types/Account.d.ts +1 -0
- package/dist/types/PersistenceAdapter.d.ts +2 -1
- package/dist/utils/withCache.js +1 -1
- package/package.json +2 -2
- package/test.ts +54 -9
- package/tests/structures/Account.test.ts +66 -26
- package/tests/structures/Block.test.ts +2 -2
- package/tests/structures/Transaction.test.ts +3 -3
- package/tests/utils/toJSON.test.ts +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Address } from '@mentaproject/core/types';
|
|
2
2
|
import { IPersistenceAdapter } from '../types/PersistenceAdapter';
|
|
3
3
|
import { Account } from '../structures/Account';
|
|
4
|
-
import { MentaClient
|
|
5
|
-
import { GetTransactionsParams } from 'src/types';
|
|
4
|
+
import { MentaClient } from '../structures';
|
|
5
|
+
import { GetTransactionsParams, JSONTransaction } from 'src/types';
|
|
6
6
|
/**
|
|
7
7
|
* Manages the persistence of data, including transactions, using a specified persistence adapter.
|
|
8
8
|
* It handles fetching data from both local storage and remote sources, and synchronizing them.
|
|
@@ -21,12 +21,13 @@ export declare class PersistenceManager {
|
|
|
21
21
|
* @param params - Parameters for retrieving transactions.
|
|
22
22
|
* @returns A promise that resolves to an array of transactions with associated account information.
|
|
23
23
|
*/
|
|
24
|
-
getTransactions(address: Address, params: GetTransactionsParams): Promise<
|
|
24
|
+
getTransactions(address: Address, params: GetTransactionsParams): Promise<JSONTransaction[]>;
|
|
25
25
|
/**
|
|
26
26
|
* Synchronizes an account's transactions from the remote source to the local persistence.
|
|
27
27
|
* It fetches new transactions starting from the last synced block and updates the local storage.
|
|
28
28
|
* @param account - The account whose transactions are to be synchronized.
|
|
29
|
+
* @param limit - The maximum number of transactions to fetch.
|
|
29
30
|
* @returns A promise that resolves when the synchronization is complete.
|
|
30
31
|
*/
|
|
31
|
-
syncTransactions(account: Account,
|
|
32
|
+
syncTransactions(account: Account, limit?: number): Promise<void>;
|
|
32
33
|
}
|
|
@@ -27,16 +27,18 @@ export class PersistenceManager {
|
|
|
27
27
|
* Synchronizes an account's transactions from the remote source to the local persistence.
|
|
28
28
|
* It fetches new transactions starting from the last synced block and updates the local storage.
|
|
29
29
|
* @param account - The account whose transactions are to be synchronized.
|
|
30
|
+
* @param limit - The maximum number of transactions to fetch.
|
|
30
31
|
* @returns A promise that resolves when the synchronization is complete.
|
|
31
32
|
*/
|
|
32
|
-
async syncTransactions(account,
|
|
33
|
-
const lastBlock = toBlock ?? (await getBlockNumber(this.client.rpc));
|
|
33
|
+
async syncTransactions(account, limit = 100000) {
|
|
34
34
|
const lastSyncedBlock = await this.persistenceAdapter.getLastSyncedBlock(account.address);
|
|
35
|
-
const
|
|
35
|
+
const lastBlock = lastSyncedBlock ?? 0n;
|
|
36
|
+
const fromBlock = lastSyncedBlock ? lastSyncedBlock + 1n : (await getBlockNumber(this.client.rpc));
|
|
36
37
|
const newTransactions = await account.transactions({
|
|
37
38
|
fromBlock: fromBlock,
|
|
38
|
-
toBlock: lastBlock
|
|
39
|
-
|
|
39
|
+
toBlock: lastBlock,
|
|
40
|
+
limit: limit
|
|
41
|
+
}, true);
|
|
40
42
|
if (newTransactions.length > 0) {
|
|
41
43
|
await this.persistenceAdapter.upsertTransactions(newTransactions);
|
|
42
44
|
const latestBlock = bigIntMax(...newTransactions.map(t => t.blockNumber || 0n));
|
|
@@ -65,18 +65,19 @@ export declare class Account implements AccountData {
|
|
|
65
65
|
* @param {number} [options.page] - The page number for paginated results.
|
|
66
66
|
* @param {number} [options.offset] - The number of items to skip from the beginning of the result set.
|
|
67
67
|
* @param {'asc' | 'desc'} [options.sort] - The sorting order for transactions based on block number ('asc' for ascending, 'desc' for descending).
|
|
68
|
+
* @param {boolean} [forceFetch=false] - Forces the method to fetch transactions from the remote source even if they are already cached. (mostly used internally)
|
|
68
69
|
* @returns {Promise<Transaction[]>} A promise that resolves to an array of transactions.
|
|
69
70
|
*/
|
|
70
|
-
transactions(params: GetTransactionsParams): Promise<Transaction[]>;
|
|
71
|
+
transactions(params: GetTransactionsParams, forceFetch?: boolean): Promise<Transaction[]>;
|
|
71
72
|
/**
|
|
72
73
|
* Synchronizes the account's transactions with the remote data source using the configured `PersistenceManager`.
|
|
73
74
|
* @description This method initiates a synchronization process to ensure that the local cache of transactions
|
|
74
75
|
* for this account is up-to-date with the blockchain.
|
|
75
|
-
* @param {
|
|
76
|
+
* @param {number} [limit] The maximum number of transactions to synchronize.
|
|
76
77
|
* @returns {Promise<void>} A promise that resolves when the synchronization process is complete.
|
|
77
78
|
* @throws {Error} If the persistence module is not configured.
|
|
78
79
|
*/
|
|
79
|
-
syncTransactions(
|
|
80
|
+
syncTransactions(limit?: number): Promise<void>;
|
|
80
81
|
/**
|
|
81
82
|
* Retrieves transactions involving a specific token. (not implemented yet)
|
|
82
83
|
* @description This method queries the persistence adapter to retrieve transactions involving a specific token.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { fetchByBlockRange, getBalance, getBlockNumber, getCode, getTransaction, getTransactionCount, sendTransaction, traceFilter } from "@mentaproject/core/actions";
|
|
2
|
+
import { Transaction } from "./Transaction";
|
|
2
3
|
import { toHex } from "@mentaproject/core/utils";
|
|
3
4
|
import { getContractType } from "@mentaproject/contracts";
|
|
4
5
|
import { toJSON } from "../utils/toJSON";
|
|
@@ -91,30 +92,31 @@ export class Account {
|
|
|
91
92
|
* @param {number} [options.page] - The page number for paginated results.
|
|
92
93
|
* @param {number} [options.offset] - The number of items to skip from the beginning of the result set.
|
|
93
94
|
* @param {'asc' | 'desc'} [options.sort] - The sorting order for transactions based on block number ('asc' for ascending, 'desc' for descending).
|
|
95
|
+
* @param {boolean} [forceFetch=false] - Forces the method to fetch transactions from the remote source even if they are already cached. (mostly used internally)
|
|
94
96
|
* @returns {Promise<Transaction[]>} A promise that resolves to an array of transactions.
|
|
95
97
|
*/
|
|
96
|
-
async transactions(params) {
|
|
97
|
-
if (!this.persistenceManager) {
|
|
98
|
+
async transactions(params, forceFetch = false) {
|
|
99
|
+
if (!this.persistenceManager || forceFetch) {
|
|
98
100
|
// If persistence is not configured, fetch directly from remote
|
|
99
101
|
const hashes = await this._fetchTransactions(params);
|
|
100
102
|
const transactions = await Promise.all(hashes.map(hash => this.client.transactions.get({ hash })));
|
|
101
103
|
return transactions;
|
|
102
104
|
}
|
|
103
105
|
const cachedTransactions = await this.persistenceManager.getTransactions(this.address, params);
|
|
104
|
-
return cachedTransactions;
|
|
106
|
+
return cachedTransactions.map(t => new Transaction(this.client, t));
|
|
105
107
|
}
|
|
106
108
|
/**
|
|
107
109
|
* Synchronizes the account's transactions with the remote data source using the configured `PersistenceManager`.
|
|
108
110
|
* @description This method initiates a synchronization process to ensure that the local cache of transactions
|
|
109
111
|
* for this account is up-to-date with the blockchain.
|
|
110
|
-
* @param {
|
|
112
|
+
* @param {number} [limit] The maximum number of transactions to synchronize.
|
|
111
113
|
* @returns {Promise<void>} A promise that resolves when the synchronization process is complete.
|
|
112
114
|
* @throws {Error} If the persistence module is not configured.
|
|
113
115
|
*/
|
|
114
|
-
async syncTransactions(
|
|
116
|
+
async syncTransactions(limit = 1000) {
|
|
115
117
|
if (!this.persistenceManager)
|
|
116
118
|
throw new Error("The persistence module is not configured.");
|
|
117
|
-
await this.persistenceManager.syncTransactions(this,
|
|
119
|
+
await this.persistenceManager.syncTransactions(this, limit);
|
|
118
120
|
}
|
|
119
121
|
/**
|
|
120
122
|
* Retrieves transactions involving a specific token. (not implemented yet)
|
|
@@ -138,11 +140,11 @@ export class Account {
|
|
|
138
140
|
async toJSON(depth = 1) {
|
|
139
141
|
return await toJSON({
|
|
140
142
|
obj: {
|
|
141
|
-
|
|
142
|
-
isContract: this.isContract,
|
|
143
|
-
contractType: this.contractType,
|
|
144
|
-
ETHBalance: this.ETHBalance,
|
|
145
|
-
transactionCount: this.transactionCount,
|
|
143
|
+
address: this.address,
|
|
144
|
+
isContract: this.isContract.bind(this),
|
|
145
|
+
contractType: this.contractType.bind(this),
|
|
146
|
+
ETHBalance: this.ETHBalance.bind(this),
|
|
147
|
+
transactionCount: this.transactionCount.bind(this),
|
|
146
148
|
},
|
|
147
149
|
depth
|
|
148
150
|
});
|
|
@@ -166,12 +168,8 @@ export class Account {
|
|
|
166
168
|
toBlock: toHex(toBlock),
|
|
167
169
|
toAddress: this.address
|
|
168
170
|
});
|
|
169
|
-
const traces = outgoing.concat(incoming);
|
|
170
|
-
return traces.map(t =>
|
|
171
|
-
blockNumber: BigInt(t.blockNumber),
|
|
172
|
-
index: t.transactionPosition,
|
|
173
|
-
hash: t.transactionHash
|
|
174
|
-
}));
|
|
171
|
+
const traces = outgoing.concat(incoming).sort((a, b) => a.blockNumber - b.blockNumber);
|
|
172
|
+
return traces.map(t => t.transactionHash);
|
|
175
173
|
};
|
|
176
174
|
return await fetchByBlockRange({
|
|
177
175
|
toBlock: BigInt(toBlock !== undefined ? toBlock : 0),
|
package/dist/types/Account.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Address } from '@mentaproject/core/types';
|
|
2
2
|
import { Transaction } from '../structures/Transaction';
|
|
3
3
|
import { GetTransactionsParams } from './Account';
|
|
4
|
+
import { JSONTransaction } from './Transaction';
|
|
4
5
|
/**
|
|
5
6
|
* @interface IPersistenceAdapter
|
|
6
7
|
* @description Defines the contract for persistence layers, ensuring consistent data storage and retrieval operations.
|
|
@@ -19,7 +20,7 @@ export interface IPersistenceAdapter {
|
|
|
19
20
|
* @param {GetTransactionsFilters} filters - An object containing filters for retrieving transactions, such as account address, block range, pagination, and sort order.
|
|
20
21
|
* @returns {Promise<Transaction[]>} A Promise that resolves with an array of transactions matching the filters.
|
|
21
22
|
*/
|
|
22
|
-
getTransactions(address: Address, params: GetTransactionsParams): Promise<
|
|
23
|
+
getTransactions(address: Address, params: GetTransactionsParams): Promise<JSONTransaction[]>;
|
|
23
24
|
/**
|
|
24
25
|
* @method getLastSyncedBlock
|
|
25
26
|
* @description Retrieves the last synced block number for a given account. This is used to track the synchronization progress of an account's transactions.
|
package/dist/utils/withCache.js
CHANGED
|
@@ -23,7 +23,7 @@ export function withCache(client, cache) {
|
|
|
23
23
|
// 3. We replace the client's `request` method
|
|
24
24
|
client.request = (async (args) => {
|
|
25
25
|
const { method, params } = args;
|
|
26
|
-
const cacheKey = `${client.chain.id}:${method}:${JSON.stringify(params || [])}`;
|
|
26
|
+
const cacheKey = `${client.chain.id}:${method}:${JSON.stringify(params || [], (_, v) => typeof v === 'bigint' ? v.toString() : v)}`;
|
|
27
27
|
let result;
|
|
28
28
|
if (method !== "eth_blockNumber")
|
|
29
29
|
result = cache.get(cacheKey);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentaproject/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
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",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"license": "ISC",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@mentaproject/contracts": "^0.0.9",
|
|
35
|
-
"@mentaproject/core": "^0.5.
|
|
35
|
+
"@mentaproject/core": "^0.5.3",
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.13.0",
|
|
37
37
|
"@shazow/whatsabi": "^0.21.1"
|
|
38
38
|
},
|
package/test.ts
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
|
-
import { http } from "@mentaproject/core";
|
|
2
|
-
import { MentaClient } from "./src";
|
|
1
|
+
import { Address, checksumAddress, http } from "@mentaproject/core";
|
|
2
|
+
import { MentaClient, Transaction } from "./src";
|
|
3
3
|
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
|
|
4
4
|
import { mainnet } from "@mentaproject/core/chains";
|
|
5
5
|
import { MemoryCache } from "./src/structures/Cache";
|
|
6
6
|
import { watchBlockNumber, watchBlocks } from "@mentaproject/core/actions";
|
|
7
7
|
import { writeFileSync } from "fs";
|
|
8
|
+
import { JSONTransaction } from "./src/types";
|
|
8
9
|
|
|
9
10
|
const viemAccount = privateKeyToAccount(generatePrivateKey());
|
|
10
11
|
|
|
11
12
|
let counter = 0;
|
|
13
|
+
let lastSyncedBlock = new Map<Address, bigint>();
|
|
14
|
+
let transactions = new Map<Address, JSONTransaction[]>();
|
|
15
|
+
|
|
12
16
|
const client = new MentaClient({
|
|
13
17
|
account: viemAccount,
|
|
14
18
|
transport: http("https://ethereum-rpc.publicnode.com", {
|
|
15
19
|
batch: {
|
|
16
|
-
batchSize:
|
|
17
|
-
wait:
|
|
20
|
+
batchSize: 50,
|
|
21
|
+
wait: 20,
|
|
18
22
|
},
|
|
19
23
|
onFetchRequest: async (r) => {
|
|
20
|
-
const data = await r.json();
|
|
21
|
-
console.log(data);
|
|
24
|
+
// const data = await r.json();
|
|
25
|
+
// console.log(data.map(j => `${j.method}(${j.params?.map(p => JSON.stringify(p)).join(', ')})`).join('\n'));
|
|
22
26
|
// console.log(`>> ${counter} requests`);
|
|
23
27
|
|
|
24
28
|
// console.log(`>> ${r.url}\n>> ${req.map(j => j.method).join(', ')}\n>> ${delay / 1000}s\n`)
|
|
@@ -26,14 +30,55 @@ const client = new MentaClient({
|
|
|
26
30
|
// onFetchResponse: (r) => console.log(`<<: ${r.url}`),
|
|
27
31
|
}),
|
|
28
32
|
cache: new MemoryCache(0n, { maxSize: 1000, defaultTtl: 1 }),
|
|
33
|
+
persistenceAdapter: {
|
|
34
|
+
getLastSyncedBlock: async (accountAddress: Address) => {
|
|
35
|
+
return lastSyncedBlock.get(accountAddress) ?? null;
|
|
36
|
+
},
|
|
37
|
+
getTransactions: async (address: Address, params) => {
|
|
38
|
+
return transactions.get(address) ?? [];
|
|
39
|
+
},
|
|
40
|
+
upsertTransactions: async (txs: Transaction[]) => {
|
|
41
|
+
console.log("upsertTransactions", txs.length);
|
|
42
|
+
for (const transaction of txs) {
|
|
43
|
+
const fromAddress = transaction.from?.address;
|
|
44
|
+
const toAddress = transaction.to?.address;
|
|
45
|
+
|
|
46
|
+
if (fromAddress) {
|
|
47
|
+
const checksumed = checksumAddress(fromAddress);
|
|
48
|
+
|
|
49
|
+
if (!transactions.has(checksumed)) {
|
|
50
|
+
transactions.set(checksumed, []);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const jsonTransaction = await transaction.toJSON();
|
|
54
|
+
transactions.get(checksumed)?.push(jsonTransaction);
|
|
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();
|
|
65
|
+
transactions.get(checksumed)?.push(jsonTransaction);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
setLastSyncedBlock: async (accountAddress: Address, blockNumber: bigint) => {
|
|
70
|
+
lastSyncedBlock.set(accountAddress, blockNumber);
|
|
71
|
+
},
|
|
72
|
+
},
|
|
29
73
|
chain: mainnet
|
|
30
74
|
});
|
|
31
75
|
|
|
32
76
|
(async () => {
|
|
33
77
|
console.log("Watching for new blocks and block numbers...");
|
|
34
78
|
|
|
35
|
-
const
|
|
36
|
-
|
|
79
|
+
const account = client.accounts.get("0x53b9B72DC6f96Eb4B54143B211B22e2548e4cf5c");
|
|
80
|
+
await account.syncTransactions(20)
|
|
81
|
+
const res = await account.transactions({ limit: 20 });
|
|
37
82
|
|
|
38
|
-
|
|
83
|
+
console.log(res.length);
|
|
39
84
|
})();
|
|
@@ -8,6 +8,7 @@ import { Address, Hash } from '@mentaproject/core/types';
|
|
|
8
8
|
|
|
9
9
|
// Mocking the core actions
|
|
10
10
|
jest.mock('@mentaproject/core/actions', () => ({
|
|
11
|
+
getBlock: jest.fn(),
|
|
11
12
|
getCode: jest.fn(),
|
|
12
13
|
getContractType: jest.fn(),
|
|
13
14
|
sendTransaction: jest.fn(),
|
|
@@ -24,26 +25,27 @@ jest.mock('@mentaproject/contracts', () => ({
|
|
|
24
25
|
getContractType: jest.fn(),
|
|
25
26
|
}));
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
rpc: {},
|
|
30
|
-
transactions: {
|
|
31
|
-
parse: jest.fn((data) => new Transaction(mockMentaClient as any, data)),
|
|
32
|
-
get: jest.fn(),
|
|
33
|
-
},
|
|
34
|
-
} as unknown as MentaClient;
|
|
28
|
+
jest.mock('../../src/structures/MentaClient');
|
|
29
|
+
jest.mock('../../src/managers/PersistenceManager');
|
|
35
30
|
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
syncTransactions: jest.fn(),
|
|
39
|
-
} as unknown as PersistenceManager;
|
|
31
|
+
const MockedMentaClient = MentaClient as jest.MockedClass<typeof MentaClient>;
|
|
32
|
+
const MockedPersistenceManager = PersistenceManager as jest.MockedClass<typeof PersistenceManager>;
|
|
40
33
|
|
|
41
34
|
describe('Account', () => {
|
|
42
35
|
const mockAddress: Address = '0x1234567890123456789012345678901234567890';
|
|
43
36
|
let account: Account;
|
|
37
|
+
let mockMentaClient: jest.Mocked<MentaClient>;
|
|
38
|
+
let mockPersistenceManager: jest.Mocked<PersistenceManager>;
|
|
44
39
|
|
|
45
40
|
beforeEach(() => {
|
|
46
41
|
jest.clearAllMocks();
|
|
42
|
+
mockMentaClient = new MockedMentaClient({} as any) as jest.Mocked<MentaClient>;
|
|
43
|
+
mockMentaClient.transactions = {
|
|
44
|
+
get: jest.fn(),
|
|
45
|
+
parse: jest.fn().mockImplementation((data) => new Transaction(mockMentaClient, data)),
|
|
46
|
+
send: jest.fn(),
|
|
47
|
+
} as any;
|
|
48
|
+
mockPersistenceManager = new MockedPersistenceManager(mockMentaClient, {} as any) as jest.Mocked<PersistenceManager>;
|
|
47
49
|
account = new Account(mockMentaClient, mockAddress, mockPersistenceManager);
|
|
48
50
|
});
|
|
49
51
|
|
|
@@ -82,8 +84,28 @@ describe('Account', () => {
|
|
|
82
84
|
|
|
83
85
|
describe('sendETH', () => {
|
|
84
86
|
it('should send ETH and return a Transaction object', async () => {
|
|
85
|
-
const mockTxHash: Hash = '
|
|
86
|
-
const mockTxData = {
|
|
87
|
+
const mockTxHash: Hash = '0x98765432109876543210987654321098765432109876543210987654321098765';
|
|
88
|
+
const mockTxData: import('@mentaproject/core/types').Transaction = {
|
|
89
|
+
blockHash: '0xblockhash',
|
|
90
|
+
blockNumber: 123n,
|
|
91
|
+
from: mockAddress,
|
|
92
|
+
gas: 21000n,
|
|
93
|
+
gasPrice: 1000000000n,
|
|
94
|
+
hash: mockTxHash,
|
|
95
|
+
input: '0x',
|
|
96
|
+
nonce: 1,
|
|
97
|
+
r: '0xr',
|
|
98
|
+
s: '0xs',
|
|
99
|
+
to: mockAddress,
|
|
100
|
+
transactionIndex: 0,
|
|
101
|
+
type: 'legacy',
|
|
102
|
+
v: 27n,
|
|
103
|
+
value: 1000n,
|
|
104
|
+
accessList: undefined,
|
|
105
|
+
typeHex: null,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
(coreActions.sendTransaction as jest.Mock).mockResolvedValue(mockTxHash);
|
|
87
109
|
|
|
88
110
|
(coreActions.sendTransaction as jest.Mock).mockResolvedValue(mockTxHash);
|
|
89
111
|
(coreActions.getTransaction as jest.Mock).mockResolvedValue(mockTxData);
|
|
@@ -120,27 +142,45 @@ describe('Account', () => {
|
|
|
120
142
|
|
|
121
143
|
describe('transactions', () => {
|
|
122
144
|
it('should fetch transactions from persistence manager if available', async () => {
|
|
123
|
-
const
|
|
124
|
-
(mockPersistenceManager.getTransactions as jest.Mock).mockResolvedValue(
|
|
145
|
+
const mockTxJSONs = [{ hash: '0x1' }];
|
|
146
|
+
(mockPersistenceManager.getTransactions as jest.Mock).mockResolvedValue(mockTxJSONs as any);
|
|
125
147
|
|
|
126
148
|
const result = await account.transactions({});
|
|
127
149
|
expect(mockPersistenceManager.getTransactions).toHaveBeenCalledWith(mockAddress, {});
|
|
128
|
-
expect(result).toEqual(
|
|
150
|
+
expect(result.map(tx => tx.hash)).toEqual(mockTxJSONs.map(tx => tx.hash));
|
|
129
151
|
});
|
|
130
152
|
|
|
131
153
|
it('should fetch transactions from remote if persistence is not configured', async () => {
|
|
132
154
|
const localAccount = new Account(mockMentaClient, mockAddress); // No persistenceManager
|
|
133
155
|
const mockHashes: Hash[] = ['0x1', '0x2'];
|
|
134
|
-
const
|
|
135
|
-
|
|
156
|
+
const mockBaseTx = {
|
|
157
|
+
blockHash: '0xblockhash',
|
|
158
|
+
blockNumber: 123n,
|
|
159
|
+
from: mockAddress,
|
|
160
|
+
gas: 21000n,
|
|
161
|
+
gasPrice: 1000000000n,
|
|
162
|
+
input: '0x',
|
|
163
|
+
nonce: 1,
|
|
164
|
+
r: '0xr',
|
|
165
|
+
s: '0xs',
|
|
166
|
+
to: mockAddress,
|
|
167
|
+
transactionIndex: 0,
|
|
168
|
+
type: 'legacy',
|
|
169
|
+
v: 27n,
|
|
170
|
+
value: 1000n,
|
|
171
|
+
accessList: undefined,
|
|
172
|
+
typeHex: null,
|
|
173
|
+
};
|
|
174
|
+
const mockTx1 = new Transaction(mockMentaClient, { ...mockBaseTx, hash: '0x1' } as any);
|
|
175
|
+
const mockTx2 = new Transaction(mockMentaClient, { ...mockBaseTx, hash: '0x2' } as any);
|
|
136
176
|
|
|
137
177
|
jest.spyOn(localAccount as any, '_fetchTransactions').mockResolvedValue(mockHashes);
|
|
138
|
-
(mockMentaClient.transactions.get as jest.Mock).mockImplementation(({ hash }) => {
|
|
139
|
-
if (hash === '0x1') return
|
|
140
|
-
if (hash === '0x2') return
|
|
141
|
-
return
|
|
178
|
+
(mockMentaClient.transactions.get as jest.Mock).mockImplementation(async ({ hash }) => {
|
|
179
|
+
if (hash === '0x1') return mockTx1;
|
|
180
|
+
if (hash === '0x2') return mockTx2;
|
|
181
|
+
return null as any;
|
|
142
182
|
});
|
|
143
|
-
|
|
183
|
+
|
|
144
184
|
const result = await localAccount.transactions({});
|
|
145
185
|
expect(localAccount['_fetchTransactions']).toHaveBeenCalled();
|
|
146
186
|
expect(mockMentaClient.transactions.get).toHaveBeenCalledTimes(2);
|
|
@@ -150,8 +190,8 @@ describe('Account', () => {
|
|
|
150
190
|
|
|
151
191
|
describe('syncTransactions', () => {
|
|
152
192
|
it('should call syncTransactions on the persistence manager', async () => {
|
|
153
|
-
await account.syncTransactions(
|
|
154
|
-
expect(mockPersistenceManager.syncTransactions).toHaveBeenCalledWith(account,
|
|
193
|
+
await account.syncTransactions(1);
|
|
194
|
+
expect(mockPersistenceManager.syncTransactions).toHaveBeenCalledWith(account, 1);
|
|
155
195
|
});
|
|
156
196
|
|
|
157
197
|
it('should throw an error if persistence manager is not configured', async () => {
|
|
@@ -2,8 +2,8 @@ import { Block } from '../../src/structures/Block';
|
|
|
2
2
|
import { MentaClient } from '../../src/structures/MentaClient';
|
|
3
3
|
import { Account } from '../../src/structures/Account';
|
|
4
4
|
import { Transaction } from '../../src/structures/Transaction';
|
|
5
|
-
import { IBlockData } from '../../src/types/Block';
|
|
6
5
|
import { Address, Hash } from '@mentaproject/core/types';
|
|
6
|
+
import { BlockData } from '../../src/types';
|
|
7
7
|
|
|
8
8
|
// Mocking MentaClient
|
|
9
9
|
const mockMentaClient = {
|
|
@@ -21,7 +21,7 @@ describe('Block', () => {
|
|
|
21
21
|
const mockMinerAddress: Address = '0xminer123';
|
|
22
22
|
const mockTxHash: Hash = '0xhash123';
|
|
23
23
|
|
|
24
|
-
const mockBlockData:
|
|
24
|
+
const mockBlockData: BlockData = {
|
|
25
25
|
number: 123n,
|
|
26
26
|
hash: '0xblockhash',
|
|
27
27
|
parentHash: '0xparenthash',
|
|
@@ -3,7 +3,7 @@ import { MentaClient } from '../../src/structures/MentaClient';
|
|
|
3
3
|
import { Account } from '../../src/structures/Account';
|
|
4
4
|
import { Block } from '../../src/structures/Block';
|
|
5
5
|
import * as coreActions from '@mentaproject/core/actions';
|
|
6
|
-
import { RawTransaction, Hash, Address } from '@mentaproject/core/types';
|
|
6
|
+
import { Transaction as RawTransaction, Hash, Address } from '@mentaproject/core/types';
|
|
7
7
|
|
|
8
8
|
// Mocking core actions
|
|
9
9
|
jest.mock('@mentaproject/core/actions', () => ({
|
|
@@ -83,8 +83,8 @@ describe('Transaction', () => {
|
|
|
83
83
|
|
|
84
84
|
expect(coreActions.traceTransaction).toHaveBeenCalledWith(mockMentaClient.rpc, mockTxHash);
|
|
85
85
|
expect(calls).toHaveLength(1);
|
|
86
|
-
expect(calls[0].value).toBe(100n);
|
|
87
|
-
expect(calls[0].from).toBeInstanceOf(Account);
|
|
86
|
+
expect(calls![0].value).toBe(100n);
|
|
87
|
+
expect(calls![0].from).toBeInstanceOf(Account);
|
|
88
88
|
});
|
|
89
89
|
});
|
|
90
90
|
|