2021-08-12 09:58:13 +00:00
|
|
|
//
|
|
|
|
// Copyright 2021 Vulcanize, Inc.
|
|
|
|
//
|
|
|
|
|
2021-05-28 11:26:40 +00:00
|
|
|
import assert from 'assert';
|
2021-07-06 11:25:11 +00:00
|
|
|
|
2022-09-09 11:43:01 +00:00
|
|
|
import { Cache } from '@cerc-io/cache';
|
2023-10-23 03:53:20 +00:00
|
|
|
import { EthClient as EthClientInterface } from '@cerc-io/util';
|
2021-05-28 11:26:40 +00:00
|
|
|
|
|
|
|
import ethQueries from './eth-queries';
|
2021-06-01 12:43:41 +00:00
|
|
|
import { padKey } from './utils';
|
2021-07-07 05:40:10 +00:00
|
|
|
import { GraphQLClient, GraphQLConfig } from './graphql-client';
|
2021-06-08 10:37:52 +00:00
|
|
|
|
2022-01-17 09:48:44 +00:00
|
|
|
export interface Config extends GraphQLConfig {
|
2021-06-08 10:37:52 +00:00
|
|
|
cache: Cache | undefined;
|
2021-06-04 06:34:12 +00:00
|
|
|
}
|
2021-05-28 11:26:40 +00:00
|
|
|
|
2021-06-04 06:34:12 +00:00
|
|
|
interface Vars {
|
|
|
|
blockHash: string;
|
2022-11-03 08:44:58 +00:00
|
|
|
blockNumber?: string;
|
2021-07-12 11:36:33 +00:00
|
|
|
contract?: string;
|
2021-06-04 06:34:12 +00:00
|
|
|
slot?: string;
|
2022-10-05 09:45:00 +00:00
|
|
|
addresses?: string[];
|
2021-06-04 06:34:12 +00:00
|
|
|
}
|
|
|
|
|
2023-10-23 03:53:20 +00:00
|
|
|
export class EthClient implements EthClientInterface {
|
2021-07-06 11:25:11 +00:00
|
|
|
_graphqlClient: GraphQLClient;
|
2021-06-08 10:37:52 +00:00
|
|
|
_cache: Cache | undefined;
|
2021-05-28 11:26:40 +00:00
|
|
|
|
2021-06-04 06:34:12 +00:00
|
|
|
constructor (config: Config) {
|
2021-06-08 10:37:52 +00:00
|
|
|
const { gqlEndpoint, gqlSubscriptionEndpoint, cache } = config;
|
|
|
|
|
2021-05-28 11:26:40 +00:00
|
|
|
assert(gqlEndpoint, 'Missing gql endpoint');
|
2021-06-08 10:37:52 +00:00
|
|
|
|
2021-07-06 11:25:11 +00:00
|
|
|
this._graphqlClient = new GraphQLClient({ gqlEndpoint, gqlSubscriptionEndpoint });
|
2021-05-28 11:26:40 +00:00
|
|
|
|
|
|
|
this._cache = cache;
|
|
|
|
}
|
|
|
|
|
2021-06-04 06:34:12 +00:00
|
|
|
async getStorageAt ({ blockHash, contract, slot }: { blockHash: string, contract: string, slot: string }): Promise<{ value: string, proof: { data: string } }> {
|
2021-06-01 12:43:41 +00:00
|
|
|
slot = `0x${padKey(slot)}`;
|
|
|
|
|
2022-09-01 08:47:43 +00:00
|
|
|
console.time(`time:eth-client#getStorageAt-${JSON.stringify({ blockHash, contract, slot })}`);
|
2021-06-01 12:43:41 +00:00
|
|
|
const result = await this._getCachedOrFetch('getStorageAt', { blockHash, contract, slot });
|
2022-09-01 08:47:43 +00:00
|
|
|
console.timeEnd(`time:eth-client#getStorageAt-${JSON.stringify({ blockHash, contract, slot })}`);
|
2021-05-31 08:36:48 +00:00
|
|
|
const { getStorageAt: { value, cid, ipldBlock } } = result;
|
|
|
|
|
2021-06-01 12:43:41 +00:00
|
|
|
return {
|
|
|
|
value,
|
|
|
|
proof: {
|
|
|
|
// TODO: Return proof only if requested.
|
|
|
|
data: JSON.stringify({
|
|
|
|
blockHash,
|
|
|
|
account: {
|
|
|
|
address: contract,
|
|
|
|
storage: {
|
|
|
|
cid,
|
|
|
|
ipldBlock
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
};
|
2021-05-28 11:26:40 +00:00
|
|
|
}
|
|
|
|
|
2021-08-04 13:12:59 +00:00
|
|
|
async getBlockWithTransactions ({ blockNumber, blockHash }: { blockNumber?: number, blockHash?: string }): Promise<any> {
|
2022-09-01 08:47:43 +00:00
|
|
|
console.time(`time:eth-client#getBlockWithTransactions-${JSON.stringify({ blockNumber, blockHash })}`);
|
|
|
|
const result = await this._graphqlClient.query(
|
2021-08-04 13:12:59 +00:00
|
|
|
ethQueries.getBlockWithTransactions,
|
|
|
|
{
|
|
|
|
blockNumber: blockNumber?.toString(),
|
|
|
|
blockHash
|
|
|
|
}
|
|
|
|
);
|
2022-09-01 08:47:43 +00:00
|
|
|
console.timeEnd(`time:eth-client#getBlockWithTransactions-${JSON.stringify({ blockNumber, blockHash })}`);
|
|
|
|
|
|
|
|
return result;
|
2021-06-25 11:05:47 +00:00
|
|
|
}
|
|
|
|
|
2021-12-03 10:53:11 +00:00
|
|
|
async getBlocks ({ blockNumber, blockHash }: { blockNumber?: number, blockHash?: string }): Promise<any> {
|
2022-09-01 08:47:43 +00:00
|
|
|
console.time(`time:eth-client#getBlocks-${JSON.stringify({ blockNumber, blockHash })}`);
|
|
|
|
const result = await this._graphqlClient.query(
|
2021-12-03 10:53:11 +00:00
|
|
|
ethQueries.getBlocks,
|
|
|
|
{
|
2022-06-08 06:43:52 +00:00
|
|
|
blockNumber: blockNumber?.toString(),
|
2021-12-03 10:53:11 +00:00
|
|
|
blockHash
|
|
|
|
}
|
|
|
|
);
|
2022-09-01 08:47:43 +00:00
|
|
|
console.timeEnd(`time:eth-client#getBlocks-${JSON.stringify({ blockNumber, blockHash })}`);
|
|
|
|
|
|
|
|
return result;
|
2021-10-20 10:36:03 +00:00
|
|
|
}
|
|
|
|
|
2021-11-17 12:44:02 +00:00
|
|
|
async getFullBlocks ({ blockNumber, blockHash }: { blockNumber?: number, blockHash?: string }): Promise<any> {
|
2022-09-01 08:47:43 +00:00
|
|
|
console.time(`time:eth-client#getFullBlocks-${JSON.stringify({ blockNumber, blockHash })}`);
|
|
|
|
const result = await this._graphqlClient.query(
|
2021-11-17 12:44:02 +00:00
|
|
|
ethQueries.getFullBlocks,
|
|
|
|
{
|
2022-06-08 06:43:52 +00:00
|
|
|
blockNumber: blockNumber?.toString(),
|
2021-11-17 12:44:02 +00:00
|
|
|
blockHash
|
|
|
|
}
|
|
|
|
);
|
2022-09-01 08:47:43 +00:00
|
|
|
console.timeEnd(`time:eth-client#getFullBlocks-${JSON.stringify({ blockNumber, blockHash })}`);
|
|
|
|
|
|
|
|
return result;
|
2021-11-17 12:44:02 +00:00
|
|
|
}
|
|
|
|
|
2022-11-03 08:44:58 +00:00
|
|
|
async getFullTransaction (txHash: string, blockNumber?: number): Promise<any> {
|
|
|
|
console.time(`time:eth-client#getFullTransaction-${JSON.stringify({ txHash, blockNumber })}`);
|
2022-09-01 08:47:43 +00:00
|
|
|
const result = this._graphqlClient.query(
|
2021-12-29 07:51:39 +00:00
|
|
|
ethQueries.getFullTransaction,
|
|
|
|
{
|
2022-11-03 08:44:58 +00:00
|
|
|
txHash,
|
|
|
|
blockNumber: blockNumber?.toString()
|
2021-12-29 07:51:39 +00:00
|
|
|
}
|
|
|
|
);
|
2022-11-03 08:44:58 +00:00
|
|
|
console.timeEnd(`time:eth-client#getFullTransaction-${JSON.stringify({ txHash, blockNumber })}`);
|
2022-09-01 08:47:43 +00:00
|
|
|
|
|
|
|
return result;
|
2021-12-29 07:51:39 +00:00
|
|
|
}
|
|
|
|
|
2021-10-26 12:06:21 +00:00
|
|
|
async getBlockByHash (blockHash?: string): Promise<any> {
|
2022-09-01 08:47:43 +00:00
|
|
|
console.time(`time:eth-client#getBlockByHash-${blockHash}`);
|
2022-07-04 10:06:47 +00:00
|
|
|
const result = await this._graphqlClient.query(ethQueries.getBlockByHash, { blockHash });
|
2022-09-01 08:47:43 +00:00
|
|
|
console.timeEnd(`time:eth-client#getBlockByHash-${blockHash}`);
|
2021-10-22 09:50:11 +00:00
|
|
|
|
2022-07-04 10:06:47 +00:00
|
|
|
return {
|
|
|
|
block: {
|
|
|
|
...result.block,
|
|
|
|
number: parseInt(result.block.number, 16),
|
|
|
|
timestamp: parseInt(result.block.timestamp, 16)
|
|
|
|
}
|
|
|
|
};
|
2021-07-23 13:52:55 +00:00
|
|
|
}
|
|
|
|
|
2023-10-26 11:55:56 +00:00
|
|
|
// TODO: Support filtering logs using topics
|
2021-06-04 06:34:12 +00:00
|
|
|
async getLogs (vars: Vars): Promise<any> {
|
2022-09-01 08:47:43 +00:00
|
|
|
console.time(`time:eth-client#getLogs-${JSON.stringify(vars)}`);
|
2021-05-31 08:36:48 +00:00
|
|
|
const result = await this._getCachedOrFetch('getLogs', vars);
|
2022-09-01 08:47:43 +00:00
|
|
|
console.timeEnd(`time:eth-client#getLogs-${JSON.stringify(vars)}`);
|
2021-07-21 07:30:26 +00:00
|
|
|
const {
|
2022-07-22 07:47:56 +00:00
|
|
|
getLogs
|
2021-07-21 07:30:26 +00:00
|
|
|
} = result;
|
|
|
|
|
2022-07-22 07:47:56 +00:00
|
|
|
return { logs: getLogs };
|
2021-07-14 12:30:26 +00:00
|
|
|
}
|
|
|
|
|
2023-10-23 10:40:09 +00:00
|
|
|
// TODO: Implement
|
|
|
|
// async getLogsForBlockRange(): Promise<any> {}
|
|
|
|
|
2021-06-04 06:34:12 +00:00
|
|
|
async _getCachedOrFetch (queryName: keyof typeof ethQueries, vars: Vars): Promise<any> {
|
2021-05-28 11:26:40 +00:00
|
|
|
const keyObj = {
|
|
|
|
queryName,
|
|
|
|
vars
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check if request cached in db, if cache is enabled.
|
|
|
|
if (this._cache) {
|
2021-06-04 06:34:12 +00:00
|
|
|
const [value, found] = await this._cache.get(keyObj) || [undefined, false];
|
2021-05-28 11:26:40 +00:00
|
|
|
if (found) {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-08 10:37:52 +00:00
|
|
|
// Result not cached or cache disabled, need to perform an upstream GQL query.
|
2021-07-06 11:25:11 +00:00
|
|
|
const result = await this._graphqlClient.query(ethQueries[queryName], vars);
|
2021-05-28 11:26:40 +00:00
|
|
|
|
|
|
|
// Cache the result and return it, if cache is enabled.
|
|
|
|
if (this._cache) {
|
|
|
|
await this._cache.put(keyObj, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|