forked from tornadocash/tornado-cli
Added Echo Service
This commit is contained in:
parent
bf6f5f21ae
commit
3f55d5ca99
@ -13,6 +13,7 @@ import {
|
|||||||
RelayerRegistry__factory,
|
RelayerRegistry__factory,
|
||||||
Aggregator__factory,
|
Aggregator__factory,
|
||||||
Governance__factory,
|
Governance__factory,
|
||||||
|
Echoer__factory,
|
||||||
} from '@tornado/contracts';
|
} from '@tornado/contracts';
|
||||||
import {
|
import {
|
||||||
JsonRpcProvider,
|
JsonRpcProvider,
|
||||||
@ -62,6 +63,7 @@ import {
|
|||||||
TornadoFeeOracle,
|
TornadoFeeOracle,
|
||||||
TokenPriceOracle,
|
TokenPriceOracle,
|
||||||
calculateSnarkProof,
|
calculateSnarkProof,
|
||||||
|
NodeEchoService,
|
||||||
NodeEncryptedNotesService,
|
NodeEncryptedNotesService,
|
||||||
NodeGovernanceService,
|
NodeGovernanceService,
|
||||||
RelayerClient,
|
RelayerClient,
|
||||||
@ -1257,10 +1259,11 @@ export function tornadoProgram() {
|
|||||||
registrySubgraph,
|
registrySubgraph,
|
||||||
tokens,
|
tokens,
|
||||||
routerContract,
|
routerContract,
|
||||||
|
echoContract,
|
||||||
registryContract,
|
registryContract,
|
||||||
['governance.contract.tornadocash.eth']: governanceContract,
|
['governance.contract.tornadocash.eth']: governanceContract,
|
||||||
deployedBlock,
|
deployedBlock,
|
||||||
constants: { GOVERNANCE_BLOCK, REGISTRY_BLOCK, ENCRYPTED_NOTES_BLOCK },
|
constants: { GOVERNANCE_BLOCK, REGISTRY_BLOCK, NOTE_ACCOUNT_BLOCK, ENCRYPTED_NOTES_BLOCK },
|
||||||
} = config;
|
} = config;
|
||||||
|
|
||||||
const provider = getProgramProvider(netId, rpc, config, {
|
const provider = getProgramProvider(netId, rpc, config, {
|
||||||
@ -1301,6 +1304,20 @@ export function tornadoProgram() {
|
|||||||
await registryService.updateEvents();
|
await registryService.updateEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const echoService = new NodeEchoService({
|
||||||
|
netId,
|
||||||
|
provider,
|
||||||
|
graphApi,
|
||||||
|
subgraphName: tornadoSubgraph,
|
||||||
|
Echoer: Echoer__factory.connect(echoContract, provider),
|
||||||
|
deployedBlock: NOTE_ACCOUNT_BLOCK,
|
||||||
|
fetchDataOptions,
|
||||||
|
cacheDirectory: EVENTS_DIR,
|
||||||
|
userDirectory: SAVED_DIR,
|
||||||
|
});
|
||||||
|
|
||||||
|
await echoService.updateEvents();
|
||||||
|
|
||||||
const encryptedNotesService = new NodeEncryptedNotesService({
|
const encryptedNotesService = new NodeEncryptedNotesService({
|
||||||
netId,
|
netId,
|
||||||
provider,
|
provider,
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
import { BaseContract, Provider, EventLog, TransactionResponse, getAddress, Block, ContractEventName } from 'ethers';
|
import { BaseContract, Provider, EventLog, TransactionResponse, getAddress, Block, ContractEventName } from 'ethers';
|
||||||
import type { Tornado, TornadoRouter, TornadoProxyLight, Governance, RelayerRegistry } from '@tornado/contracts';
|
import type {
|
||||||
|
Tornado,
|
||||||
|
TornadoRouter,
|
||||||
|
TornadoProxyLight,
|
||||||
|
Governance,
|
||||||
|
RelayerRegistry,
|
||||||
|
Echoer,
|
||||||
|
} from '@tornado/contracts';
|
||||||
import * as graph from '../graphql';
|
import * as graph from '../graphql';
|
||||||
import {
|
import {
|
||||||
BatchEventsService,
|
BatchEventsService,
|
||||||
@ -21,6 +28,7 @@ import type {
|
|||||||
GovernanceUndelegatedEvents,
|
GovernanceUndelegatedEvents,
|
||||||
RegistersEvents,
|
RegistersEvents,
|
||||||
BaseGraphEvents,
|
BaseGraphEvents,
|
||||||
|
EchoEvents,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export const DEPOSIT = 'deposit';
|
export const DEPOSIT = 'deposit';
|
||||||
@ -454,6 +462,76 @@ export class BaseDepositsService extends BaseEventsService<DepositsEvents | With
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BaseEchoServiceConstructor = {
|
||||||
|
netId: number | string;
|
||||||
|
provider: Provider;
|
||||||
|
graphApi?: string;
|
||||||
|
subgraphName?: string;
|
||||||
|
Echoer: Echoer;
|
||||||
|
deployedBlock?: number;
|
||||||
|
fetchDataOptions?: fetchDataOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class BaseEchoService extends BaseEventsService<EchoEvents> {
|
||||||
|
constructor({
|
||||||
|
netId,
|
||||||
|
provider,
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
Echoer,
|
||||||
|
deployedBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
}: BaseEchoServiceConstructor) {
|
||||||
|
super({ netId, provider, graphApi, subgraphName, contract: Echoer, deployedBlock, fetchDataOptions });
|
||||||
|
}
|
||||||
|
|
||||||
|
getInstanceName(): string {
|
||||||
|
return `echo_${this.netId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getType(): string {
|
||||||
|
return 'Echo';
|
||||||
|
}
|
||||||
|
|
||||||
|
getGraphMethod(): string {
|
||||||
|
return 'getAllGraphEchoEvents';
|
||||||
|
}
|
||||||
|
|
||||||
|
async formatEvents(events: EventLog[]) {
|
||||||
|
return events
|
||||||
|
.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
|
||||||
|
const { who, data } = args;
|
||||||
|
|
||||||
|
if (who && data) {
|
||||||
|
const eventObjects = {
|
||||||
|
blockNumber,
|
||||||
|
logIndex,
|
||||||
|
transactionHash,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...eventObjects,
|
||||||
|
address: who,
|
||||||
|
encryptedAccount: data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((e) => e) as EchoEvents[];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEventsFromGraph({ fromBlock }: { fromBlock: number }): Promise<BaseEvents<EchoEvents>> {
|
||||||
|
// TheGraph doesn't support our batch sync due to missing blockNumber field
|
||||||
|
if (!this.graphApi || this.graphApi.includes('api.thegraph.com')) {
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
lastBlock: fromBlock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getEventsFromGraph({ fromBlock });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type BaseEncryptedNotesServiceConstructor = {
|
export type BaseEncryptedNotesServiceConstructor = {
|
||||||
netId: number | string;
|
netId: number | string;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
@ -556,7 +634,7 @@ export class BaseGovernanceService extends BaseEventsService<BaseGovernanceEvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
getGraphMethod() {
|
getGraphMethod() {
|
||||||
return 'governanceEvents';
|
return 'getGovernanceEvents';
|
||||||
}
|
}
|
||||||
|
|
||||||
async formatEvents(events: EventLog[]): Promise<BaseGovernanceEventTypes[]> {
|
async formatEvents(events: EventLog[]): Promise<BaseGovernanceEventTypes[]> {
|
||||||
|
@ -12,8 +12,17 @@ import {
|
|||||||
BaseGovernanceServiceConstructor,
|
BaseGovernanceServiceConstructor,
|
||||||
BaseRegistryServiceConstructor,
|
BaseRegistryServiceConstructor,
|
||||||
BaseGovernanceEventTypes,
|
BaseGovernanceEventTypes,
|
||||||
|
BaseEchoServiceConstructor,
|
||||||
|
BaseEchoService,
|
||||||
} from './base';
|
} from './base';
|
||||||
import type { BaseEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, RegistersEvents } from './types';
|
import type {
|
||||||
|
BaseEvents,
|
||||||
|
DepositsEvents,
|
||||||
|
WithdrawalsEvents,
|
||||||
|
EncryptedNotesEvents,
|
||||||
|
RegistersEvents,
|
||||||
|
EchoEvents,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
export type NodeDepositsServiceConstructor = BaseDepositsServiceConstructor & {
|
export type NodeDepositsServiceConstructor = BaseDepositsServiceConstructor & {
|
||||||
cacheDirectory?: string;
|
cacheDirectory?: string;
|
||||||
@ -184,6 +193,151 @@ export class NodeDepositsService extends BaseDepositsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NodeEchoServiceConstructor = BaseEchoServiceConstructor & {
|
||||||
|
cacheDirectory?: string;
|
||||||
|
userDirectory?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class NodeEchoService extends BaseEchoService {
|
||||||
|
cacheDirectory?: string;
|
||||||
|
userDirectory?: string;
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
netId,
|
||||||
|
provider,
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
Echoer,
|
||||||
|
deployedBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
cacheDirectory,
|
||||||
|
userDirectory,
|
||||||
|
}: NodeEchoServiceConstructor) {
|
||||||
|
super({
|
||||||
|
netId,
|
||||||
|
provider,
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
Echoer,
|
||||||
|
deployedBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cacheDirectory = cacheDirectory;
|
||||||
|
this.userDirectory = userDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
|
||||||
|
if (toBlock) {
|
||||||
|
console.log(`fromBlock - ${fromBlock}`);
|
||||||
|
console.log(`toBlock - ${toBlock}`);
|
||||||
|
|
||||||
|
if (count) {
|
||||||
|
console.log(`downloaded ${type} events count - ${count}`);
|
||||||
|
console.log('____________________________________________');
|
||||||
|
console.log(`Fetched ${type} events from ${fromBlock} to ${toBlock}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
|
||||||
|
if (toBlock) {
|
||||||
|
console.log(`fromBlock - ${fromBlock}`);
|
||||||
|
console.log(`toBlock - ${toBlock}`);
|
||||||
|
|
||||||
|
if (count) {
|
||||||
|
console.log(`downloaded ${type} events from graph node count - ${count}`);
|
||||||
|
console.log('____________________________________________');
|
||||||
|
console.log(`Fetched ${type} events from graph node ${fromBlock} to ${toBlock}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEventsFromDB() {
|
||||||
|
if (!this.userDirectory) {
|
||||||
|
console.log(`Updating events for ${this.netId} chain echo events\n`);
|
||||||
|
console.log(`savedEvents count - ${0}`);
|
||||||
|
console.log(`savedEvents lastBlock - ${this.deployedBlock}\n`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
lastBlock: this.deployedBlock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedEvents = await loadSavedEvents<EchoEvents>({
|
||||||
|
name: this.getInstanceName(),
|
||||||
|
userDirectory: this.userDirectory,
|
||||||
|
deployedBlock: this.deployedBlock,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Updating events for ${this.netId} chain echo events\n`);
|
||||||
|
console.log(`savedEvents count - ${savedEvents.events.length}`);
|
||||||
|
console.log(`savedEvents lastBlock - ${savedEvents.lastBlock}\n`);
|
||||||
|
|
||||||
|
return savedEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEventsFromCache() {
|
||||||
|
if (!this.cacheDirectory) {
|
||||||
|
console.log(`cachedEvents count - ${0}`);
|
||||||
|
console.log(`cachedEvents lastBlock - ${this.deployedBlock}\n`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
lastBlock: this.deployedBlock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedEvents = await loadCachedEvents<EchoEvents>({
|
||||||
|
name: this.getInstanceName(),
|
||||||
|
cacheDirectory: this.cacheDirectory,
|
||||||
|
deployedBlock: this.deployedBlock,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`cachedEvents count - ${cachedEvents.events.length}`);
|
||||||
|
console.log(`cachedEvents lastBlock - ${cachedEvents.lastBlock}\n`);
|
||||||
|
|
||||||
|
return cachedEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveEvents({ events, lastBlock }: BaseEvents<EchoEvents>) {
|
||||||
|
const instanceName = this.getInstanceName();
|
||||||
|
|
||||||
|
console.log('\ntotalEvents count - ', events.length);
|
||||||
|
console.log(
|
||||||
|
`totalEvents lastBlock - ${events[events.length - 1] ? events[events.length - 1].blockNumber : lastBlock}\n`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const eventTable = new Table();
|
||||||
|
|
||||||
|
eventTable.push(
|
||||||
|
[{ colSpan: 2, content: 'Echo Accounts', hAlign: 'center' }],
|
||||||
|
['Network', `${this.netId} chain`],
|
||||||
|
['Events', `${events.length} events`],
|
||||||
|
[{ colSpan: 2, content: 'Latest events' }],
|
||||||
|
...events
|
||||||
|
.slice(events.length - 10)
|
||||||
|
.reverse()
|
||||||
|
.map(({ blockNumber }, index) => {
|
||||||
|
const eventIndex = events.length - index;
|
||||||
|
|
||||||
|
return [eventIndex, blockNumber];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(eventTable.toString() + '\n');
|
||||||
|
|
||||||
|
if (this.userDirectory) {
|
||||||
|
await saveEvents<EchoEvents>({
|
||||||
|
name: instanceName,
|
||||||
|
userDirectory: this.userDirectory,
|
||||||
|
events,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type NodeEncryptedNotesServiceConstructor = BaseEncryptedNotesServiceConstructor & {
|
export type NodeEncryptedNotesServiceConstructor = BaseEncryptedNotesServiceConstructor & {
|
||||||
cacheDirectory?: string;
|
cacheDirectory?: string;
|
||||||
userDirectory?: string;
|
userDirectory?: string;
|
||||||
|
@ -64,6 +64,11 @@ export type WithdrawalsEvents = MinimalEvents & {
|
|||||||
timestamp: number;
|
timestamp: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EchoEvents = MinimalEvents & {
|
||||||
|
address: string;
|
||||||
|
encryptedAccount: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type EncryptedNotesEvents = MinimalEvents & {
|
export type EncryptedNotesEvents = MinimalEvents & {
|
||||||
encryptedNote: string;
|
encryptedNote: string;
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@ import type {
|
|||||||
WithdrawalsEvents,
|
WithdrawalsEvents,
|
||||||
EncryptedNotesEvents,
|
EncryptedNotesEvents,
|
||||||
BatchGraphOnProgress,
|
BatchGraphOnProgress,
|
||||||
|
EchoEvents,
|
||||||
} from '../events';
|
} from '../events';
|
||||||
import {
|
import {
|
||||||
_META,
|
_META,
|
||||||
@ -17,6 +18,7 @@ import {
|
|||||||
GET_WITHDRAWALS,
|
GET_WITHDRAWALS,
|
||||||
GET_NOTE_ACCOUNTS,
|
GET_NOTE_ACCOUNTS,
|
||||||
GET_ENCRYPTED_NOTES,
|
GET_ENCRYPTED_NOTES,
|
||||||
|
GET_ECHO_EVENTS,
|
||||||
} from './queries';
|
} from './queries';
|
||||||
|
|
||||||
export * from './queries';
|
export * from './queries';
|
||||||
@ -662,6 +664,132 @@ export async function getNoteAccounts({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GraphEchoEvents {
|
||||||
|
noteAccounts: {
|
||||||
|
id: string;
|
||||||
|
blockNumber: string;
|
||||||
|
address: string;
|
||||||
|
encryptedAccount: string;
|
||||||
|
}[];
|
||||||
|
_meta: {
|
||||||
|
block: {
|
||||||
|
number: number;
|
||||||
|
};
|
||||||
|
hasIndexingErrors: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface getGraphEchoEventsParams {
|
||||||
|
graphApi: string;
|
||||||
|
subgraphName: string;
|
||||||
|
fromBlock: number;
|
||||||
|
fetchDataOptions?: fetchDataOptions;
|
||||||
|
onProgress?: BatchGraphOnProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGraphEchoEvents({
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
fromBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
}: getGraphEchoEventsParams): Promise<GraphEchoEvents> {
|
||||||
|
return queryGraph<GraphEchoEvents>({
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
query: GET_ECHO_EVENTS,
|
||||||
|
variables: {
|
||||||
|
first,
|
||||||
|
fromBlock,
|
||||||
|
},
|
||||||
|
fetchDataOptions,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllGraphEchoEvents({
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
fromBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
onProgress,
|
||||||
|
}: getGraphEchoEventsParams): Promise<BaseGraphEvents<EchoEvents>> {
|
||||||
|
try {
|
||||||
|
const events = [];
|
||||||
|
let lastSyncBlock = fromBlock;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
while (true) {
|
||||||
|
let {
|
||||||
|
noteAccounts: result,
|
||||||
|
_meta: {
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
block: { number: currentBlock },
|
||||||
|
},
|
||||||
|
} = await getGraphEchoEvents({ graphApi, subgraphName, fromBlock, fetchDataOptions });
|
||||||
|
|
||||||
|
lastSyncBlock = currentBlock;
|
||||||
|
|
||||||
|
if (isEmptyArray(result)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [firstEvent] = result;
|
||||||
|
const [lastEvent] = result.slice(-1);
|
||||||
|
|
||||||
|
if (typeof onProgress === 'function') {
|
||||||
|
onProgress({
|
||||||
|
type: 'EchoEvents',
|
||||||
|
fromBlock: Number(firstEvent.blockNumber),
|
||||||
|
toBlock: Number(lastEvent.blockNumber),
|
||||||
|
count: result.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.length < 900) {
|
||||||
|
events.push(...result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result.filter(({ blockNumber }) => blockNumber !== lastEvent.blockNumber);
|
||||||
|
fromBlock = Number(lastEvent.blockNumber);
|
||||||
|
|
||||||
|
events.push(...result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!events.length) {
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
lastSyncBlock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = events.map((e) => {
|
||||||
|
const [transactionHash, logIndex] = e.id.split('-');
|
||||||
|
|
||||||
|
return {
|
||||||
|
blockNumber: Number(e.blockNumber),
|
||||||
|
logIndex: Number(logIndex),
|
||||||
|
transactionHash: transactionHash,
|
||||||
|
address: e.address,
|
||||||
|
encryptedAccount: e.encryptedAccount,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const [lastEvent] = result.slice(-1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
events: result,
|
||||||
|
lastSyncBlock: lastEvent && lastEvent.blockNumber >= lastSyncBlock ? lastEvent.blockNumber + 1 : lastSyncBlock,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Error from getAllGraphEchoEvents query');
|
||||||
|
console.log(err);
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
lastSyncBlock: fromBlock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface GraphEncryptedNotes {
|
export interface GraphEncryptedNotes {
|
||||||
encryptedNotes: {
|
encryptedNotes: {
|
||||||
blockNumber: string;
|
blockNumber: string;
|
||||||
|
@ -107,6 +107,23 @@ export const GET_NOTE_ACCOUNTS = `
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const GET_ECHO_EVENTS = `
|
||||||
|
query getNoteAccounts($first: Int, $fromBlock: Int) {
|
||||||
|
noteAccounts(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||||
|
id
|
||||||
|
blockNumber
|
||||||
|
address
|
||||||
|
encryptedAccount
|
||||||
|
}
|
||||||
|
_meta {
|
||||||
|
block {
|
||||||
|
number
|
||||||
|
}
|
||||||
|
hasIndexingErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const GET_ENCRYPTED_NOTES = `
|
export const GET_ENCRYPTED_NOTES = `
|
||||||
query getEncryptedNotes($first: Int, $fromBlock: Int) {
|
query getEncryptedNotes($first: Int, $fromBlock: Int) {
|
||||||
encryptedNotes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
encryptedNotes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||||
|
Loading…
Reference in New Issue
Block a user