From 56c903c5764908999e422d41fbee2450f4f948bf Mon Sep 17 00:00:00 2001 From: neeraj Date: Thu, 6 Jun 2024 15:26:42 +0530 Subject: [PATCH] Generate sushiswap legacy watcher --- packages/sushiswap-watcher/.eslintignore | 2 + packages/sushiswap-watcher/.eslintrc.json | 28 + packages/sushiswap-watcher/.gitignore | 6 + packages/sushiswap-watcher/.husky/pre-commit | 4 + packages/sushiswap-watcher/.npmrc | 1 + packages/sushiswap-watcher/LICENSE | 661 +++ packages/sushiswap-watcher/README.md | 212 + .../sushiswap-watcher/environments/local.toml | 109 + .../gql-logs/watcher-gql-error.log | 0 .../gql-logs/watcher-gql.log | 0 packages/sushiswap-watcher/package.json | 76 + .../src/artifacts/Factory.json | 217 + .../sushiswap-watcher/src/artifacts/Pair.json | 660 +++ .../src/cli/checkpoint-cmds/create.ts | 44 + .../src/cli/checkpoint-cmds/verify.ts | 40 + .../sushiswap-watcher/src/cli/checkpoint.ts | 39 + .../sushiswap-watcher/src/cli/export-state.ts | 38 + .../sushiswap-watcher/src/cli/import-state.ts | 39 + .../sushiswap-watcher/src/cli/index-block.ts | 38 + .../sushiswap-watcher/src/cli/inspect-cid.ts | 38 + .../src/cli/reset-cmds/job-queue.ts | 22 + .../src/cli/reset-cmds/state.ts | 24 + .../src/cli/reset-cmds/watcher.ts | 37 + packages/sushiswap-watcher/src/cli/reset.ts | 24 + .../src/cli/watch-contract.ts | 38 + packages/sushiswap-watcher/src/client.ts | 55 + packages/sushiswap-watcher/src/database.ts | 304 ++ .../src/entity/BlockProgress.ts | 48 + .../sushiswap-watcher/src/entity/Bundle.ts | 29 + packages/sushiswap-watcher/src/entity/Burn.ts | 65 + .../sushiswap-watcher/src/entity/Contract.ts | 27 + .../sushiswap-watcher/src/entity/Event.ts | 38 + .../sushiswap-watcher/src/entity/Factory.ts | 60 + .../src/entity/FactoryDaySnapshot.ts | 53 + .../src/entity/FactoryHourSnapshot.ts | 53 + .../src/entity/FrothyEntity.ts | 21 + .../src/entity/LiquidityPosition.ts | 40 + .../src/entity/LiquidityPositionSnapshot.ts | 62 + packages/sushiswap-watcher/src/entity/Mint.ts | 62 + packages/sushiswap-watcher/src/entity/Pair.ts | 105 + .../src/entity/PairDaySnapshot.ts | 68 + .../src/entity/PairHourSnapshot.ts | 68 + .../sushiswap-watcher/src/entity/State.ts | 31 + .../src/entity/StateSyncStatus.ts | 17 + .../src/entity/Subscriber.ts | 21 + packages/sushiswap-watcher/src/entity/Swap.ts | 59 + .../src/entity/SyncStatus.ts | 45 + .../sushiswap-watcher/src/entity/Token.ts | 80 + .../src/entity/TokenDaySnapshot.ts | 65 + .../src/entity/TokenHourSnapshot.ts | 65 + .../src/entity/TokenPrice.ts | 41 + .../src/entity/Transaction.ts | 46 + packages/sushiswap-watcher/src/entity/User.ts | 28 + .../src/entity/_TokenPair.ts | 30 + .../src/entity/_WhitelistedTokenPair.ts | 30 + packages/sushiswap-watcher/src/fill.ts | 48 + packages/sushiswap-watcher/src/gql/index.ts | 3 + .../src/gql/mutations/index.ts | 4 + .../src/gql/mutations/watchContract.gql | 3 + .../src/gql/queries/_TokenPair.gql | 52 + .../src/gql/queries/_TokenPairs.gql | 52 + .../src/gql/queries/_WhitelistedTokenPair.gql | 52 + .../gql/queries/_WhitelistedTokenPairs.gql | 52 + .../src/gql/queries/_meta.gql | 11 + .../src/gql/queries/bundle.gql | 6 + .../src/gql/queries/bundles.gql | 6 + .../src/gql/queries/burn.gql | 50 + .../src/gql/queries/burns.gql | 50 + .../src/gql/queries/events.gql | 63 + .../src/gql/queries/eventsInRange.gql | 63 + .../src/gql/queries/factories.gql | 16 + .../src/gql/queries/factory.gql | 16 + .../src/gql/queries/factoryDaySnapshot.gql | 27 + .../src/gql/queries/factoryDaySnapshots.gql | 27 + .../src/gql/queries/factoryHourSnapshot.gql | 27 + .../src/gql/queries/factoryHourSnapshots.gql | 27 + .../src/gql/queries/getState.gql | 15 + .../src/gql/queries/getStateByCID.gql | 15 + .../src/gql/queries/getSyncStatus.gql | 12 + .../src/gql/queries/index.ts | 49 + .../src/gql/queries/liquidityPosition.gql | 39 + .../gql/queries/liquidityPositionSnapshot.gql | 51 + .../queries/liquidityPositionSnapshots.gql | 51 + .../src/gql/queries/liquidityPositions.gql | 39 + .../src/gql/queries/mint.gql | 49 + .../src/gql/queries/mints.gql | 49 + .../src/gql/queries/pair.gql | 119 + .../src/gql/queries/pairDaySnapshot.gql | 45 + .../src/gql/queries/pairDaySnapshots.gql | 45 + .../src/gql/queries/pairHourSnapshot.gql | 45 + .../src/gql/queries/pairHourSnapshots.gql | 45 + .../src/gql/queries/pairs.gql | 119 + .../src/gql/queries/swap.gql | 86 + .../src/gql/queries/swaps.gql | 86 + .../src/gql/queries/token.gql | 33 + .../src/gql/queries/tokenDaySnapshot.gql | 37 + .../src/gql/queries/tokenDaySnapshots.gql | 37 + .../src/gql/queries/tokenHourSnapshot.gql | 37 + .../src/gql/queries/tokenHourSnapshots.gql | 37 + .../src/gql/queries/tokenPrice.gql | 74 + .../src/gql/queries/tokenPrices.gql | 74 + .../src/gql/queries/tokens.gql | 33 + .../src/gql/queries/transaction.gql | 46 + .../src/gql/queries/transactions.gql | 46 + .../src/gql/queries/user.gql | 12 + .../src/gql/queries/users.gql | 12 + .../src/gql/subscriptions/index.ts | 4 + .../src/gql/subscriptions/onEvent.gql | 63 + packages/sushiswap-watcher/src/hooks.ts | 86 + packages/sushiswap-watcher/src/indexer.ts | 1197 ++++++ packages/sushiswap-watcher/src/job-runner.ts | 48 + packages/sushiswap-watcher/src/resolvers.ts | 1179 +++++ packages/sushiswap-watcher/src/schema.gql | 3798 +++++++++++++++++ packages/sushiswap-watcher/src/server.ts | 43 + packages/sushiswap-watcher/src/types.ts | 7 + .../subgraph-build/Factory/Factory.wasm | Bin 0 -> 216887 bytes .../subgraph-build/schema.graphql | 421 ++ .../subgraph-build/subgraph.yaml | 69 + .../subgraph-build/templates/Pair/Pair.wasm | Bin 0 -> 326868 bytes packages/sushiswap-watcher/tsconfig.json | 74 + 120 files changed, 13194 insertions(+) create mode 100644 packages/sushiswap-watcher/.eslintignore create mode 100644 packages/sushiswap-watcher/.eslintrc.json create mode 100644 packages/sushiswap-watcher/.gitignore create mode 100755 packages/sushiswap-watcher/.husky/pre-commit create mode 100644 packages/sushiswap-watcher/.npmrc create mode 100644 packages/sushiswap-watcher/LICENSE create mode 100644 packages/sushiswap-watcher/README.md create mode 100644 packages/sushiswap-watcher/environments/local.toml create mode 100644 packages/sushiswap-watcher/gql-logs/watcher-gql-error.log create mode 100644 packages/sushiswap-watcher/gql-logs/watcher-gql.log create mode 100644 packages/sushiswap-watcher/package.json create mode 100644 packages/sushiswap-watcher/src/artifacts/Factory.json create mode 100644 packages/sushiswap-watcher/src/artifacts/Pair.json create mode 100644 packages/sushiswap-watcher/src/cli/checkpoint-cmds/create.ts create mode 100644 packages/sushiswap-watcher/src/cli/checkpoint-cmds/verify.ts create mode 100644 packages/sushiswap-watcher/src/cli/checkpoint.ts create mode 100644 packages/sushiswap-watcher/src/cli/export-state.ts create mode 100644 packages/sushiswap-watcher/src/cli/import-state.ts create mode 100644 packages/sushiswap-watcher/src/cli/index-block.ts create mode 100644 packages/sushiswap-watcher/src/cli/inspect-cid.ts create mode 100644 packages/sushiswap-watcher/src/cli/reset-cmds/job-queue.ts create mode 100644 packages/sushiswap-watcher/src/cli/reset-cmds/state.ts create mode 100644 packages/sushiswap-watcher/src/cli/reset-cmds/watcher.ts create mode 100644 packages/sushiswap-watcher/src/cli/reset.ts create mode 100644 packages/sushiswap-watcher/src/cli/watch-contract.ts create mode 100644 packages/sushiswap-watcher/src/client.ts create mode 100644 packages/sushiswap-watcher/src/database.ts create mode 100644 packages/sushiswap-watcher/src/entity/BlockProgress.ts create mode 100644 packages/sushiswap-watcher/src/entity/Bundle.ts create mode 100644 packages/sushiswap-watcher/src/entity/Burn.ts create mode 100644 packages/sushiswap-watcher/src/entity/Contract.ts create mode 100644 packages/sushiswap-watcher/src/entity/Event.ts create mode 100644 packages/sushiswap-watcher/src/entity/Factory.ts create mode 100644 packages/sushiswap-watcher/src/entity/FactoryDaySnapshot.ts create mode 100644 packages/sushiswap-watcher/src/entity/FactoryHourSnapshot.ts create mode 100644 packages/sushiswap-watcher/src/entity/FrothyEntity.ts create mode 100644 packages/sushiswap-watcher/src/entity/LiquidityPosition.ts create mode 100644 packages/sushiswap-watcher/src/entity/LiquidityPositionSnapshot.ts create mode 100644 packages/sushiswap-watcher/src/entity/Mint.ts create mode 100644 packages/sushiswap-watcher/src/entity/Pair.ts create mode 100644 packages/sushiswap-watcher/src/entity/PairDaySnapshot.ts create mode 100644 packages/sushiswap-watcher/src/entity/PairHourSnapshot.ts create mode 100644 packages/sushiswap-watcher/src/entity/State.ts create mode 100644 packages/sushiswap-watcher/src/entity/StateSyncStatus.ts create mode 100644 packages/sushiswap-watcher/src/entity/Subscriber.ts create mode 100644 packages/sushiswap-watcher/src/entity/Swap.ts create mode 100644 packages/sushiswap-watcher/src/entity/SyncStatus.ts create mode 100644 packages/sushiswap-watcher/src/entity/Token.ts create mode 100644 packages/sushiswap-watcher/src/entity/TokenDaySnapshot.ts create mode 100644 packages/sushiswap-watcher/src/entity/TokenHourSnapshot.ts create mode 100644 packages/sushiswap-watcher/src/entity/TokenPrice.ts create mode 100644 packages/sushiswap-watcher/src/entity/Transaction.ts create mode 100644 packages/sushiswap-watcher/src/entity/User.ts create mode 100644 packages/sushiswap-watcher/src/entity/_TokenPair.ts create mode 100644 packages/sushiswap-watcher/src/entity/_WhitelistedTokenPair.ts create mode 100644 packages/sushiswap-watcher/src/fill.ts create mode 100644 packages/sushiswap-watcher/src/gql/index.ts create mode 100644 packages/sushiswap-watcher/src/gql/mutations/index.ts create mode 100644 packages/sushiswap-watcher/src/gql/mutations/watchContract.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/_TokenPair.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/_TokenPairs.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/_WhitelistedTokenPair.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/_WhitelistedTokenPairs.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/_meta.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/bundle.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/bundles.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/burn.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/burns.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/events.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/eventsInRange.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/factories.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/factory.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/factoryDaySnapshot.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/factoryDaySnapshots.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/factoryHourSnapshot.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/factoryHourSnapshots.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/getState.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/getStateByCID.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/getSyncStatus.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/index.ts create mode 100644 packages/sushiswap-watcher/src/gql/queries/liquidityPosition.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/liquidityPositionSnapshot.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/liquidityPositionSnapshots.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/liquidityPositions.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/mint.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/mints.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/pair.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/pairDaySnapshot.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/pairDaySnapshots.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/pairHourSnapshot.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/pairHourSnapshots.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/pairs.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/swap.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/swaps.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/token.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/tokenDaySnapshot.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/tokenDaySnapshots.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/tokenHourSnapshot.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/tokenHourSnapshots.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/tokenPrice.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/tokenPrices.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/tokens.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/transaction.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/transactions.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/user.gql create mode 100644 packages/sushiswap-watcher/src/gql/queries/users.gql create mode 100644 packages/sushiswap-watcher/src/gql/subscriptions/index.ts create mode 100644 packages/sushiswap-watcher/src/gql/subscriptions/onEvent.gql create mode 100644 packages/sushiswap-watcher/src/hooks.ts create mode 100644 packages/sushiswap-watcher/src/indexer.ts create mode 100644 packages/sushiswap-watcher/src/job-runner.ts create mode 100644 packages/sushiswap-watcher/src/resolvers.ts create mode 100644 packages/sushiswap-watcher/src/schema.gql create mode 100644 packages/sushiswap-watcher/src/server.ts create mode 100644 packages/sushiswap-watcher/src/types.ts create mode 100644 packages/sushiswap-watcher/subgraph-build/Factory/Factory.wasm create mode 100644 packages/sushiswap-watcher/subgraph-build/schema.graphql create mode 100644 packages/sushiswap-watcher/subgraph-build/subgraph.yaml create mode 100644 packages/sushiswap-watcher/subgraph-build/templates/Pair/Pair.wasm create mode 100644 packages/sushiswap-watcher/tsconfig.json diff --git a/packages/sushiswap-watcher/.eslintignore b/packages/sushiswap-watcher/.eslintignore new file mode 100644 index 0000000..55cb522 --- /dev/null +++ b/packages/sushiswap-watcher/.eslintignore @@ -0,0 +1,2 @@ +# Don't lint build output. +dist diff --git a/packages/sushiswap-watcher/.eslintrc.json b/packages/sushiswap-watcher/.eslintrc.json new file mode 100644 index 0000000..a2b842c --- /dev/null +++ b/packages/sushiswap-watcher/.eslintrc.json @@ -0,0 +1,28 @@ +{ + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "semistandard", + "plugin:@typescript-eslint/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "indent": ["error", 2, { "SwitchCase": 1 }], + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/explicit-module-boundary-types": [ + "warn", + { + "allowArgumentsExplicitlyTypedAsAny": true + } + ] + } +} diff --git a/packages/sushiswap-watcher/.gitignore b/packages/sushiswap-watcher/.gitignore new file mode 100644 index 0000000..0b4cc3f --- /dev/null +++ b/packages/sushiswap-watcher/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +out/ + +.vscode +.idea diff --git a/packages/sushiswap-watcher/.husky/pre-commit b/packages/sushiswap-watcher/.husky/pre-commit new file mode 100755 index 0000000..9dcd433 --- /dev/null +++ b/packages/sushiswap-watcher/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn lint diff --git a/packages/sushiswap-watcher/.npmrc b/packages/sushiswap-watcher/.npmrc new file mode 100644 index 0000000..6b64c5b --- /dev/null +++ b/packages/sushiswap-watcher/.npmrc @@ -0,0 +1 @@ +@cerc-io:registry=https://git.vdb.to/api/packages/cerc-io/npm/ diff --git a/packages/sushiswap-watcher/LICENSE b/packages/sushiswap-watcher/LICENSE new file mode 100644 index 0000000..331f7cf --- /dev/null +++ b/packages/sushiswap-watcher/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for + software and other kinds of works, specifically designed to ensure + cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed + to take away your freedom to share and change the works. By contrast, + our General Public Licenses are intended to guarantee your freedom to + share and change all versions of a program--to make sure it remains free + software for all its users. + + When we speak of free software, we are referring to freedom, not + price. Our General Public Licenses are designed to make sure that you + have the freedom to distribute copies of free software (and charge for + them if you wish), that you receive source code or can get it if you + want it, that you can change the software or use pieces of it in new + free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights + with two steps: (1) assert copyright on the software, and (2) offer + you this License which gives you legal permission to copy, distribute + and/or modify the software. + + A secondary benefit of defending all users' freedom is that + improvements made in alternate versions of the program, if they + receive widespread use, become available for other developers to + incorporate. Many developers of free software are heartened and + encouraged by the resulting cooperation. However, in the case of + software used on network servers, this result may fail to come about. + The GNU General Public License permits making a modified version and + letting the public access it on a server without ever releasing its + source code to the public. + + The GNU Affero General Public License is designed specifically to + ensure that, in such cases, the modified source code becomes available + to the community. It requires the operator of a network server to + provide the source code of the modified version running there to the + users of that server. Therefore, public use of a modified version, on + a publicly accessible server, gives the public access to the source + code of the modified version. + + An older license, called the Affero General Public License and + published by Affero, was designed to accomplish similar goals. This is + a different license, not a version of the Affero GPL, but Affero has + released a new version of the Affero GPL which permits relicensing under + this license. + + The precise terms and conditions for copying, distribution and + modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of + works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this + License. Each licensee is addressed as "you". "Licensees" and + "recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work + in a fashion requiring copyright permission, other than the making of an + exact copy. The resulting work is called a "modified version" of the + earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based + on the Program. + + To "propagate" a work means to do anything with it that, without + permission, would make you directly or secondarily liable for + infringement under applicable copyright law, except executing it on a + computer or modifying a private copy. Propagation includes copying, + distribution (with or without modification), making available to the + public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other + parties to make or receive copies. Mere interaction with a user through + a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" + to the extent that it includes a convenient and prominently visible + feature that (1) displays an appropriate copyright notice, and (2) + tells the user that there is no warranty for the work (except to the + extent that warranties are provided), that licensees may convey the + work under this License, and how to view a copy of this License. If + the interface presents a list of user commands or options, such as a + menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work + for making modifications to it. "Object code" means any non-source + form of a work. + + A "Standard Interface" means an interface that either is an official + standard defined by a recognized standards body, or, in the case of + interfaces specified for a particular programming language, one that + is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other + than the work as a whole, that (a) is included in the normal form of + packaging a Major Component, but which is not part of that Major + Component, and (b) serves only to enable use of the work with that + Major Component, or to implement a Standard Interface for which an + implementation is available to the public in source code form. A + "Major Component", in this context, means a major essential component + (kernel, window system, and so on) of the specific operating system + (if any) on which the executable work runs, or a compiler used to + produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all + the source code needed to generate, install, and (for an executable + work) run the object code and to modify the work, including scripts to + control those activities. However, it does not include the work's + System Libraries, or general-purpose tools or generally available free + programs which are used unmodified in performing those activities but + which are not part of the work. For example, Corresponding Source + includes interface definition files associated with source files for + the work, and the source code for shared libraries and dynamically + linked subprograms that the work is specifically designed to require, + such as by intimate data communication or control flow between those + subprograms and other parts of the work. + + The Corresponding Source need not include anything that users + can regenerate automatically from other parts of the Corresponding + Source. + + The Corresponding Source for a work in source code form is that + same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of + copyright on the Program, and are irrevocable provided the stated + conditions are met. This License explicitly affirms your unlimited + permission to run the unmodified Program. The output from running a + covered work is covered by this License only if the output, given its + content, constitutes a covered work. This License acknowledges your + rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not + convey, without conditions so long as your license otherwise remains + in force. You may convey covered works to others for the sole purpose + of having them make modifications exclusively for you, or provide you + with facilities for running those works, provided that you comply with + the terms of this License in conveying all material for which you do + not control copyright. Those thus making or running the covered works + for you must do so exclusively on your behalf, under your direction + and control, on terms that prohibit them from making any copies of + your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under + the conditions stated below. Sublicensing is not allowed; section 10 + makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological + measure under any applicable law fulfilling obligations under article + 11 of the WIPO copyright treaty adopted on 20 December 1996, or + similar laws prohibiting or restricting circumvention of such + measures. + + When you convey a covered work, you waive any legal power to forbid + circumvention of technological measures to the extent such circumvention + is effected by exercising rights under this License with respect to + the covered work, and you disclaim any intention to limit operation or + modification of the work as a means of enforcing, against the work's + users, your or third parties' legal rights to forbid circumvention of + technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you + receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice; + keep intact all notices stating that this License and any + non-permissive terms added in accord with section 7 apply to the code; + keep intact all notices of the absence of any warranty; and give all + recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, + and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to + produce it from the Program, in the form of source code under the + terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent + works, which are not by their nature extensions of the covered work, + and which are not combined with it such as to form a larger program, + in or on a volume of a storage or distribution medium, is called an + "aggregate" if the compilation and its resulting copyright are not + used to limit the access or legal rights of the compilation's users + beyond what the individual works permit. Inclusion of a covered work + in an aggregate does not cause this License to apply to the other + parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms + of sections 4 and 5, provided that you also convey the + machine-readable Corresponding Source under the terms of this License, + in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded + from the Corresponding Source as a System Library, need not be + included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any + tangible personal property which is normally used for personal, family, + or household purposes, or (2) anything designed or sold for incorporation + into a dwelling. In determining whether a product is a consumer product, + doubtful cases shall be resolved in favor of coverage. For a particular + product received by a particular user, "normally used" refers to a + typical or common use of that class of product, regardless of the status + of the particular user or of the way in which the particular user + actually uses, or expects or is expected to use, the product. A product + is a consumer product regardless of whether the product has substantial + commercial, industrial or non-consumer uses, unless such uses represent + the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, + procedures, authorization keys, or other information required to install + and execute modified versions of a covered work in that User Product from + a modified version of its Corresponding Source. The information must + suffice to ensure that the continued functioning of the modified object + code is in no case prevented or interfered with solely because + modification has been made. + + If you convey an object code work under this section in, or with, or + specifically for use in, a User Product, and the conveying occurs as + part of a transaction in which the right of possession and use of the + User Product is transferred to the recipient in perpetuity or for a + fixed term (regardless of how the transaction is characterized), the + Corresponding Source conveyed under this section must be accompanied + by the Installation Information. But this requirement does not apply + if neither you nor any third party retains the ability to install + modified object code on the User Product (for example, the work has + been installed in ROM). + + The requirement to provide Installation Information does not include a + requirement to continue to provide support service, warranty, or updates + for a work that has been modified or installed by the recipient, or for + the User Product in which it has been modified or installed. Access to a + network may be denied when the modification itself materially and + adversely affects the operation of the network or violates the rules and + protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, + in accord with this section must be in a format that is publicly + documented (and with an implementation available to the public in + source code form), and must require no special password or key for + unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this + License by making exceptions from one or more of its conditions. + Additional permissions that are applicable to the entire Program shall + be treated as though they were included in this License, to the extent + that they are valid under applicable law. If additional permissions + apply only to part of the Program, that part may be used separately + under those permissions, but the entire Program remains governed by + this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option + remove any additional permissions from that copy, or from any part of + it. (Additional permissions may be written to require their own + removal in certain cases when you modify the work.) You may place + additional permissions on material, added by you to a covered work, + for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you + add to a covered work, you may (if authorized by the copyright holders of + that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further + restrictions" within the meaning of section 10. If the Program as you + received it, or any part of it, contains a notice stating that it is + governed by this License along with a term that is a further + restriction, you may remove that term. If a license document contains + a further restriction but permits relicensing or conveying under this + License, you may add to a covered work material governed by the terms + of that license document, provided that the further restriction does + not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you + must place, in the relevant source files, a statement of the + additional terms that apply to those files, or a notice indicating + where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the + form of a separately written license, or stated as exceptions; + the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly + provided under this License. Any attempt otherwise to propagate or + modify it is void, and will automatically terminate your rights under + this License (including any patent licenses granted under the third + paragraph of section 11). + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the copyright + holder fails to notify you of the violation by some reasonable means + prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from that + copyright holder, and you cure the violation prior to 30 days after + your receipt of the notice. + + Termination of your rights under this section does not terminate the + licenses of parties who have received copies or rights from you under + this License. If your rights have been terminated and not permanently + reinstated, you do not qualify to receive new licenses for the same + material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or + run a copy of the Program. Ancillary propagation of a covered work + occurring solely as a consequence of using peer-to-peer transmission + to receive a copy likewise does not require acceptance. However, + nothing other than this License grants you permission to propagate or + modify any covered work. These actions infringe copyright if you do + not accept this License. Therefore, by modifying or propagating a + covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically + receives a license from the original licensors, to run, modify and + propagate that work, subject to this License. You are not responsible + for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an + organization, or substantially all assets of one, or subdividing an + organization, or merging organizations. If propagation of a covered + work results from an entity transaction, each party to that + transaction who receives a copy of the work also receives whatever + licenses to the work the party's predecessor in interest had or could + give under the previous paragraph, plus a right to possession of the + Corresponding Source of the work from the predecessor in interest, if + the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the + rights granted or affirmed under this License. For example, you may + not impose a license fee, royalty, or other charge for exercise of + rights granted under this License, and you may not initiate litigation + (including a cross-claim or counterclaim in a lawsuit) alleging that + any patent claim is infringed by making, using, selling, offering for + sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this + License of the Program or a work on which the Program is based. The + work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims + owned or controlled by the contributor, whether already acquired or + hereafter acquired, that would be infringed by some manner, permitted + by this License, of making, using, or selling its contributor version, + but do not include claims that would be infringed only as a + consequence of further modification of the contributor version. For + purposes of this definition, "control" includes the right to grant + patent sublicenses in a manner consistent with the requirements of + this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free + patent license under the contributor's essential patent claims, to + make, use, sell, offer for sale, import and otherwise run, modify and + propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express + agreement or commitment, however denominated, not to enforce a patent + (such as an express permission to practice a patent or covenant not to + sue for patent infringement). To "grant" such a patent license to a + party means to make such an agreement or commitment not to enforce a + patent against the party. + + If you convey a covered work, knowingly relying on a patent license, + and the Corresponding Source of the work is not available for anyone + to copy, free of charge and under the terms of this License, through a + publicly available network server or other readily accessible means, + then you must either (1) cause the Corresponding Source to be so + available, or (2) arrange to deprive yourself of the benefit of the + patent license for this particular work, or (3) arrange, in a manner + consistent with the requirements of this License, to extend the patent + license to downstream recipients. "Knowingly relying" means you have + actual knowledge that, but for the patent license, your conveying the + covered work in a country, or your recipient's use of the covered work + in a country, would infringe one or more identifiable patents in that + country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or + arrangement, you convey, or propagate by procuring conveyance of, a + covered work, and grant a patent license to some of the parties + receiving the covered work authorizing them to use, propagate, modify + or convey a specific copy of the covered work, then the patent license + you grant is automatically extended to all recipients of the covered + work and works based on it. + + A patent license is "discriminatory" if it does not include within + the scope of its coverage, prohibits the exercise of, or is + conditioned on the non-exercise of one or more of the rights that are + specifically granted under this License. You may not convey a covered + work if you are a party to an arrangement with a third party that is + in the business of distributing software, under which you make payment + to the third party based on the extent of your activity of conveying + the work, and under which the third party grants, to any of the + parties who would receive the covered work from you, a discriminatory + patent license (a) in connection with copies of the covered work + conveyed by you (or copies made from those copies), or (b) primarily + for and in connection with specific products or compilations that + contain the covered work, unless you entered into that arrangement, + or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting + any implied license or other defenses to infringement that may + otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot convey a + covered work so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you may + not convey it at all. For example, if you agree to terms that obligate you + to collect a royalty for further conveying from those to whom you convey + the Program, the only way you could satisfy both those terms and this + License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the + Program, your modified version must prominently offer all users + interacting with it remotely through a computer network (if your version + supports such interaction) an opportunity to receive the Corresponding + Source of your version by providing access to the Corresponding Source + from a network server at no charge, through some standard or customary + means of facilitating copying of software. This Corresponding Source + shall include the Corresponding Source for any work covered by version 3 + of the GNU General Public License that is incorporated pursuant to the + following paragraph. + + Notwithstanding any other provision of this License, you have + permission to link or combine any covered work with a work licensed + under version 3 of the GNU General Public License into a single + combined work, and to convey the resulting work. The terms of this + License will continue to apply to the part which is the covered work, + but the work with which it is combined will remain governed by version + 3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of + the GNU Affero General Public License from time to time. Such new versions + will be similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies that a certain numbered version of the GNU Affero General + Public License "or any later version" applies to it, you have the + option of following the terms and conditions either of that numbered + version or of any later version published by the Free Software + Foundation. If the Program does not specify a version number of the + GNU Affero General Public License, you may choose any version ever published + by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future + versions of the GNU Affero General Public License can be used, that proxy's + public statement of acceptance of a version permanently authorizes you + to choose that version for the Program. + + Later license versions may give you additional or different + permissions. However, no additional obligations are imposed on any + author or copyright holder as a result of your choosing to follow a + later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT + HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY + OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM + IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF + ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS + THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY + GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE + USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), + EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF + SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided + above cannot be given local legal effect according to their terms, + reviewing courts shall apply local law that most closely approximates + an absolute waiver of all civil liability in connection with the + Program, unless a warranty or assumption of liability accompanies a + copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest + possible use to the public, the best way to achieve this is to make it + free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest + to attach them to the start of each source file to most effectively + state the exclusion of warranty; and each file should have at least + the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer + network, you should also make sure that it provides a way for users to + get its source. For example, if your program is a web application, its + interface could display a "Source" link that leads users to an archive + of the code. There are many ways you could offer source, and different + solutions will be better for different programs; see section 13 for the + specific requirements. + + You should also get your employer (if you work as a programmer) or school, + if any, to sign a "copyright disclaimer" for the program, if necessary. + For more information on this, and how to apply and follow the GNU AGPL, see + . diff --git a/packages/sushiswap-watcher/README.md b/packages/sushiswap-watcher/README.md new file mode 100644 index 0000000..54edbbc --- /dev/null +++ b/packages/sushiswap-watcher/README.md @@ -0,0 +1,212 @@ +# sushiswap-watcher + +## Setup + +* Run the following command to install required packages: + + ```bash + yarn + ``` + +* Create a postgres12 database for the watcher: + + ```bash + sudo su - postgres + createdb sushiswap-watcher + ``` + +* If the watcher is an `active` watcher: + + Create database for the job queue and enable the `pgcrypto` extension on them (https://github.com/timgit/pg-boss/blob/master/docs/usage.md#intro): + + ``` + createdb sushiswap-watcher-job-queue + ``` + + ``` + postgres@tesla:~$ psql -U postgres -h localhost sushiswap-watcher-job-queue + Password for user postgres: + psql (12.7 (Ubuntu 12.7-1.pgdg18.04+1)) + SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off) + Type "help" for help. + + sushiswap-watcher-job-queue=# CREATE EXTENSION pgcrypto; + CREATE EXTENSION + sushiswap-watcher-job-queue=# exit + ``` + +* In the [config file](./environments/local.toml): + + * Update the database connection settings. + + * Update the `upstream` config and provide the `ipld-eth-server` GQL API endpoint. + + * Update the `server` config with state checkpoint settings. + +## Customize + +* Indexing on an event: + + * Edit the custom hook function `handleEvent` (triggered on an event) in [hooks.ts](./src/hooks.ts) to perform corresponding indexing using the `Indexer` object. + + * While using the indexer storage methods for indexing, pass `diff` as true if default state is desired to be generated using the state variables being indexed. + +* Generating state: + + * Edit the custom hook function `createInitialState` (triggered if the watcher passes the start block, checkpoint: `true`) in [hooks.ts](./src/hooks.ts) to save an initial `State` using the `Indexer` object. + + * Edit the custom hook function `createStateDiff` (triggered on a block) in [hooks.ts](./src/hooks.ts) to save the state in a `diff` `State` using the `Indexer` object. The default state (if exists) is updated. + + * Edit the custom hook function `createStateCheckpoint` (triggered just before default and CLI checkpoint) in [hooks.ts](./src/hooks.ts) to save the state in a `checkpoint` `State` using the `Indexer` object. + +### GQL Caching + +To enable GQL requests caching: + +* Update the `server.gql.cache` config with required settings. + +* In the GQL [schema file](./src/schema.gql), use the `cacheControl` directive to apply cache hints at schema level. + + * Eg. Set `inheritMaxAge` to true for non-scalar fields of a type. + +* In the GQL [resolvers file](./src/resolvers.ts), uncomment the `setGQLCacheHints()` calls in resolvers for required queries. + +## Run + +* If the watcher is a `lazy` watcher: + + * Run the server: + + ```bash + yarn server + ``` + + GQL console: http://localhost:3008/graphql + +* If the watcher is an `active` watcher: + + * Run the job-runner: + + ```bash + yarn job-runner + ``` + + * Run the server: + + ```bash + yarn server + ``` + + GQL console: http://localhost:3008/graphql + + * To watch a contract: + + ```bash + yarn watch:contract --address --kind --checkpoint --starting-block [block-number] + ``` + + * `address`: Address or identifier of the contract to be watched. + * `kind`: Kind of the contract. + * `checkpoint`: Turn checkpointing on (`true` | `false`). + * `starting-block`: Starting block for the contract (default: `1`). + + Examples: + + Watch a contract with its address and checkpointing on: + + ```bash + yarn watch:contract --address 0x1F78641644feB8b64642e833cE4AFE93DD6e7833 --kind ERC20 --checkpoint true + ``` + + Watch a contract with its identifier and checkpointing on: + + ```bash + yarn watch:contract --address MyProtocol --kind protocol --checkpoint true + ``` + + * To fill a block range: + + ```bash + yarn fill --start-block --end-block + ``` + + * `start-block`: Block number to start filling from. + * `end-block`: Block number till which to fill. + + * To create a checkpoint for a contract: + + ```bash + yarn checkpoint create --address --block-hash [block-hash] + ``` + + * `address`: Address or identifier of the contract for which to create a checkpoint. + * `block-hash`: Hash of a block (in the pruned region) at which to create the checkpoint (default: latest canonical block hash). + + * To verify a checkpoint: + + ```bash + yarn checkpoint verify --cid + ``` + + `cid`: CID of the checkpoint for which to verify. + + * To reset the watcher to a previous block number: + + * Reset watcher: + + ```bash + yarn reset watcher --block-number + ``` + + * Reset job-queue: + + ```bash + yarn reset job-queue + ``` + + * Reset state: + + ```bash + yarn reset state --block-number + ``` + + * `block-number`: Block number to which to reset the watcher. + + * To export and import the watcher state: + + * In source watcher, export watcher state: + + ```bash + yarn export-state --export-file [export-file-path] --block-number [snapshot-block-height] + ``` + + * `export-file`: Path of file to which to export the watcher data. + * `block-number`: Block height at which to take snapshot for export. + + * In target watcher, run job-runner: + + ```bash + yarn job-runner + ``` + + * Import watcher state: + + ```bash + yarn import-state --import-file + ``` + + * `import-file`: Path of file from which to import the watcher data. + + * Run server: + + ```bash + yarn server + ``` + + * To inspect a CID: + + ```bash + yarn inspect-cid --cid + ``` + + * `cid`: CID to be inspected. diff --git a/packages/sushiswap-watcher/environments/local.toml b/packages/sushiswap-watcher/environments/local.toml new file mode 100644 index 0000000..2ec6725 --- /dev/null +++ b/packages/sushiswap-watcher/environments/local.toml @@ -0,0 +1,109 @@ +[server] + host = "127.0.0.1" + port = 3008 + kind = "active" + + # Checkpointing state. + checkpointing = true + + # Checkpoint interval in number of blocks. + checkpointInterval = 2000 + + # Enable state creation + # CAUTION: Disable only if state creation is not desired or can be filled subsequently + enableState = true + + subgraphPath = "./subgraph-build" + + # Interval to restart wasm instance periodically + wasmRestartBlocksInterval = 20 + + # Interval in number of blocks at which to clear entities cache. + clearEntitiesCacheInterval = 1000 + + # Flag to specify whether RPC endpoint supports block hash as block tag parameter + rpcSupportsBlockHashParam = true + + # Server GQL config + [server.gql] + path = "/graphql" + + # Max block range for which to return events in eventsInRange GQL query. + # Use -1 for skipping check on block range. + maxEventsBlockRange = 1000 + + # Log directory for GQL requests + logDir = "./gql-logs" + + # GQL cache settings + [server.gql.cache] + enabled = true + + # Max in-memory cache size (in bytes) (default 8 MB) + # maxCacheSize + + # GQL cache-control max-age settings (in seconds) + maxAge = 15 + timeTravelMaxAge = 86400 # 1 day + +[metrics] + host = "127.0.0.1" + port = 9000 + [metrics.gql] + port = 9001 + +[database] + type = "postgres" + host = "localhost" + port = 5432 + database = "sushiswap-watcher" + username = "postgres" + password = "postgres" + synchronize = true + logging = false + +[upstream] + [upstream.ethServer] + gqlApiEndpoint = "http://127.0.0.1:8082/graphql" + rpcProviderEndpoints = [ + "http://127.0.0.1:8081" + ] + + # Boolean flag to specify if rpc-eth-client should be used for RPC endpoint instead of ipld-eth-client (ipld-eth-server GQL client) + rpcClient = false + + # Boolean flag to specify if rpcProviderEndpoint is an FEVM RPC endpoint + isFEVM = false + + # Boolean flag to filter event logs by contracts + filterLogsByAddresses = true + # Boolean flag to filter event logs by topics + filterLogsByTopics = true + + [upstream.cache] + name = "requests" + enabled = false + deleteOnStart = false + +[jobQueue] + dbConnectionString = "postgres://postgres:postgres@localhost/sushiswap-watcher-job-queue" + maxCompletionLagInSecs = 300 + jobDelayInMilliSecs = 100 + eventsInBatch = 50 + subgraphEventsOrder = true + blockDelayInMilliSecs = 2000 + + # Boolean to switch between modes of processing events when starting the server. + # Setting to true will fetch filtered events and required blocks in a range of blocks and then process them. + # Setting to false will fetch blocks consecutively with its events and then process them (Behaviour is followed in realtime processing near head). + useBlockRanges = true + + # Block range in which logs are fetched during historical blocks processing + historicalLogsBlockRange = 2000 + + # Max block range of historical processing after which it waits for completion of events processing + # If set to -1 historical processing does not wait for events processing and completes till latest canonical block + historicalMaxFetchAhead = 10000 + + # Max number of retries to fetch new block after which watcher will failover to other RPC endpoints + maxNewBlockRetries = 3 diff --git a/packages/sushiswap-watcher/gql-logs/watcher-gql-error.log b/packages/sushiswap-watcher/gql-logs/watcher-gql-error.log new file mode 100644 index 0000000..e69de29 diff --git a/packages/sushiswap-watcher/gql-logs/watcher-gql.log b/packages/sushiswap-watcher/gql-logs/watcher-gql.log new file mode 100644 index 0000000..e69de29 diff --git a/packages/sushiswap-watcher/package.json b/packages/sushiswap-watcher/package.json new file mode 100644 index 0000000..d7c5689 --- /dev/null +++ b/packages/sushiswap-watcher/package.json @@ -0,0 +1,76 @@ +{ + "name": "@cerc-io/sushiswap-watcher", + "version": "0.1.0", + "description": "sushiswap-watcher", + "private": true, + "main": "dist/index.js", + "scripts": { + "lint": "eslint --max-warnings=0 .", + "build": "yarn clean && tsc && yarn copy-assets", + "clean": "rm -rf ./dist", + "prepare": "husky install", + "copy-assets": "copyfiles -u 1 src/**/*.gql dist/", + "server": "DEBUG=vulcanize:* YARN_CHILD_PROCESS=true node --enable-source-maps dist/server.js", + "server:dev": "DEBUG=vulcanize:* YARN_CHILD_PROCESS=true ts-node src/server.ts", + "job-runner": "DEBUG=vulcanize:* YARN_CHILD_PROCESS=true node --enable-source-maps dist/job-runner.js", + "job-runner:dev": "DEBUG=vulcanize:* YARN_CHILD_PROCESS=true ts-node src/job-runner.ts", + "watch:contract": "DEBUG=vulcanize:* ts-node src/cli/watch-contract.ts", + "fill": "DEBUG=vulcanize:* ts-node src/fill.ts", + "fill:state": "DEBUG=vulcanize:* ts-node src/fill.ts --state", + "reset": "DEBUG=vulcanize:* ts-node src/cli/reset.ts", + "checkpoint": "DEBUG=vulcanize:* node --enable-source-maps dist/cli/checkpoint.js", + "checkpoint:dev": "DEBUG=vulcanize:* ts-node src/cli/checkpoint.ts", + "export-state": "DEBUG=vulcanize:* node --enable-source-maps dist/cli/export-state.js", + "export-state:dev": "DEBUG=vulcanize:* ts-node src/cli/export-state.ts", + "import-state": "DEBUG=vulcanize:* node --enable-source-maps dist/cli/import-state.js", + "import-state:dev": "DEBUG=vulcanize:* ts-node src/cli/import-state.ts", + "inspect-cid": "DEBUG=vulcanize:* ts-node src/cli/inspect-cid.ts", + "index-block": "DEBUG=vulcanize:* ts-node src/cli/index-block.ts" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/cerc-io/watcher-ts.git" + }, + "author": "", + "license": "AGPL-3.0", + "bugs": { + "url": "https://github.com/cerc-io/watcher-ts/issues" + }, + "homepage": "https://github.com/cerc-io/watcher-ts#readme", + "dependencies": { + "@apollo/client": "^3.3.19", + "@cerc-io/cli": "^0.2.93", + "@cerc-io/ipld-eth-client": "^0.2.93", + "@cerc-io/solidity-mapper": "^0.2.93", + "@cerc-io/util": "^0.2.93", + "@cerc-io/graph-node": "^0.2.93", + "@ethersproject/providers": "^5.4.4", + "debug": "^4.3.1", + "decimal.js": "^10.3.1", + "ethers": "^5.4.4", + "graphql": "^15.5.0", + "json-bigint": "^1.0.0", + "reflect-metadata": "^0.1.13", + "typeorm": "0.2.37", + "yargs": "^17.0.1" + }, + "devDependencies": { + "@ethersproject/abi": "^5.3.0", + "@types/debug": "^4.1.5", + "@types/json-bigint": "^1.0.0", + "@types/yargs": "^17.0.0", + "@typescript-eslint/eslint-plugin": "^5.47.1", + "@typescript-eslint/parser": "^5.47.1", + "copyfiles": "^2.4.1", + "eslint": "^8.35.0", + "eslint-config-semistandard": "^15.0.1", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.1.0", + "eslint-plugin-standard": "^5.0.0", + "husky": "^7.0.2", + "ts-node": "^10.2.1", + "typescript": "^5.0.2" + } +} diff --git a/packages/sushiswap-watcher/src/artifacts/Factory.json b/packages/sushiswap-watcher/src/artifacts/Factory.json new file mode 100644 index 0000000..244820a --- /dev/null +++ b/packages/sushiswap-watcher/src/artifacts/Factory.json @@ -0,0 +1,217 @@ +{ + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_feeToSetter", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "pair", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "PairCreated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "allPairs", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "allPairsLength", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + } + ], + "name": "createPair", + "outputs": [ + { + "internalType": "address", + "name": "pair", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "feeTo", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeToSetter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "getPair", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "migrator", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pairCodeHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_feeTo", + "type": "address" + } + ], + "name": "setFeeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_feeToSetter", + "type": "address" + } + ], + "name": "setFeeToSetter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_migrator", + "type": "address" + } + ], + "name": "setMigrator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/artifacts/Pair.json b/packages/sushiswap-watcher/src/artifacts/Pair.json new file mode 100644 index 0000000..26f7c8c --- /dev/null +++ b/packages/sushiswap-watcher/src/artifacts/Pair.json @@ -0,0 +1,660 @@ +{ + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1In", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "Swap", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint112", + "name": "reserve0", + "type": "uint112" + }, + { + "indexed": false, + "internalType": "uint112", + "name": "reserve1", + "type": "uint112" + } + ], + "name": "Sync", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MINIMUM_LIQUIDITY", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PERMIT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getReserves", + "outputs": [ + { + "internalType": "uint112", + "name": "_reserve0", + "type": "uint112" + }, + { + "internalType": "uint112", + "name": "_reserve1", + "type": "uint112" + }, + { + "internalType": "uint32", + "name": "_blockTimestampLast", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_token0", + "type": "address" + }, + { + "internalType": "address", + "name": "_token1", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "kLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "price0CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "price1CumulativeLast", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "skim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount0Out", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Out", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "sync", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "token0", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token1", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ] +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/cli/checkpoint-cmds/create.ts b/packages/sushiswap-watcher/src/cli/checkpoint-cmds/create.ts new file mode 100644 index 0000000..e771c70 --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/checkpoint-cmds/create.ts @@ -0,0 +1,44 @@ +// +// Copyright 2022 Vulcanize, Inc. +// + +import { CreateCheckpointCmd } from '@cerc-io/cli'; +import { getGraphDbAndWatcher } from '@cerc-io/graph-node'; + +import { Database, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP } from '../../database'; +import { Indexer } from '../../indexer'; + +export const command = 'create'; + +export const desc = 'Create checkpoint'; + +export const builder = { + address: { + type: 'string', + require: true, + demandOption: true, + describe: 'Contract address to create the checkpoint for.' + }, + blockHash: { + type: 'string', + describe: 'Blockhash at which to create the checkpoint.' + } +}; + +export const handler = async (argv: any): Promise => { + const createCheckpointCmd = new CreateCheckpointCmd(); + await createCheckpointCmd.init(argv, Database); + + const { graphWatcher } = await getGraphDbAndWatcher( + createCheckpointCmd.config.server, + createCheckpointCmd.clients.ethClient, + createCheckpointCmd.ethProvider, + createCheckpointCmd.database.baseDatabase, + ENTITY_QUERY_TYPE_MAP, + ENTITY_TO_LATEST_ENTITY_MAP + ); + + await createCheckpointCmd.initIndexer(Indexer, graphWatcher); + + await createCheckpointCmd.exec(); +}; diff --git a/packages/sushiswap-watcher/src/cli/checkpoint-cmds/verify.ts b/packages/sushiswap-watcher/src/cli/checkpoint-cmds/verify.ts new file mode 100644 index 0000000..3709f54 --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/checkpoint-cmds/verify.ts @@ -0,0 +1,40 @@ +// +// Copyright 2022 Vulcanize, Inc. +// + +import { VerifyCheckpointCmd } from '@cerc-io/cli'; +import { getGraphDbAndWatcher } from '@cerc-io/graph-node'; + +import { Database, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP } from '../../database'; +import { Indexer } from '../../indexer'; + +export const command = 'verify'; + +export const desc = 'Verify checkpoint'; + +export const builder = { + cid: { + type: 'string', + alias: 'c', + demandOption: true, + describe: 'Checkpoint CID to be verified' + } +}; + +export const handler = async (argv: any): Promise => { + const verifyCheckpointCmd = new VerifyCheckpointCmd(); + await verifyCheckpointCmd.init(argv, Database); + + const { graphWatcher, graphDb } = await getGraphDbAndWatcher( + verifyCheckpointCmd.config.server, + verifyCheckpointCmd.clients.ethClient, + verifyCheckpointCmd.ethProvider, + verifyCheckpointCmd.database.baseDatabase, + ENTITY_QUERY_TYPE_MAP, + ENTITY_TO_LATEST_ENTITY_MAP + ); + + await verifyCheckpointCmd.initIndexer(Indexer, graphWatcher); + + await verifyCheckpointCmd.exec(graphDb); +}; diff --git a/packages/sushiswap-watcher/src/cli/checkpoint.ts b/packages/sushiswap-watcher/src/cli/checkpoint.ts new file mode 100644 index 0000000..d05ad8a --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/checkpoint.ts @@ -0,0 +1,39 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import yargs from 'yargs'; +import 'reflect-metadata'; +import debug from 'debug'; + +import { DEFAULT_CONFIG_PATH } from '@cerc-io/util'; + +import { hideBin } from 'yargs/helpers'; + +const log = debug('vulcanize:checkpoint'); + +const main = async () => { + return yargs(hideBin(process.argv)) + .parserConfiguration({ + 'parse-numbers': false + }).options({ + configFile: { + alias: 'f', + type: 'string', + require: true, + demandOption: true, + describe: 'configuration file path (toml)', + default: DEFAULT_CONFIG_PATH + } + }) + .commandDir('checkpoint-cmds', { extensions: ['ts', 'js'], exclude: /([a-zA-Z0-9\s_\\.\-:])+(.d.ts)$/ }) + .demandCommand(1) + .help() + .argv; +}; + +main().then(() => { + process.exit(); +}).catch(err => { + log(err); +}); diff --git a/packages/sushiswap-watcher/src/cli/export-state.ts b/packages/sushiswap-watcher/src/cli/export-state.ts new file mode 100644 index 0000000..bcd1c8a --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/export-state.ts @@ -0,0 +1,38 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import 'reflect-metadata'; +import debug from 'debug'; + +import { ExportStateCmd } from '@cerc-io/cli'; +import { getGraphDbAndWatcher } from '@cerc-io/graph-node'; + +import { Database, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP } from '../database'; +import { Indexer } from '../indexer'; + +const log = debug('vulcanize:export-state'); + +const main = async (): Promise => { + const exportStateCmd = new ExportStateCmd(); + await exportStateCmd.init(Database); + + const { graphWatcher } = await getGraphDbAndWatcher( + exportStateCmd.config.server, + exportStateCmd.clients.ethClient, + exportStateCmd.ethProvider, + exportStateCmd.database.baseDatabase, + ENTITY_QUERY_TYPE_MAP, + ENTITY_TO_LATEST_ENTITY_MAP + ); + + await exportStateCmd.initIndexer(Indexer, graphWatcher); + + await exportStateCmd.exec(); +}; + +main().catch(err => { + log(err); +}).finally(() => { + process.exit(0); +}); diff --git a/packages/sushiswap-watcher/src/cli/import-state.ts b/packages/sushiswap-watcher/src/cli/import-state.ts new file mode 100644 index 0000000..04ce0e8 --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/import-state.ts @@ -0,0 +1,39 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import 'reflect-metadata'; +import debug from 'debug'; + +import { ImportStateCmd } from '@cerc-io/cli'; +import { getGraphDbAndWatcher } from '@cerc-io/graph-node'; + +import { Database, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP } from '../database'; +import { Indexer } from '../indexer'; +import { State } from '../entity/State'; + +const log = debug('vulcanize:import-state'); + +export const main = async (): Promise => { + const importStateCmd = new ImportStateCmd(); + await importStateCmd.init(Database); + + const { graphWatcher, graphDb } = await getGraphDbAndWatcher( + importStateCmd.config.server, + importStateCmd.clients.ethClient, + importStateCmd.ethProvider, + importStateCmd.database.baseDatabase, + ENTITY_QUERY_TYPE_MAP, + ENTITY_TO_LATEST_ENTITY_MAP + ); + + await importStateCmd.initIndexer(Indexer, graphWatcher); + + await importStateCmd.exec(State, graphDb); +}; + +main().catch(err => { + log(err); +}).finally(() => { + process.exit(0); +}); diff --git a/packages/sushiswap-watcher/src/cli/index-block.ts b/packages/sushiswap-watcher/src/cli/index-block.ts new file mode 100644 index 0000000..19a302a --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/index-block.ts @@ -0,0 +1,38 @@ +// +// Copyright 2022 Vulcanize, Inc. +// + +import 'reflect-metadata'; +import debug from 'debug'; + +import { IndexBlockCmd } from '@cerc-io/cli'; +import { getGraphDbAndWatcher } from '@cerc-io/graph-node'; + +import { Database, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP } from '../database'; +import { Indexer } from '../indexer'; + +const log = debug('vulcanize:index-block'); + +const main = async (): Promise => { + const indexBlockCmd = new IndexBlockCmd(); + await indexBlockCmd.init(Database); + + const { graphWatcher } = await getGraphDbAndWatcher( + indexBlockCmd.config.server, + indexBlockCmd.clients.ethClient, + indexBlockCmd.ethProvider, + indexBlockCmd.database.baseDatabase, + ENTITY_QUERY_TYPE_MAP, + ENTITY_TO_LATEST_ENTITY_MAP + ); + + await indexBlockCmd.initIndexer(Indexer, graphWatcher); + + await indexBlockCmd.exec(); +}; + +main().catch(err => { + log(err); +}).finally(() => { + process.exit(0); +}); diff --git a/packages/sushiswap-watcher/src/cli/inspect-cid.ts b/packages/sushiswap-watcher/src/cli/inspect-cid.ts new file mode 100644 index 0000000..4f5955e --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/inspect-cid.ts @@ -0,0 +1,38 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import 'reflect-metadata'; +import debug from 'debug'; + +import { InspectCIDCmd } from '@cerc-io/cli'; +import { getGraphDbAndWatcher } from '@cerc-io/graph-node'; + +import { Database, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP } from '../database'; +import { Indexer } from '../indexer'; + +const log = debug('vulcanize:inspect-cid'); + +const main = async (): Promise => { + const inspectCIDCmd = new InspectCIDCmd(); + await inspectCIDCmd.init(Database); + + const { graphWatcher } = await getGraphDbAndWatcher( + inspectCIDCmd.config.server, + inspectCIDCmd.clients.ethClient, + inspectCIDCmd.ethProvider, + inspectCIDCmd.database.baseDatabase, + ENTITY_QUERY_TYPE_MAP, + ENTITY_TO_LATEST_ENTITY_MAP + ); + + await inspectCIDCmd.initIndexer(Indexer, graphWatcher); + + await inspectCIDCmd.exec(); +}; + +main().catch(err => { + log(err); +}).finally(() => { + process.exit(0); +}); diff --git a/packages/sushiswap-watcher/src/cli/reset-cmds/job-queue.ts b/packages/sushiswap-watcher/src/cli/reset-cmds/job-queue.ts new file mode 100644 index 0000000..c33cbfd --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/reset-cmds/job-queue.ts @@ -0,0 +1,22 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import debug from 'debug'; + +import { getConfig, resetJobs, Config } from '@cerc-io/util'; + +const log = debug('vulcanize:reset-job-queue'); + +export const command = 'job-queue'; + +export const desc = 'Reset job queue'; + +export const builder = {}; + +export const handler = async (argv: any): Promise => { + const config: Config = await getConfig(argv.configFile); + await resetJobs(config); + + log('Job queue reset successfully'); +}; diff --git a/packages/sushiswap-watcher/src/cli/reset-cmds/state.ts b/packages/sushiswap-watcher/src/cli/reset-cmds/state.ts new file mode 100644 index 0000000..33211d6 --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/reset-cmds/state.ts @@ -0,0 +1,24 @@ +// +// Copyright 2022 Vulcanize, Inc. +// + +import { ResetStateCmd } from '@cerc-io/cli'; + +import { Database } from '../../database'; + +export const command = 'state'; + +export const desc = 'Reset State to a given block number'; + +export const builder = { + blockNumber: { + type: 'number' + } +}; + +export const handler = async (argv: any): Promise => { + const resetStateCmd = new ResetStateCmd(); + await resetStateCmd.init(argv, Database); + + await resetStateCmd.exec(); +}; diff --git a/packages/sushiswap-watcher/src/cli/reset-cmds/watcher.ts b/packages/sushiswap-watcher/src/cli/reset-cmds/watcher.ts new file mode 100644 index 0000000..827fd28 --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/reset-cmds/watcher.ts @@ -0,0 +1,37 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { ResetWatcherCmd } from '@cerc-io/cli'; +import { getGraphDbAndWatcher } from '@cerc-io/graph-node'; + +import { Database, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP } from '../../database'; +import { Indexer } from '../../indexer'; + +export const command = 'watcher'; + +export const desc = 'Reset watcher to a block number'; + +export const builder = { + blockNumber: { + type: 'number' + } +}; + +export const handler = async (argv: any): Promise => { + const resetWatcherCmd = new ResetWatcherCmd(); + await resetWatcherCmd.init(argv, Database); + + const { graphWatcher } = await getGraphDbAndWatcher( + resetWatcherCmd.config.server, + resetWatcherCmd.clients.ethClient, + resetWatcherCmd.ethProvider, + resetWatcherCmd.database.baseDatabase, + ENTITY_QUERY_TYPE_MAP, + ENTITY_TO_LATEST_ENTITY_MAP + ); + + await resetWatcherCmd.initIndexer(Indexer, graphWatcher); + + await resetWatcherCmd.exec(); +}; diff --git a/packages/sushiswap-watcher/src/cli/reset.ts b/packages/sushiswap-watcher/src/cli/reset.ts new file mode 100644 index 0000000..95648c8 --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/reset.ts @@ -0,0 +1,24 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import 'reflect-metadata'; +import debug from 'debug'; + +import { getResetYargs } from '@cerc-io/util'; + +const log = debug('vulcanize:reset'); + +const main = async () => { + return getResetYargs() + .commandDir('reset-cmds', { extensions: ['ts', 'js'], exclude: /([a-zA-Z0-9\s_\\.\-:])+(.d.ts)$/ }) + .demandCommand(1) + .help() + .argv; +}; + +main().then(() => { + process.exit(); +}).catch(err => { + log(err); +}); diff --git a/packages/sushiswap-watcher/src/cli/watch-contract.ts b/packages/sushiswap-watcher/src/cli/watch-contract.ts new file mode 100644 index 0000000..7d6ce1a --- /dev/null +++ b/packages/sushiswap-watcher/src/cli/watch-contract.ts @@ -0,0 +1,38 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import 'reflect-metadata'; +import debug from 'debug'; + +import { WatchContractCmd } from '@cerc-io/cli'; +import { getGraphDbAndWatcher } from '@cerc-io/graph-node'; + +import { Database, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP } from '../database'; +import { Indexer } from '../indexer'; + +const log = debug('vulcanize:watch-contract'); + +const main = async (): Promise => { + const watchContractCmd = new WatchContractCmd(); + await watchContractCmd.init(Database); + + const { graphWatcher } = await getGraphDbAndWatcher( + watchContractCmd.config.server, + watchContractCmd.clients.ethClient, + watchContractCmd.ethProvider, + watchContractCmd.database.baseDatabase, + ENTITY_QUERY_TYPE_MAP, + ENTITY_TO_LATEST_ENTITY_MAP + ); + + await watchContractCmd.initIndexer(Indexer, graphWatcher); + + await watchContractCmd.exec(); +}; + +main().catch(err => { + log(err); +}).finally(() => { + process.exit(0); +}); diff --git a/packages/sushiswap-watcher/src/client.ts b/packages/sushiswap-watcher/src/client.ts new file mode 100644 index 0000000..8bb2bb0 --- /dev/null +++ b/packages/sushiswap-watcher/src/client.ts @@ -0,0 +1,55 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { gql } from '@apollo/client/core'; +import { GraphQLClient, GraphQLConfig } from '@cerc-io/ipld-eth-client'; + +import { queries, mutations, subscriptions } from './gql'; + +export class Client { + _config: GraphQLConfig; + _client: GraphQLClient; + + constructor (config: GraphQLConfig) { + this._config = config; + + this._client = new GraphQLClient(config); + } + + async getEvents (blockHash: string, contractAddress: string, name: string): Promise { + const { events } = await this._client.query( + gql(queries.events), + { blockHash, contractAddress, name } + ); + + return events; + } + + async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise { + const { eventsInRange } = await this._client.query( + gql(queries.eventsInRange), + { fromBlockNumber, toBlockNumber } + ); + + return eventsInRange; + } + + async watchContract (contractAddress: string, startingBlock?: number): Promise { + const { watchContract } = await this._client.mutate( + gql(mutations.watchContract), + { contractAddress, startingBlock } + ); + + return watchContract; + } + + async watchEvents (onNext: (value: any) => void): Promise { + return this._client.subscribe( + gql(subscriptions.onEvent), + ({ data }) => { + onNext(data.onEvent); + } + ); + } +} diff --git a/packages/sushiswap-watcher/src/database.ts b/packages/sushiswap-watcher/src/database.ts new file mode 100644 index 0000000..05ef00e --- /dev/null +++ b/packages/sushiswap-watcher/src/database.ts @@ -0,0 +1,304 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import assert from 'assert'; +import { Connection, ConnectionOptions, DeepPartial, FindConditions, QueryRunner, FindManyOptions, EntityTarget } from 'typeorm'; +import path from 'path'; + +import { + ENTITY_QUERY_TYPE, + Database as BaseDatabase, + DatabaseInterface, + QueryOptions, + StateKind, + Where +} from '@cerc-io/util'; + +import { Contract } from './entity/Contract'; +import { Event } from './entity/Event'; +import { SyncStatus } from './entity/SyncStatus'; +import { StateSyncStatus } from './entity/StateSyncStatus'; +import { BlockProgress } from './entity/BlockProgress'; +import { State } from './entity/State'; +import { Factory } from './entity/Factory'; +import { Bundle } from './entity/Bundle'; +import { Token } from './entity/Token'; +import { TokenPrice } from './entity/TokenPrice'; +import { _TokenPair } from './entity/_TokenPair'; +import { _WhitelistedTokenPair } from './entity/_WhitelistedTokenPair'; +import { Pair } from './entity/Pair'; +import { User } from './entity/User'; +import { LiquidityPosition } from './entity/LiquidityPosition'; +import { Mint } from './entity/Mint'; +import { Burn } from './entity/Burn'; +import { Swap } from './entity/Swap'; +import { Transaction } from './entity/Transaction'; +import { LiquidityPositionSnapshot } from './entity/LiquidityPositionSnapshot'; +import { PairHourSnapshot } from './entity/PairHourSnapshot'; +import { PairDaySnapshot } from './entity/PairDaySnapshot'; +import { TokenHourSnapshot } from './entity/TokenHourSnapshot'; +import { TokenDaySnapshot } from './entity/TokenDaySnapshot'; +import { FactoryHourSnapshot } from './entity/FactoryHourSnapshot'; +import { FactoryDaySnapshot } from './entity/FactoryDaySnapshot'; + +export const SUBGRAPH_ENTITIES = new Set([Factory, Bundle, Token, TokenPrice, _TokenPair, _WhitelistedTokenPair, Pair, User, LiquidityPosition, Mint, Burn, Swap, Transaction, LiquidityPositionSnapshot, PairHourSnapshot, PairDaySnapshot, TokenHourSnapshot, TokenDaySnapshot, FactoryHourSnapshot, FactoryDaySnapshot]); +export const ENTITIES = [...SUBGRAPH_ENTITIES]; +// Map: Entity to suitable query type. +export const ENTITY_QUERY_TYPE_MAP = new Map any, ENTITY_QUERY_TYPE>([]); + +export const ENTITY_TO_LATEST_ENTITY_MAP: Map = new Map(); + +export class Database implements DatabaseInterface { + _config: ConnectionOptions; + _conn!: Connection; + _baseDatabase: BaseDatabase; + _propColMaps: { [key: string]: Map; }; + + constructor (config: ConnectionOptions) { + assert(config); + + this._config = { + ...config, + subscribers: [path.join(__dirname, 'entity/Subscriber.*')], + entities: [path.join(__dirname, 'entity/*')] + }; + + this._baseDatabase = new BaseDatabase(this._config); + this._propColMaps = {}; + } + + get baseDatabase (): BaseDatabase { + return this._baseDatabase; + } + + async init (): Promise { + this._conn = await this._baseDatabase.init(); + } + + async close (): Promise { + return this._baseDatabase.close(); + } + + getNewState (): State { + return new State(); + } + + async getStates (where: FindConditions): Promise { + const repo = this._conn.getRepository(State); + + return this._baseDatabase.getStates(repo, where); + } + + async getLatestState (contractAddress: string, kind: StateKind | null, blockNumber?: number): Promise { + const repo = this._conn.getRepository(State); + + return this._baseDatabase.getLatestState(repo, contractAddress, kind, blockNumber); + } + + async getPrevState (blockHash: string, contractAddress: string, kind?: string): Promise { + const repo = this._conn.getRepository(State); + + return this._baseDatabase.getPrevState(repo, blockHash, contractAddress, kind); + } + + // Fetch all diff States after the specified block number. + async getDiffStatesInRange (contractAddress: string, startblock: number, endBlock: number): Promise { + const repo = this._conn.getRepository(State); + + return this._baseDatabase.getDiffStatesInRange(repo, contractAddress, startblock, endBlock); + } + + async saveOrUpdateState (dbTx: QueryRunner, state: State): Promise { + const repo = dbTx.manager.getRepository(State); + + return this._baseDatabase.saveOrUpdateState(repo, state); + } + + async removeStates (dbTx: QueryRunner, blockNumber: number, kind: string): Promise { + const repo = dbTx.manager.getRepository(State); + + await this._baseDatabase.removeStates(repo, blockNumber, kind); + } + + async removeStatesAfterBlock (dbTx: QueryRunner, blockNumber: number): Promise { + const repo = dbTx.manager.getRepository(State); + + await this._baseDatabase.removeStatesAfterBlock(repo, blockNumber); + } + + async getStateSyncStatus (): Promise { + const repo = this._conn.getRepository(StateSyncStatus); + + return this._baseDatabase.getStateSyncStatus(repo); + } + + async updateStateSyncStatusIndexedBlock (queryRunner: QueryRunner, blockNumber: number, force?: boolean): Promise { + const repo = queryRunner.manager.getRepository(StateSyncStatus); + + return this._baseDatabase.updateStateSyncStatusIndexedBlock(repo, blockNumber, force); + } + + async updateStateSyncStatusCheckpointBlock (queryRunner: QueryRunner, blockNumber: number, force?: boolean): Promise { + const repo = queryRunner.manager.getRepository(StateSyncStatus); + + return this._baseDatabase.updateStateSyncStatusCheckpointBlock(repo, blockNumber, force); + } + + async getContracts (): Promise { + const repo = this._conn.getRepository(Contract); + + return this._baseDatabase.getContracts(repo); + } + + async createTransactionRunner (): Promise { + return this._baseDatabase.createTransactionRunner(); + } + + async getProcessedBlockCountForRange (fromBlockNumber: number, toBlockNumber: number): Promise<{ expected: number, actual: number }> { + const repo = this._conn.getRepository(BlockProgress); + + return this._baseDatabase.getProcessedBlockCountForRange(repo, fromBlockNumber, toBlockNumber); + } + + async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise> { + const repo = this._conn.getRepository(Event); + + return this._baseDatabase.getEventsInRange(repo, fromBlockNumber, toBlockNumber); + } + + async saveEventEntity (queryRunner: QueryRunner, entity: Event): Promise { + const repo = queryRunner.manager.getRepository(Event); + return this._baseDatabase.saveEventEntity(repo, entity); + } + + async getBlockEvents (blockHash: string, where: Where, queryOptions: QueryOptions): Promise { + const repo = this._conn.getRepository(Event); + + return this._baseDatabase.getBlockEvents(repo, blockHash, where, queryOptions); + } + + async saveBlockWithEvents (queryRunner: QueryRunner, block: DeepPartial, events: DeepPartial[]): Promise { + const blockRepo = queryRunner.manager.getRepository(BlockProgress); + const eventRepo = queryRunner.manager.getRepository(Event); + + return this._baseDatabase.saveBlockWithEvents(blockRepo, eventRepo, block, events); + } + + async saveEvents (queryRunner: QueryRunner, events: Event[]): Promise { + const eventRepo = queryRunner.manager.getRepository(Event); + + return this._baseDatabase.saveEvents(eventRepo, events); + } + + async saveBlockProgress (queryRunner: QueryRunner, block: DeepPartial): Promise { + const repo = queryRunner.manager.getRepository(BlockProgress); + + return this._baseDatabase.saveBlockProgress(repo, block); + } + + async saveContract (queryRunner: QueryRunner, address: string, kind: string, checkpoint: boolean, startingBlock: number, context?: any): Promise { + const repo = queryRunner.manager.getRepository(Contract); + + return this._baseDatabase.saveContract(repo, address, kind, checkpoint, startingBlock, context); + } + + async updateSyncStatusIndexedBlock (queryRunner: QueryRunner, blockHash: string, blockNumber: number, force = false): Promise { + const repo = queryRunner.manager.getRepository(SyncStatus); + + return this._baseDatabase.updateSyncStatusIndexedBlock(repo, blockHash, blockNumber, force); + } + + async updateSyncStatusCanonicalBlock (queryRunner: QueryRunner, blockHash: string, blockNumber: number, force = false): Promise { + const repo = queryRunner.manager.getRepository(SyncStatus); + + return this._baseDatabase.updateSyncStatusCanonicalBlock(repo, blockHash, blockNumber, force); + } + + async updateSyncStatusChainHead (queryRunner: QueryRunner, blockHash: string, blockNumber: number, force = false): Promise { + const repo = queryRunner.manager.getRepository(SyncStatus); + + return this._baseDatabase.updateSyncStatusChainHead(repo, blockHash, blockNumber, force); + } + + async updateSyncStatusProcessedBlock (queryRunner: QueryRunner, blockHash: string, blockNumber: number, force = false): Promise { + const repo = queryRunner.manager.getRepository(SyncStatus); + + return this._baseDatabase.updateSyncStatusProcessedBlock(repo, blockHash, blockNumber, force); + } + + async updateSyncStatusIndexingError (queryRunner: QueryRunner, hasIndexingError: boolean): Promise { + const repo = queryRunner.manager.getRepository(SyncStatus); + + return this._baseDatabase.updateSyncStatusIndexingError(repo, hasIndexingError); + } + + async updateSyncStatus (queryRunner: QueryRunner, syncStatus: DeepPartial): Promise { + const repo = queryRunner.manager.getRepository(SyncStatus); + + return this._baseDatabase.updateSyncStatus(repo, syncStatus); + } + + async getSyncStatus (queryRunner: QueryRunner): Promise { + const repo = queryRunner.manager.getRepository(SyncStatus); + + return this._baseDatabase.getSyncStatus(repo); + } + + async getEvent (id: string): Promise { + const repo = this._conn.getRepository(Event); + + return this._baseDatabase.getEvent(repo, id); + } + + async getBlocksAtHeight (height: number, isPruned: boolean): Promise { + const repo = this._conn.getRepository(BlockProgress); + + return this._baseDatabase.getBlocksAtHeight(repo, height, isPruned); + } + + async markBlocksAsPruned (queryRunner: QueryRunner, blocks: BlockProgress[]): Promise { + const repo = queryRunner.manager.getRepository(BlockProgress); + + return this._baseDatabase.markBlocksAsPruned(repo, blocks); + } + + async getBlockProgress (blockHash: string): Promise { + const repo = this._conn.getRepository(BlockProgress); + return this._baseDatabase.getBlockProgress(repo, blockHash); + } + + async getBlockProgressEntities (where: FindConditions, options: FindManyOptions): Promise { + const repo = this._conn.getRepository(BlockProgress); + + return this._baseDatabase.getBlockProgressEntities(repo, where, options); + } + + async getEntitiesForBlock (blockHash: string, tableName: string): Promise { + return this._baseDatabase.getEntitiesForBlock(blockHash, tableName); + } + + async updateBlockProgress (queryRunner: QueryRunner, block: BlockProgress, lastProcessedEventIndex: number): Promise { + const repo = queryRunner.manager.getRepository(BlockProgress); + + return this._baseDatabase.updateBlockProgress(repo, block, lastProcessedEventIndex); + } + + async removeEntities (queryRunner: QueryRunner, entity: new () => Entity, findConditions?: FindManyOptions | FindConditions): Promise { + return this._baseDatabase.removeEntities(queryRunner, entity, findConditions); + } + + async deleteEntitiesByConditions (queryRunner: QueryRunner, entity: EntityTarget, findConditions: FindConditions): Promise { + await this._baseDatabase.deleteEntitiesByConditions(queryRunner, entity, findConditions); + } + + async getAncestorAtHeight (blockHash: string, height: number): Promise { + return this._baseDatabase.getAncestorAtHeight(blockHash, height); + } + + _getPropertyColumnMapForEntity (entityName: string): Map { + return this._conn.getMetadata(entityName).ownColumns.reduce((acc, curr) => { + return acc.set(curr.propertyName, curr.databaseName); + }, new Map()); + } +} diff --git a/packages/sushiswap-watcher/src/entity/BlockProgress.ts b/packages/sushiswap-watcher/src/entity/BlockProgress.ts new file mode 100644 index 0000000..ded4a86 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/BlockProgress.ts @@ -0,0 +1,48 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryGeneratedColumn, Column, Index, CreateDateColumn } from 'typeorm'; +import { BlockProgressInterface } from '@cerc-io/util'; + +@Entity() +@Index(['blockHash'], { unique: true }) +@Index(['blockNumber']) +@Index(['parentHash']) +export class BlockProgress implements BlockProgressInterface { + @PrimaryGeneratedColumn() + id!: number; + + @Column('varchar', { nullable: true }) + cid!: string | null; + + @Column('varchar', { length: 66 }) + blockHash!: string; + + @Column('varchar', { length: 66 }) + parentHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('integer') + blockTimestamp!: number; + + @Column('integer') + numEvents!: number; + + @Column('integer') + numProcessedEvents!: number; + + @Column('integer') + lastProcessedEventIndex!: number; + + @Column('boolean') + isComplete!: boolean; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @CreateDateColumn() + createdAt!: Date; +} diff --git a/packages/sushiswap-watcher/src/entity/Bundle.ts b/packages/sushiswap-watcher/src/entity/Bundle.ts new file mode 100644 index 0000000..140aa41 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/Bundle.ts @@ -0,0 +1,29 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class Bundle { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('numeric', { transformer: decimalTransformer }) + nativePrice!: Decimal; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/Burn.ts b/packages/sushiswap-watcher/src/entity/Burn.ts new file mode 100644 index 0000000..7d39724 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/Burn.ts @@ -0,0 +1,65 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class Burn { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + transaction!: string; + + @Column('numeric', { transformer: bigintTransformer }) + timestamp!: bigint; + + @Column('varchar') + pair!: string; + + @Column('numeric', { transformer: decimalTransformer }) + liquidity!: Decimal; + + @Column('varchar', { nullable: true }) + sender!: string | null; + + @Column('numeric', { nullable: true, transformer: decimalTransformer }) + amount0!: Decimal | null; + + @Column('numeric', { nullable: true, transformer: decimalTransformer }) + amount1!: Decimal | null; + + @Column('varchar', { nullable: true }) + to!: string | null; + + @Column('numeric', { nullable: true, transformer: bigintTransformer }) + logIndex!: bigint | null; + + @Column('numeric', { nullable: true, transformer: decimalTransformer }) + amountUSD!: Decimal | null; + + @Column('boolean') + complete!: boolean; + + @Column('varchar', { nullable: true }) + feeTo!: string | null; + + @Column('numeric', { nullable: true, transformer: decimalTransformer }) + feeLiquidity!: Decimal | null; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/Contract.ts b/packages/sushiswap-watcher/src/entity/Contract.ts new file mode 100644 index 0000000..e4defa8 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/Contract.ts @@ -0,0 +1,27 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryGeneratedColumn, Column, Index } from 'typeorm'; + +@Entity() +@Index(['address'], { unique: true }) +export class Contract { + @PrimaryGeneratedColumn() + id!: number; + + @Column('varchar', { length: 42 }) + address!: string; + + @Column('varchar') + kind!: string; + + @Column('boolean') + checkpoint!: boolean; + + @Column('integer') + startingBlock!: number; + + @Column('jsonb', { nullable: true }) + context!: Record; +} diff --git a/packages/sushiswap-watcher/src/entity/Event.ts b/packages/sushiswap-watcher/src/entity/Event.ts new file mode 100644 index 0000000..91f1e6d --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/Event.ts @@ -0,0 +1,38 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryGeneratedColumn, Column, Index, ManyToOne } from 'typeorm'; +import { BlockProgress } from './BlockProgress'; + +@Entity() +@Index(['block', 'contract']) +@Index(['block', 'contract', 'eventName']) +export class Event { + @PrimaryGeneratedColumn() + id!: number; + + @ManyToOne(() => BlockProgress, { onDelete: 'CASCADE' }) + block!: BlockProgress; + + @Column('varchar', { length: 66 }) + txHash!: string; + + @Column('integer') + index!: number; + + @Column('varchar', { length: 42 }) + contract!: string; + + @Column('varchar', { length: 256 }) + eventName!: string; + + @Column('text') + eventInfo!: string; + + @Column('text') + extraInfo!: string; + + @Column('text') + proof!: string; +} diff --git a/packages/sushiswap-watcher/src/entity/Factory.ts b/packages/sushiswap-watcher/src/entity/Factory.ts new file mode 100644 index 0000000..5107e05 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/Factory.ts @@ -0,0 +1,60 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { PairType } from '../types'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class Factory { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column({ type: 'enum', enum: PairType }) + type!: PairType; + + @Column('numeric', { transformer: decimalTransformer }) + volumeUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesNative!: Decimal; + + @Column('numeric', { transformer: bigintTransformer }) + pairCount!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + transactionCount!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + tokenCount!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + userCount!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/FactoryDaySnapshot.ts b/packages/sushiswap-watcher/src/entity/FactoryDaySnapshot.ts new file mode 100644 index 0000000..c230214 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/FactoryDaySnapshot.ts @@ -0,0 +1,53 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class FactoryDaySnapshot { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + factory!: string; + + @Column('integer') + date!: number; + + @Column('numeric', { transformer: decimalTransformer }) + volumeUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesUSD!: Decimal; + + @Column('numeric', { transformer: bigintTransformer }) + transactionCount!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/FactoryHourSnapshot.ts b/packages/sushiswap-watcher/src/entity/FactoryHourSnapshot.ts new file mode 100644 index 0000000..86a7adc --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/FactoryHourSnapshot.ts @@ -0,0 +1,53 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class FactoryHourSnapshot { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + factory!: string; + + @Column('integer') + date!: number; + + @Column('numeric', { transformer: decimalTransformer }) + volumeUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesUSD!: Decimal; + + @Column('numeric', { transformer: bigintTransformer }) + transactionCount!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/FrothyEntity.ts b/packages/sushiswap-watcher/src/entity/FrothyEntity.ts new file mode 100644 index 0000000..9898ce8 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/FrothyEntity.ts @@ -0,0 +1,21 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; + +@Entity() +@Index(['blockNumber']) +export class FrothyEntity { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar') + name!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; +} diff --git a/packages/sushiswap-watcher/src/entity/LiquidityPosition.ts b/packages/sushiswap-watcher/src/entity/LiquidityPosition.ts new file mode 100644 index 0000000..2130c9d --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/LiquidityPosition.ts @@ -0,0 +1,40 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { bigintTransformer } from '@cerc-io/util'; + +@Entity() +@Index(['blockNumber']) +export class LiquidityPosition { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + pair!: string; + + @Column('varchar') + user!: string; + + @Column('numeric', { transformer: bigintTransformer }) + balance!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + createdAtBlock!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + createdAtTimestamp!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/LiquidityPositionSnapshot.ts b/packages/sushiswap-watcher/src/entity/LiquidityPositionSnapshot.ts new file mode 100644 index 0000000..138eb77 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/LiquidityPositionSnapshot.ts @@ -0,0 +1,62 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class LiquidityPositionSnapshot { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + liquidityPosition!: string; + + @Column('integer') + timestamp!: number; + + @Column('integer') + block!: number; + + @Column('varchar') + user!: string; + + @Column('varchar') + pair!: string; + + @Column('numeric', { transformer: decimalTransformer }) + token0PriceUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + token1PriceUSD!: Decimal; + + @Column('numeric', { transformer: bigintTransformer }) + reserve0!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + reserve1!: bigint; + + @Column('numeric', { transformer: decimalTransformer }) + reserveUSD!: Decimal; + + @Column('numeric', { transformer: bigintTransformer }) + liquidityTokenTotalSupply!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + liquidityTokenBalance!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/Mint.ts b/packages/sushiswap-watcher/src/entity/Mint.ts new file mode 100644 index 0000000..ec1512a --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/Mint.ts @@ -0,0 +1,62 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class Mint { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + transaction!: string; + + @Column('numeric', { transformer: bigintTransformer }) + timestamp!: bigint; + + @Column('varchar') + pair!: string; + + @Column('varchar') + to!: string; + + @Column('numeric', { transformer: decimalTransformer }) + liquidity!: Decimal; + + @Column('varchar', { nullable: true }) + sender!: string | null; + + @Column('numeric', { nullable: true, transformer: decimalTransformer }) + amount0!: Decimal | null; + + @Column('numeric', { nullable: true, transformer: decimalTransformer }) + amount1!: Decimal | null; + + @Column('numeric', { nullable: true, transformer: bigintTransformer }) + logIndex!: bigint | null; + + @Column('numeric', { nullable: true, transformer: decimalTransformer }) + amountUSD!: Decimal | null; + + @Column('varchar', { nullable: true }) + feeTo!: string | null; + + @Column('numeric', { nullable: true, transformer: decimalTransformer }) + feeLiquidity!: Decimal | null; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/Pair.ts b/packages/sushiswap-watcher/src/entity/Pair.ts new file mode 100644 index 0000000..d81de25 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/Pair.ts @@ -0,0 +1,105 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { PairType } from '../types'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class Pair { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column({ type: 'enum', enum: PairType }) + type!: PairType; + + @Column('numeric', { transformer: bigintTransformer }) + swapFee!: bigint; + + @Column('boolean') + twapEnabled!: boolean; + + @Column('varchar') + name!: string; + + @Column('varchar') + token0!: string; + + @Column('varchar') + token1!: string; + + @Column('varchar') + source!: string; + + @Column('numeric', { transformer: bigintTransformer }) + createdAtBlock!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + createdAtTimestamp!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + reserve0!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + reserve1!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + liquidity!: bigint; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + trackedLiquidityNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + token0Price!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + token1Price!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeToken0!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeToken1!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + apr!: Decimal; + + @Column('numeric', { transformer: bigintTransformer }) + aprUpdatedAtTimestamp!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + txCount!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/PairDaySnapshot.ts b/packages/sushiswap-watcher/src/entity/PairDaySnapshot.ts new file mode 100644 index 0000000..1a49ce5 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/PairDaySnapshot.ts @@ -0,0 +1,68 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class PairDaySnapshot { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + pair!: string; + + @Column('integer') + date!: number; + + @Column('numeric', { transformer: decimalTransformer }) + cumulativeVolumeUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeToken0!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeToken1!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidity!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + apr!: Decimal; + + @Column('numeric', { transformer: bigintTransformer }) + transactionCount!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/PairHourSnapshot.ts b/packages/sushiswap-watcher/src/entity/PairHourSnapshot.ts new file mode 100644 index 0000000..426d5a8 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/PairHourSnapshot.ts @@ -0,0 +1,68 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class PairHourSnapshot { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + pair!: string; + + @Column('integer') + date!: number; + + @Column('numeric', { transformer: decimalTransformer }) + cumulativeVolumeUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeToken0!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeToken1!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidity!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + apr!: Decimal; + + @Column('numeric', { transformer: bigintTransformer }) + transactionCount!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/State.ts b/packages/sushiswap-watcher/src/entity/State.ts new file mode 100644 index 0000000..bc05bca --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/State.ts @@ -0,0 +1,31 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryGeneratedColumn, Column, Index, ManyToOne } from 'typeorm'; +import { StateKind } from '@cerc-io/util'; +import { BlockProgress } from './BlockProgress'; + +@Entity() +@Index(['cid'], { unique: true }) +@Index(['block', 'contractAddress']) +@Index(['block', 'contractAddress', 'kind'], { unique: true }) +export class State { + @PrimaryGeneratedColumn() + id!: number; + + @ManyToOne(() => BlockProgress, { onDelete: 'CASCADE' }) + block!: BlockProgress; + + @Column('varchar', { length: 42 }) + contractAddress!: string; + + @Column('varchar') + cid!: string; + + @Column({ type: 'enum', enum: StateKind }) + kind!: StateKind; + + @Column('bytea') + data!: Buffer; +} diff --git a/packages/sushiswap-watcher/src/entity/StateSyncStatus.ts b/packages/sushiswap-watcher/src/entity/StateSyncStatus.ts new file mode 100644 index 0000000..1535eb4 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/StateSyncStatus.ts @@ -0,0 +1,17 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; + +@Entity() +export class StateSyncStatus { + @PrimaryGeneratedColumn() + id!: number; + + @Column('integer') + latestIndexedBlockNumber!: number; + + @Column('integer') + latestCheckpointBlockNumber!: number; +} diff --git a/packages/sushiswap-watcher/src/entity/Subscriber.ts b/packages/sushiswap-watcher/src/entity/Subscriber.ts new file mode 100644 index 0000000..2cccb84 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/Subscriber.ts @@ -0,0 +1,21 @@ +// +// Copyright 2022 Vulcanize, Inc. +// + +import { EventSubscriber, EntitySubscriberInterface, InsertEvent, UpdateEvent } from 'typeorm'; + +import { afterEntityInsertOrUpdate } from '@cerc-io/util'; + +import { FrothyEntity } from './FrothyEntity'; +import { ENTITY_TO_LATEST_ENTITY_MAP, SUBGRAPH_ENTITIES } from '../database'; + +@EventSubscriber() +export class EntitySubscriber implements EntitySubscriberInterface { + async afterInsert (event: InsertEvent): Promise { + await afterEntityInsertOrUpdate(FrothyEntity, SUBGRAPH_ENTITIES, event, ENTITY_TO_LATEST_ENTITY_MAP); + } + + async afterUpdate (event: UpdateEvent): Promise { + await afterEntityInsertOrUpdate(FrothyEntity, SUBGRAPH_ENTITIES, event, ENTITY_TO_LATEST_ENTITY_MAP); + } +} diff --git a/packages/sushiswap-watcher/src/entity/Swap.ts b/packages/sushiswap-watcher/src/entity/Swap.ts new file mode 100644 index 0000000..f319132 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/Swap.ts @@ -0,0 +1,59 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class Swap { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + transaction!: string; + + @Column('numeric', { transformer: bigintTransformer }) + timestamp!: bigint; + + @Column('varchar') + pair!: string; + + @Column('varchar') + sender!: string; + + @Column('varchar') + tokenIn!: string; + + @Column('varchar') + tokenOut!: string; + + @Column('numeric', { transformer: decimalTransformer }) + amountIn!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + amountOut!: Decimal; + + @Column('varchar') + to!: string; + + @Column('numeric', { nullable: true, transformer: bigintTransformer }) + logIndex!: bigint | null; + + @Column('numeric', { transformer: decimalTransformer }) + amountUSD!: Decimal; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/SyncStatus.ts b/packages/sushiswap-watcher/src/entity/SyncStatus.ts new file mode 100644 index 0000000..cc13c70 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/SyncStatus.ts @@ -0,0 +1,45 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; +import { SyncStatusInterface } from '@cerc-io/util'; + +@Entity() +export class SyncStatus implements SyncStatusInterface { + @PrimaryGeneratedColumn() + id!: number; + + @Column('varchar', { length: 66 }) + chainHeadBlockHash!: string; + + @Column('integer') + chainHeadBlockNumber!: number; + + @Column('varchar', { length: 66 }) + latestIndexedBlockHash!: string; + + @Column('integer') + latestIndexedBlockNumber!: number; + + @Column('varchar', { length: 66 }) + latestProcessedBlockHash!: string; + + @Column('integer') + latestProcessedBlockNumber!: number; + + @Column('varchar', { length: 66 }) + latestCanonicalBlockHash!: string; + + @Column('integer') + latestCanonicalBlockNumber!: number; + + @Column('varchar', { length: 66 }) + initialIndexedBlockHash!: string; + + @Column('integer') + initialIndexedBlockNumber!: number; + + @Column('boolean', { default: false }) + hasIndexingError!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/Token.ts b/packages/sushiswap-watcher/src/entity/Token.ts new file mode 100644 index 0000000..482a899 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/Token.ts @@ -0,0 +1,80 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class Token { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + price!: string; + + @Column('varchar') + symbol!: string; + + @Column('boolean') + symbolSuccess!: boolean; + + @Column('varchar') + name!: string; + + @Column('boolean') + nameSuccess!: boolean; + + @Column('numeric', { transformer: bigintTransformer }) + decimals!: bigint; + + @Column('boolean') + decimalsSuccess!: boolean; + + @Column('numeric', { transformer: bigintTransformer }) + liquidity!: bigint; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volume!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesUSD!: Decimal; + + @Column('numeric', { transformer: bigintTransformer }) + txCount!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + pairCount!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + whitelistedPairCount!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/TokenDaySnapshot.ts b/packages/sushiswap-watcher/src/entity/TokenDaySnapshot.ts new file mode 100644 index 0000000..6472399 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/TokenDaySnapshot.ts @@ -0,0 +1,65 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class TokenDaySnapshot { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('integer') + date!: number; + + @Column('varchar') + token!: string; + + @Column('numeric', { transformer: decimalTransformer }) + liquidity!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volume!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + priceNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + priceUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesUSD!: Decimal; + + @Column('numeric', { transformer: bigintTransformer }) + transactionCount!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/TokenHourSnapshot.ts b/packages/sushiswap-watcher/src/entity/TokenHourSnapshot.ts new file mode 100644 index 0000000..c78ebc7 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/TokenHourSnapshot.ts @@ -0,0 +1,65 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer, bigintTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class TokenHourSnapshot { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('integer') + date!: number; + + @Column('varchar') + token!: string; + + @Column('numeric', { transformer: decimalTransformer }) + liquidity!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + liquidityUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volume!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + volumeUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + priceNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + priceUSD!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + feesUSD!: Decimal; + + @Column('numeric', { transformer: bigintTransformer }) + transactionCount!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/TokenPrice.ts b/packages/sushiswap-watcher/src/entity/TokenPrice.ts new file mode 100644 index 0000000..1730dff --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/TokenPrice.ts @@ -0,0 +1,41 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { decimalTransformer } from '@cerc-io/util'; +import { Decimal } from 'decimal.js'; + +@Entity() +@Index(['blockNumber']) +export class TokenPrice { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + token!: string; + + @Column('numeric', { transformer: decimalTransformer }) + derivedNative!: Decimal; + + @Column('numeric', { transformer: decimalTransformer }) + lastUsdPrice!: Decimal; + + @Column('varchar', { nullable: true }) + pricedOffToken!: string | null; + + @Column('varchar', { nullable: true }) + pricedOffPair!: string | null; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/Transaction.ts b/packages/sushiswap-watcher/src/entity/Transaction.ts new file mode 100644 index 0000000..85f1576 --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/Transaction.ts @@ -0,0 +1,46 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { bigintTransformer } from '@cerc-io/util'; + +@Entity() +@Index(['blockNumber']) +export class Transaction { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('numeric', { transformer: bigintTransformer }) + gasLimit!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + gasPrice!: bigint; + + @Column('varchar', { array: true }) + mints!: string[]; + + @Column('varchar', { array: true }) + burns!: string[]; + + @Column('varchar', { array: true }) + swaps!: string[]; + + @Column('numeric', { transformer: bigintTransformer }) + createdAtBlock!: bigint; + + @Column('numeric', { transformer: bigintTransformer }) + createdAtTimestamp!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/User.ts b/packages/sushiswap-watcher/src/entity/User.ts new file mode 100644 index 0000000..9d6c39c --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/User.ts @@ -0,0 +1,28 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; +import { bigintTransformer } from '@cerc-io/util'; + +@Entity() +@Index(['blockNumber']) +export class User { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('numeric', { transformer: bigintTransformer }) + lpSnapshotsCount!: bigint; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/_TokenPair.ts b/packages/sushiswap-watcher/src/entity/_TokenPair.ts new file mode 100644 index 0000000..3fead9b --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/_TokenPair.ts @@ -0,0 +1,30 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; + +@Entity() +@Index(['blockNumber']) +export class _TokenPair { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + pair!: string; + + @Column('varchar') + token!: string; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/entity/_WhitelistedTokenPair.ts b/packages/sushiswap-watcher/src/entity/_WhitelistedTokenPair.ts new file mode 100644 index 0000000..796c96d --- /dev/null +++ b/packages/sushiswap-watcher/src/entity/_WhitelistedTokenPair.ts @@ -0,0 +1,30 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import { Entity, PrimaryColumn, Column, Index } from 'typeorm'; + +@Entity() +@Index(['blockNumber']) +export class _WhitelistedTokenPair { + @PrimaryColumn('varchar') + id!: string; + + @PrimaryColumn('varchar', { length: 66 }) + blockHash!: string; + + @Column('integer') + blockNumber!: number; + + @Column('varchar') + pair!: string; + + @Column('varchar') + token!: string; + + @Column('boolean', { default: false }) + isPruned!: boolean; + + @Column('boolean', { default: false }) + isRemoved!: boolean; +} diff --git a/packages/sushiswap-watcher/src/fill.ts b/packages/sushiswap-watcher/src/fill.ts new file mode 100644 index 0000000..210341e --- /dev/null +++ b/packages/sushiswap-watcher/src/fill.ts @@ -0,0 +1,48 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import 'reflect-metadata'; +import debug from 'debug'; + +import { FillCmd } from '@cerc-io/cli'; +import { getContractEntitiesMap } from '@cerc-io/util'; +import { getGraphDbAndWatcher } from '@cerc-io/graph-node'; + +import { Database, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP } from './database'; +import { Indexer } from './indexer'; + +const log = debug('vulcanize:fill'); + +export const main = async (): Promise => { + const fillCmd = new FillCmd(); + await fillCmd.init(Database); + + const { graphWatcher } = await getGraphDbAndWatcher( + fillCmd.config.server, + fillCmd.clients.ethClient, + fillCmd.ethProvider, + fillCmd.database.baseDatabase, + ENTITY_QUERY_TYPE_MAP, + ENTITY_TO_LATEST_ENTITY_MAP + ); + + await fillCmd.initIndexer(Indexer, graphWatcher); + + // Get contractEntitiesMap required for fill-state + // NOTE: Assuming each entity type is only mapped to a single contract + const contractEntitiesMap = getContractEntitiesMap(graphWatcher.dataSources); + + await fillCmd.exec(contractEntitiesMap); +}; + +main().catch(err => { + log(err); +}).finally(() => { + process.exit(); +}); + +process.on('SIGINT', () => { + log(`Exiting process ${process.pid} with code 0`); + process.exit(0); +}); diff --git a/packages/sushiswap-watcher/src/gql/index.ts b/packages/sushiswap-watcher/src/gql/index.ts new file mode 100644 index 0000000..4732f68 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/index.ts @@ -0,0 +1,3 @@ +export * as mutations from './mutations'; +export * as queries from './queries'; +export * as subscriptions from './subscriptions'; diff --git a/packages/sushiswap-watcher/src/gql/mutations/index.ts b/packages/sushiswap-watcher/src/gql/mutations/index.ts new file mode 100644 index 0000000..0c3bd85 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/mutations/index.ts @@ -0,0 +1,4 @@ +import fs from 'fs'; +import path from 'path'; + +export const watchContract = fs.readFileSync(path.join(__dirname, 'watchContract.gql'), 'utf8'); diff --git a/packages/sushiswap-watcher/src/gql/mutations/watchContract.gql b/packages/sushiswap-watcher/src/gql/mutations/watchContract.gql new file mode 100644 index 0000000..2ecc74f --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/mutations/watchContract.gql @@ -0,0 +1,3 @@ +mutation watchContract($address: String!, $kind: String!, $checkpoint: Boolean!, $startingBlock: Int){ + watchContract(address: $address, kind: $kind, checkpoint: $checkpoint, startingBlock: $startingBlock) +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/_TokenPair.gql b/packages/sushiswap-watcher/src/gql/queries/_TokenPair.gql new file mode 100644 index 0000000..fda98bc --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/_TokenPair.gql @@ -0,0 +1,52 @@ +query _TokenPair($id: ID!, $block: Block_height){ + _TokenPair(id: $id, block: $block){ + id + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + token{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/_TokenPairs.gql b/packages/sushiswap-watcher/src/gql/queries/_TokenPairs.gql new file mode 100644 index 0000000..20992b4 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/_TokenPairs.gql @@ -0,0 +1,52 @@ +query _TokenPairs($block: Block_height, $where: _TokenPair_filter, $orderBy: _TokenPair_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + _TokenPairs(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + token{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/_WhitelistedTokenPair.gql b/packages/sushiswap-watcher/src/gql/queries/_WhitelistedTokenPair.gql new file mode 100644 index 0000000..0f78550 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/_WhitelistedTokenPair.gql @@ -0,0 +1,52 @@ +query _WhitelistedTokenPair($id: ID!, $block: Block_height){ + _WhitelistedTokenPair(id: $id, block: $block){ + id + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + token{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/_WhitelistedTokenPairs.gql b/packages/sushiswap-watcher/src/gql/queries/_WhitelistedTokenPairs.gql new file mode 100644 index 0000000..3be1662 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/_WhitelistedTokenPairs.gql @@ -0,0 +1,52 @@ +query _WhitelistedTokenPairs($block: Block_height, $where: _WhitelistedTokenPair_filter, $orderBy: _WhitelistedTokenPair_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + _WhitelistedTokenPairs(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + token{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/_meta.gql b/packages/sushiswap-watcher/src/gql/queries/_meta.gql new file mode 100644 index 0000000..d686e04 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/_meta.gql @@ -0,0 +1,11 @@ +query _meta($block: Block_height){ + _meta(block: $block){ + block{ + hash + number + timestamp + } + deployment + hasIndexingErrors + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/bundle.gql b/packages/sushiswap-watcher/src/gql/queries/bundle.gql new file mode 100644 index 0000000..fe156e7 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/bundle.gql @@ -0,0 +1,6 @@ +query bundle($id: ID!, $block: Block_height){ + bundle(id: $id, block: $block){ + id + nativePrice + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/bundles.gql b/packages/sushiswap-watcher/src/gql/queries/bundles.gql new file mode 100644 index 0000000..2d47827 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/bundles.gql @@ -0,0 +1,6 @@ +query bundles($block: Block_height, $where: Bundle_filter, $orderBy: Bundle_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + bundles(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + nativePrice + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/burn.gql b/packages/sushiswap-watcher/src/gql/queries/burn.gql new file mode 100644 index 0000000..64c8154 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/burn.gql @@ -0,0 +1,50 @@ +query burn($id: ID!, $block: Block_height){ + burn(id: $id, block: $block){ + id + transaction{ + id + gasLimit + gasPrice + createdAtBlock + createdAtTimestamp + } + timestamp + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + liquidity + sender + amount0 + amount1 + to + logIndex + amountUSD + complete + feeTo + feeLiquidity + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/burns.gql b/packages/sushiswap-watcher/src/gql/queries/burns.gql new file mode 100644 index 0000000..184995a --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/burns.gql @@ -0,0 +1,50 @@ +query burns($block: Block_height, $where: Burn_filter, $orderBy: Burn_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + burns(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + transaction{ + id + gasLimit + gasPrice + createdAtBlock + createdAtTimestamp + } + timestamp + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + liquidity + sender + amount0 + amount1 + to + logIndex + amountUSD + complete + feeTo + feeLiquidity + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/events.gql b/packages/sushiswap-watcher/src/gql/queries/events.gql new file mode 100644 index 0000000..03b8121 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/events.gql @@ -0,0 +1,63 @@ +query events($blockHash: String!, $contractAddress: String!, $name: String){ + events(blockHash: $blockHash, contractAddress: $contractAddress, name: $name){ + block{ + cid + hash + number + timestamp + parentHash + } + tx{ + hash + index + from + to + } + contract + eventIndex + event{ + ... on PairCreatedEvent { + token0 + token1 + pair + null + } + ... on ApprovalEvent { + owner + spender + value + } + ... on BurnEvent { + sender + amount0 + amount1 + to + } + ... on MintEvent { + sender + amount0 + amount1 + } + ... on SwapEvent { + sender + amount0In + amount1In + amount0Out + amount1Out + to + } + ... on SyncEvent { + reserve0 + reserve1 + } + ... on TransferEvent { + from + to + value + } + } + proof{ + data + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/eventsInRange.gql b/packages/sushiswap-watcher/src/gql/queries/eventsInRange.gql new file mode 100644 index 0000000..edf319a --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/eventsInRange.gql @@ -0,0 +1,63 @@ +query eventsInRange($fromBlockNumber: Int!, $toBlockNumber: Int!){ + eventsInRange(fromBlockNumber: $fromBlockNumber, toBlockNumber: $toBlockNumber){ + block{ + cid + hash + number + timestamp + parentHash + } + tx{ + hash + index + from + to + } + contract + eventIndex + event{ + ... on PairCreatedEvent { + token0 + token1 + pair + null + } + ... on ApprovalEvent { + owner + spender + value + } + ... on BurnEvent { + sender + amount0 + amount1 + to + } + ... on MintEvent { + sender + amount0 + amount1 + } + ... on SwapEvent { + sender + amount0In + amount1In + amount0Out + amount1Out + to + } + ... on SyncEvent { + reserve0 + reserve1 + } + ... on TransferEvent { + from + to + value + } + } + proof{ + data + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/factories.gql b/packages/sushiswap-watcher/src/gql/queries/factories.gql new file mode 100644 index 0000000..cadaba4 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/factories.gql @@ -0,0 +1,16 @@ +query factories($block: Block_height, $where: Factory_filter, $orderBy: Factory_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + factories(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + type + volumeUSD + volumeNative + liquidityUSD + liquidityNative + feesUSD + feesNative + pairCount + transactionCount + tokenCount + userCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/factory.gql b/packages/sushiswap-watcher/src/gql/queries/factory.gql new file mode 100644 index 0000000..36422d9 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/factory.gql @@ -0,0 +1,16 @@ +query factory($id: ID!, $block: Block_height){ + factory(id: $id, block: $block){ + id + type + volumeUSD + volumeNative + liquidityUSD + liquidityNative + feesUSD + feesNative + pairCount + transactionCount + tokenCount + userCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/factoryDaySnapshot.gql b/packages/sushiswap-watcher/src/gql/queries/factoryDaySnapshot.gql new file mode 100644 index 0000000..2410d9c --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/factoryDaySnapshot.gql @@ -0,0 +1,27 @@ +query factoryDaySnapshot($id: ID!, $block: Block_height){ + factoryDaySnapshot(id: $id, block: $block){ + id + factory{ + id + type + volumeUSD + volumeNative + liquidityUSD + liquidityNative + feesUSD + feesNative + pairCount + transactionCount + tokenCount + userCount + } + date + volumeUSD + volumeNative + liquidityNative + liquidityUSD + feesNative + feesUSD + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/factoryDaySnapshots.gql b/packages/sushiswap-watcher/src/gql/queries/factoryDaySnapshots.gql new file mode 100644 index 0000000..efa965e --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/factoryDaySnapshots.gql @@ -0,0 +1,27 @@ +query factoryDaySnapshots($block: Block_height, $where: FactoryDaySnapshot_filter, $orderBy: FactoryDaySnapshot_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + factoryDaySnapshots(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + factory{ + id + type + volumeUSD + volumeNative + liquidityUSD + liquidityNative + feesUSD + feesNative + pairCount + transactionCount + tokenCount + userCount + } + date + volumeUSD + volumeNative + liquidityNative + liquidityUSD + feesNative + feesUSD + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/factoryHourSnapshot.gql b/packages/sushiswap-watcher/src/gql/queries/factoryHourSnapshot.gql new file mode 100644 index 0000000..a854f7a --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/factoryHourSnapshot.gql @@ -0,0 +1,27 @@ +query factoryHourSnapshot($id: ID!, $block: Block_height){ + factoryHourSnapshot(id: $id, block: $block){ + id + factory{ + id + type + volumeUSD + volumeNative + liquidityUSD + liquidityNative + feesUSD + feesNative + pairCount + transactionCount + tokenCount + userCount + } + date + volumeUSD + volumeNative + liquidityNative + liquidityUSD + feesNative + feesUSD + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/factoryHourSnapshots.gql b/packages/sushiswap-watcher/src/gql/queries/factoryHourSnapshots.gql new file mode 100644 index 0000000..2015d21 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/factoryHourSnapshots.gql @@ -0,0 +1,27 @@ +query factoryHourSnapshots($block: Block_height, $where: FactoryHourSnapshot_filter, $orderBy: FactoryHourSnapshot_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + factoryHourSnapshots(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + factory{ + id + type + volumeUSD + volumeNative + liquidityUSD + liquidityNative + feesUSD + feesNative + pairCount + transactionCount + tokenCount + userCount + } + date + volumeUSD + volumeNative + liquidityNative + liquidityUSD + feesNative + feesUSD + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/getState.gql b/packages/sushiswap-watcher/src/gql/queries/getState.gql new file mode 100644 index 0000000..3b8f605 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/getState.gql @@ -0,0 +1,15 @@ +query getState($blockHash: String!, $contractAddress: String!, $kind: String){ + getState(blockHash: $blockHash, contractAddress: $contractAddress, kind: $kind){ + block{ + cid + hash + number + timestamp + parentHash + } + contractAddress + cid + kind + data + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/getStateByCID.gql b/packages/sushiswap-watcher/src/gql/queries/getStateByCID.gql new file mode 100644 index 0000000..6c3c4fd --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/getStateByCID.gql @@ -0,0 +1,15 @@ +query getStateByCID($cid: String!){ + getStateByCID(cid: $cid){ + block{ + cid + hash + number + timestamp + parentHash + } + contractAddress + cid + kind + data + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/getSyncStatus.gql b/packages/sushiswap-watcher/src/gql/queries/getSyncStatus.gql new file mode 100644 index 0000000..48175b4 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/getSyncStatus.gql @@ -0,0 +1,12 @@ +query getSyncStatus{ + getSyncStatus{ + latestIndexedBlockHash + latestIndexedBlockNumber + latestCanonicalBlockHash + latestCanonicalBlockNumber + initialIndexedBlockHash + initialIndexedBlockNumber + latestProcessedBlockHash + latestProcessedBlockNumber + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/index.ts b/packages/sushiswap-watcher/src/gql/queries/index.ts new file mode 100644 index 0000000..63a6a35 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/index.ts @@ -0,0 +1,49 @@ +import fs from 'fs'; +import path from 'path'; + +export const events = fs.readFileSync(path.join(__dirname, 'events.gql'), 'utf8'); +export const eventsInRange = fs.readFileSync(path.join(__dirname, 'eventsInRange.gql'), 'utf8'); +export const factory = fs.readFileSync(path.join(__dirname, 'factory.gql'), 'utf8'); +export const factories = fs.readFileSync(path.join(__dirname, 'factories.gql'), 'utf8'); +export const bundle = fs.readFileSync(path.join(__dirname, 'bundle.gql'), 'utf8'); +export const bundles = fs.readFileSync(path.join(__dirname, 'bundles.gql'), 'utf8'); +export const token = fs.readFileSync(path.join(__dirname, 'token.gql'), 'utf8'); +export const tokens = fs.readFileSync(path.join(__dirname, 'tokens.gql'), 'utf8'); +export const tokenPrice = fs.readFileSync(path.join(__dirname, 'tokenPrice.gql'), 'utf8'); +export const tokenPrices = fs.readFileSync(path.join(__dirname, 'tokenPrices.gql'), 'utf8'); +export const _TokenPair = fs.readFileSync(path.join(__dirname, '_TokenPair.gql'), 'utf8'); +export const _TokenPairs = fs.readFileSync(path.join(__dirname, '_TokenPairs.gql'), 'utf8'); +export const _WhitelistedTokenPair = fs.readFileSync(path.join(__dirname, '_WhitelistedTokenPair.gql'), 'utf8'); +export const _WhitelistedTokenPairs = fs.readFileSync(path.join(__dirname, '_WhitelistedTokenPairs.gql'), 'utf8'); +export const pair = fs.readFileSync(path.join(__dirname, 'pair.gql'), 'utf8'); +export const pairs = fs.readFileSync(path.join(__dirname, 'pairs.gql'), 'utf8'); +export const user = fs.readFileSync(path.join(__dirname, 'user.gql'), 'utf8'); +export const users = fs.readFileSync(path.join(__dirname, 'users.gql'), 'utf8'); +export const liquidityPosition = fs.readFileSync(path.join(__dirname, 'liquidityPosition.gql'), 'utf8'); +export const liquidityPositions = fs.readFileSync(path.join(__dirname, 'liquidityPositions.gql'), 'utf8'); +export const mint = fs.readFileSync(path.join(__dirname, 'mint.gql'), 'utf8'); +export const mints = fs.readFileSync(path.join(__dirname, 'mints.gql'), 'utf8'); +export const burn = fs.readFileSync(path.join(__dirname, 'burn.gql'), 'utf8'); +export const burns = fs.readFileSync(path.join(__dirname, 'burns.gql'), 'utf8'); +export const swap = fs.readFileSync(path.join(__dirname, 'swap.gql'), 'utf8'); +export const swaps = fs.readFileSync(path.join(__dirname, 'swaps.gql'), 'utf8'); +export const transaction = fs.readFileSync(path.join(__dirname, 'transaction.gql'), 'utf8'); +export const transactions = fs.readFileSync(path.join(__dirname, 'transactions.gql'), 'utf8'); +export const liquidityPositionSnapshot = fs.readFileSync(path.join(__dirname, 'liquidityPositionSnapshot.gql'), 'utf8'); +export const liquidityPositionSnapshots = fs.readFileSync(path.join(__dirname, 'liquidityPositionSnapshots.gql'), 'utf8'); +export const pairHourSnapshot = fs.readFileSync(path.join(__dirname, 'pairHourSnapshot.gql'), 'utf8'); +export const pairHourSnapshots = fs.readFileSync(path.join(__dirname, 'pairHourSnapshots.gql'), 'utf8'); +export const pairDaySnapshot = fs.readFileSync(path.join(__dirname, 'pairDaySnapshot.gql'), 'utf8'); +export const pairDaySnapshots = fs.readFileSync(path.join(__dirname, 'pairDaySnapshots.gql'), 'utf8'); +export const tokenHourSnapshot = fs.readFileSync(path.join(__dirname, 'tokenHourSnapshot.gql'), 'utf8'); +export const tokenHourSnapshots = fs.readFileSync(path.join(__dirname, 'tokenHourSnapshots.gql'), 'utf8'); +export const tokenDaySnapshot = fs.readFileSync(path.join(__dirname, 'tokenDaySnapshot.gql'), 'utf8'); +export const tokenDaySnapshots = fs.readFileSync(path.join(__dirname, 'tokenDaySnapshots.gql'), 'utf8'); +export const factoryHourSnapshot = fs.readFileSync(path.join(__dirname, 'factoryHourSnapshot.gql'), 'utf8'); +export const factoryHourSnapshots = fs.readFileSync(path.join(__dirname, 'factoryHourSnapshots.gql'), 'utf8'); +export const factoryDaySnapshot = fs.readFileSync(path.join(__dirname, 'factoryDaySnapshot.gql'), 'utf8'); +export const factoryDaySnapshots = fs.readFileSync(path.join(__dirname, 'factoryDaySnapshots.gql'), 'utf8'); +export const _meta = fs.readFileSync(path.join(__dirname, '_meta.gql'), 'utf8'); +export const getStateByCID = fs.readFileSync(path.join(__dirname, 'getStateByCID.gql'), 'utf8'); +export const getState = fs.readFileSync(path.join(__dirname, 'getState.gql'), 'utf8'); +export const getSyncStatus = fs.readFileSync(path.join(__dirname, 'getSyncStatus.gql'), 'utf8'); diff --git a/packages/sushiswap-watcher/src/gql/queries/liquidityPosition.gql b/packages/sushiswap-watcher/src/gql/queries/liquidityPosition.gql new file mode 100644 index 0000000..fa948c2 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/liquidityPosition.gql @@ -0,0 +1,39 @@ +query liquidityPosition($id: ID!, $block: Block_height){ + liquidityPosition(id: $id, block: $block){ + id + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + user{ + id + lpSnapshotsCount + } + balance + createdAtBlock + createdAtTimestamp + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/liquidityPositionSnapshot.gql b/packages/sushiswap-watcher/src/gql/queries/liquidityPositionSnapshot.gql new file mode 100644 index 0000000..41ebce6 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/liquidityPositionSnapshot.gql @@ -0,0 +1,51 @@ +query liquidityPositionSnapshot($id: ID!, $block: Block_height){ + liquidityPositionSnapshot(id: $id, block: $block){ + id + liquidityPosition{ + id + balance + createdAtBlock + createdAtTimestamp + } + timestamp + block + user{ + id + lpSnapshotsCount + } + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + token0PriceUSD + token1PriceUSD + reserve0 + reserve1 + reserveUSD + liquidityTokenTotalSupply + liquidityTokenBalance + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/liquidityPositionSnapshots.gql b/packages/sushiswap-watcher/src/gql/queries/liquidityPositionSnapshots.gql new file mode 100644 index 0000000..f56f868 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/liquidityPositionSnapshots.gql @@ -0,0 +1,51 @@ +query liquidityPositionSnapshots($block: Block_height, $where: LiquidityPositionSnapshot_filter, $orderBy: LiquidityPositionSnapshot_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + liquidityPositionSnapshots(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + liquidityPosition{ + id + balance + createdAtBlock + createdAtTimestamp + } + timestamp + block + user{ + id + lpSnapshotsCount + } + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + token0PriceUSD + token1PriceUSD + reserve0 + reserve1 + reserveUSD + liquidityTokenTotalSupply + liquidityTokenBalance + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/liquidityPositions.gql b/packages/sushiswap-watcher/src/gql/queries/liquidityPositions.gql new file mode 100644 index 0000000..f9f7d2f --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/liquidityPositions.gql @@ -0,0 +1,39 @@ +query liquidityPositions($block: Block_height, $where: LiquidityPosition_filter, $orderBy: LiquidityPosition_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + liquidityPositions(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + user{ + id + lpSnapshotsCount + } + balance + createdAtBlock + createdAtTimestamp + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/mint.gql b/packages/sushiswap-watcher/src/gql/queries/mint.gql new file mode 100644 index 0000000..5a0115f --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/mint.gql @@ -0,0 +1,49 @@ +query mint($id: ID!, $block: Block_height){ + mint(id: $id, block: $block){ + id + transaction{ + id + gasLimit + gasPrice + createdAtBlock + createdAtTimestamp + } + timestamp + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + to + liquidity + sender + amount0 + amount1 + logIndex + amountUSD + feeTo + feeLiquidity + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/mints.gql b/packages/sushiswap-watcher/src/gql/queries/mints.gql new file mode 100644 index 0000000..dece868 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/mints.gql @@ -0,0 +1,49 @@ +query mints($block: Block_height, $where: Mint_filter, $orderBy: Mint_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + mints(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + transaction{ + id + gasLimit + gasPrice + createdAtBlock + createdAtTimestamp + } + timestamp + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + to + liquidity + sender + amount0 + amount1 + logIndex + amountUSD + feeTo + feeLiquidity + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/pair.gql b/packages/sushiswap-watcher/src/gql/queries/pair.gql new file mode 100644 index 0000000..1cad206 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/pair.gql @@ -0,0 +1,119 @@ +query pair($id: ID!, $block: Block_height){ + pair(id: $id, block: $block){ + id + type + swapFee + twapEnabled + name + token0{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + token1{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + liquidityPositions{ + id + balance + createdAtBlock + createdAtTimestamp + } + liquidityPositionSnapshots{ + id + timestamp + block + token0PriceUSD + token1PriceUSD + reserve0 + reserve1 + reserveUSD + liquidityTokenTotalSupply + liquidityTokenBalance + } + hourSnapshots{ + id + date + cumulativeVolumeUSD + volumeUSD + volumeNative + volumeToken0 + volumeToken1 + liquidity + liquidityNative + liquidityUSD + feesNative + feesUSD + apr + transactionCount + } + daySnapshots{ + id + date + cumulativeVolumeUSD + volumeUSD + volumeNative + volumeToken0 + volumeToken1 + liquidity + liquidityNative + liquidityUSD + feesNative + feesUSD + apr + transactionCount + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/pairDaySnapshot.gql b/packages/sushiswap-watcher/src/gql/queries/pairDaySnapshot.gql new file mode 100644 index 0000000..24ed11e --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/pairDaySnapshot.gql @@ -0,0 +1,45 @@ +query pairDaySnapshot($id: ID!, $block: Block_height){ + pairDaySnapshot(id: $id, block: $block){ + id + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + date + cumulativeVolumeUSD + volumeUSD + volumeNative + volumeToken0 + volumeToken1 + liquidity + liquidityNative + liquidityUSD + feesNative + feesUSD + apr + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/pairDaySnapshots.gql b/packages/sushiswap-watcher/src/gql/queries/pairDaySnapshots.gql new file mode 100644 index 0000000..b8fa060 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/pairDaySnapshots.gql @@ -0,0 +1,45 @@ +query pairDaySnapshots($block: Block_height, $where: PairDaySnapshot_filter, $orderBy: PairDaySnapshot_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + pairDaySnapshots(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + date + cumulativeVolumeUSD + volumeUSD + volumeNative + volumeToken0 + volumeToken1 + liquidity + liquidityNative + liquidityUSD + feesNative + feesUSD + apr + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/pairHourSnapshot.gql b/packages/sushiswap-watcher/src/gql/queries/pairHourSnapshot.gql new file mode 100644 index 0000000..7392c93 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/pairHourSnapshot.gql @@ -0,0 +1,45 @@ +query pairHourSnapshot($id: ID!, $block: Block_height){ + pairHourSnapshot(id: $id, block: $block){ + id + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + date + cumulativeVolumeUSD + volumeUSD + volumeNative + volumeToken0 + volumeToken1 + liquidity + liquidityNative + liquidityUSD + feesNative + feesUSD + apr + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/pairHourSnapshots.gql b/packages/sushiswap-watcher/src/gql/queries/pairHourSnapshots.gql new file mode 100644 index 0000000..f144927 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/pairHourSnapshots.gql @@ -0,0 +1,45 @@ +query pairHourSnapshots($block: Block_height, $where: PairHourSnapshot_filter, $orderBy: PairHourSnapshot_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + pairHourSnapshots(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + date + cumulativeVolumeUSD + volumeUSD + volumeNative + volumeToken0 + volumeToken1 + liquidity + liquidityNative + liquidityUSD + feesNative + feesUSD + apr + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/pairs.gql b/packages/sushiswap-watcher/src/gql/queries/pairs.gql new file mode 100644 index 0000000..0331a34 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/pairs.gql @@ -0,0 +1,119 @@ +query pairs($block: Block_height, $where: Pair_filter, $orderBy: Pair_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + pairs(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + type + swapFee + twapEnabled + name + token0{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + token1{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + liquidityPositions{ + id + balance + createdAtBlock + createdAtTimestamp + } + liquidityPositionSnapshots{ + id + timestamp + block + token0PriceUSD + token1PriceUSD + reserve0 + reserve1 + reserveUSD + liquidityTokenTotalSupply + liquidityTokenBalance + } + hourSnapshots{ + id + date + cumulativeVolumeUSD + volumeUSD + volumeNative + volumeToken0 + volumeToken1 + liquidity + liquidityNative + liquidityUSD + feesNative + feesUSD + apr + transactionCount + } + daySnapshots{ + id + date + cumulativeVolumeUSD + volumeUSD + volumeNative + volumeToken0 + volumeToken1 + liquidity + liquidityNative + liquidityUSD + feesNative + feesUSD + apr + transactionCount + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/swap.gql b/packages/sushiswap-watcher/src/gql/queries/swap.gql new file mode 100644 index 0000000..a7d1872 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/swap.gql @@ -0,0 +1,86 @@ +query swap($id: ID!, $block: Block_height){ + swap(id: $id, block: $block){ + id + transaction{ + id + gasLimit + gasPrice + createdAtBlock + createdAtTimestamp + } + timestamp + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + sender + tokenIn{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + tokenOut{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + amountIn + amountOut + to + logIndex + amountUSD + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/swaps.gql b/packages/sushiswap-watcher/src/gql/queries/swaps.gql new file mode 100644 index 0000000..dded99c --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/swaps.gql @@ -0,0 +1,86 @@ +query swaps($block: Block_height, $where: Swap_filter, $orderBy: Swap_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + swaps(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + transaction{ + id + gasLimit + gasPrice + createdAtBlock + createdAtTimestamp + } + timestamp + pair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + sender + tokenIn{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + tokenOut{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + amountIn + amountOut + to + logIndex + amountUSD + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/token.gql b/packages/sushiswap-watcher/src/gql/queries/token.gql new file mode 100644 index 0000000..8bd95fc --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/token.gql @@ -0,0 +1,33 @@ +query token($id: ID!, $block: Block_height){ + token(id: $id, block: $block){ + id + price{ + id + derivedNative + lastUsdPrice + } + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + pairs{ + id + } + whitelistedPairs{ + id + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/tokenDaySnapshot.gql b/packages/sushiswap-watcher/src/gql/queries/tokenDaySnapshot.gql new file mode 100644 index 0000000..bfa3dc6 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/tokenDaySnapshot.gql @@ -0,0 +1,37 @@ +query tokenDaySnapshot($id: ID!, $block: Block_height){ + tokenDaySnapshot(id: $id, block: $block){ + id + date + token{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + priceNative + priceUSD + feesNative + feesUSD + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/tokenDaySnapshots.gql b/packages/sushiswap-watcher/src/gql/queries/tokenDaySnapshots.gql new file mode 100644 index 0000000..1233ec0 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/tokenDaySnapshots.gql @@ -0,0 +1,37 @@ +query tokenDaySnapshots($block: Block_height, $where: TokenDaySnapshot_filter, $orderBy: TokenDaySnapshot_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + tokenDaySnapshots(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + date + token{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + priceNative + priceUSD + feesNative + feesUSD + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/tokenHourSnapshot.gql b/packages/sushiswap-watcher/src/gql/queries/tokenHourSnapshot.gql new file mode 100644 index 0000000..3ce1de4 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/tokenHourSnapshot.gql @@ -0,0 +1,37 @@ +query tokenHourSnapshot($id: ID!, $block: Block_height){ + tokenHourSnapshot(id: $id, block: $block){ + id + date + token{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + priceNative + priceUSD + feesNative + feesUSD + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/tokenHourSnapshots.gql b/packages/sushiswap-watcher/src/gql/queries/tokenHourSnapshots.gql new file mode 100644 index 0000000..90750a7 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/tokenHourSnapshots.gql @@ -0,0 +1,37 @@ +query tokenHourSnapshots($block: Block_height, $where: TokenHourSnapshot_filter, $orderBy: TokenHourSnapshot_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + tokenHourSnapshots(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + date + token{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + priceNative + priceUSD + feesNative + feesUSD + transactionCount + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/tokenPrice.gql b/packages/sushiswap-watcher/src/gql/queries/tokenPrice.gql new file mode 100644 index 0000000..c2149c8 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/tokenPrice.gql @@ -0,0 +1,74 @@ +query tokenPrice($id: ID!, $block: Block_height){ + tokenPrice(id: $id, block: $block){ + id + token{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + derivedNative + lastUsdPrice + pricedOffToken{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + pricedOffPair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/tokenPrices.gql b/packages/sushiswap-watcher/src/gql/queries/tokenPrices.gql new file mode 100644 index 0000000..3069e3e --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/tokenPrices.gql @@ -0,0 +1,74 @@ +query tokenPrices($block: Block_height, $where: TokenPrice_filter, $orderBy: TokenPrice_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + tokenPrices(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + token{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + derivedNative + lastUsdPrice + pricedOffToken{ + id + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + } + pricedOffPair{ + id + type + swapFee + twapEnabled + name + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/tokens.gql b/packages/sushiswap-watcher/src/gql/queries/tokens.gql new file mode 100644 index 0000000..47b128b --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/tokens.gql @@ -0,0 +1,33 @@ +query tokens($block: Block_height, $where: Token_filter, $orderBy: Token_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + tokens(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + price{ + id + derivedNative + lastUsdPrice + } + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + pairs{ + id + } + whitelistedPairs{ + id + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/transaction.gql b/packages/sushiswap-watcher/src/gql/queries/transaction.gql new file mode 100644 index 0000000..2239f8f --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/transaction.gql @@ -0,0 +1,46 @@ +query transaction($id: ID!, $block: Block_height){ + transaction(id: $id, block: $block){ + id + gasLimit + gasPrice + mints{ + id + timestamp + to + liquidity + sender + amount0 + amount1 + logIndex + amountUSD + feeTo + feeLiquidity + } + burns{ + id + timestamp + liquidity + sender + amount0 + amount1 + to + logIndex + amountUSD + complete + feeTo + feeLiquidity + } + swaps{ + id + timestamp + sender + amountIn + amountOut + to + logIndex + amountUSD + } + createdAtBlock + createdAtTimestamp + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/transactions.gql b/packages/sushiswap-watcher/src/gql/queries/transactions.gql new file mode 100644 index 0000000..a1b4e12 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/transactions.gql @@ -0,0 +1,46 @@ +query transactions($block: Block_height, $where: Transaction_filter, $orderBy: Transaction_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + transactions(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + gasLimit + gasPrice + mints{ + id + timestamp + to + liquidity + sender + amount0 + amount1 + logIndex + amountUSD + feeTo + feeLiquidity + } + burns{ + id + timestamp + liquidity + sender + amount0 + amount1 + to + logIndex + amountUSD + complete + feeTo + feeLiquidity + } + swaps{ + id + timestamp + sender + amountIn + amountOut + to + logIndex + amountUSD + } + createdAtBlock + createdAtTimestamp + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/user.gql b/packages/sushiswap-watcher/src/gql/queries/user.gql new file mode 100644 index 0000000..d7183af --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/user.gql @@ -0,0 +1,12 @@ +query user($id: ID!, $block: Block_height){ + user(id: $id, block: $block){ + id + lpSnapshotsCount + liquidityPositions{ + id + balance + createdAtBlock + createdAtTimestamp + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/queries/users.gql b/packages/sushiswap-watcher/src/gql/queries/users.gql new file mode 100644 index 0000000..8344916 --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/queries/users.gql @@ -0,0 +1,12 @@ +query users($block: Block_height, $where: User_filter, $orderBy: User_orderBy, $orderDirection: OrderDirection, $first: Int, $skip: Int){ + users(block: $block, where: $where, orderBy: $orderBy, orderDirection: $orderDirection, first: $first, skip: $skip){ + id + lpSnapshotsCount + liquidityPositions{ + id + balance + createdAtBlock + createdAtTimestamp + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/gql/subscriptions/index.ts b/packages/sushiswap-watcher/src/gql/subscriptions/index.ts new file mode 100644 index 0000000..f12910c --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/subscriptions/index.ts @@ -0,0 +1,4 @@ +import fs from 'fs'; +import path from 'path'; + +export const onEvent = fs.readFileSync(path.join(__dirname, 'onEvent.gql'), 'utf8'); diff --git a/packages/sushiswap-watcher/src/gql/subscriptions/onEvent.gql b/packages/sushiswap-watcher/src/gql/subscriptions/onEvent.gql new file mode 100644 index 0000000..305777a --- /dev/null +++ b/packages/sushiswap-watcher/src/gql/subscriptions/onEvent.gql @@ -0,0 +1,63 @@ +subscription onEvent{ + onEvent{ + block{ + cid + hash + number + timestamp + parentHash + } + tx{ + hash + index + from + to + } + contract + eventIndex + event{ + ... on PairCreatedEvent { + token0 + token1 + pair + null + } + ... on ApprovalEvent { + owner + spender + value + } + ... on BurnEvent { + sender + amount0 + amount1 + to + } + ... on MintEvent { + sender + amount0 + amount1 + } + ... on SwapEvent { + sender + amount0In + amount1In + amount0Out + amount1Out + to + } + ... on SyncEvent { + reserve0 + reserve1 + } + ... on TransferEvent { + from + to + value + } + } + proof{ + data + } + } +} \ No newline at end of file diff --git a/packages/sushiswap-watcher/src/hooks.ts b/packages/sushiswap-watcher/src/hooks.ts new file mode 100644 index 0000000..d45498b --- /dev/null +++ b/packages/sushiswap-watcher/src/hooks.ts @@ -0,0 +1,86 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import assert from 'assert'; + +import { + ResultEvent, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + updateStateForMappingType, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + updateStateForElementaryType +} from '@cerc-io/util'; + +import { Indexer } from './indexer'; + +/** + * Hook function to store an initial state. + * @param indexer Indexer instance. + * @param blockHash Hash of the concerned block. + * @param contractAddress Address of the concerned contract. + * @returns Data block to be stored. + */ +export async function createInitialState (indexer: Indexer, contractAddress: string, blockHash: string): Promise { + assert(indexer); + assert(blockHash); + assert(contractAddress); + + // Store an empty State. + const stateData: any = { + state: {} + }; + + // Use updateStateForElementaryType to update initial state with an elementary property. + // Eg. const stateData = updateStateForElementaryType(stateData, '_totalBalance', result.value.toString()); + + // Use updateStateForMappingType to update initial state with a nested property. + // Eg. const stateData = updateStateForMappingType(stateData, '_allowances', [owner, spender], allowance.value.toString()); + + // Return initial state data to be saved. + return stateData; +} + +/** + * Hook function to create state diff. + * @param indexer Indexer instance that contains methods to fetch the contract variable values. + * @param blockHash Block hash of the concerned block. + */ +export async function createStateDiff (indexer: Indexer, blockHash: string): Promise { + assert(indexer); + assert(blockHash); + + // Use indexer.createDiff() method to save custom state diff(s). +} + +/** + * Hook function to create state checkpoint + * @param indexer Indexer instance. + * @param contractAddress Address of the concerned contract. + * @param blockHash Block hash of the concerned block. + * @returns Whether to disable default checkpoint. If false, the state from this hook is updated with that from default checkpoint. + */ +export async function createStateCheckpoint (indexer: Indexer, contractAddress: string, blockHash: string): Promise { + assert(indexer); + assert(blockHash); + assert(contractAddress); + + // Use indexer.createStateCheckpoint() method to create a custom checkpoint. + + // Return false to update the state created by this hook by auto-generated checkpoint state. + // Return true to disable update of the state created by this hook by auto-generated checkpoint state. + return false; +} + +/** + * Event hook function. + * @param indexer Indexer instance that contains methods to fetch and update the contract values in the database. + * @param eventData ResultEvent object containing event information. + */ +export async function handleEvent (indexer: Indexer, eventData: ResultEvent): Promise { + assert(indexer); + assert(eventData); + + // Use indexer methods to index data. + // Pass `diff` parameter to indexer methods as true to save an auto-generated state from the indexed data. +} diff --git a/packages/sushiswap-watcher/src/indexer.ts b/packages/sushiswap-watcher/src/indexer.ts new file mode 100644 index 0000000..b7174d1 --- /dev/null +++ b/packages/sushiswap-watcher/src/indexer.ts @@ -0,0 +1,1197 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import assert from 'assert'; +import { DeepPartial, FindConditions, FindManyOptions, ObjectLiteral } from 'typeorm'; +import debug from 'debug'; +import { ethers, constants } from 'ethers'; +import { GraphQLResolveInfo } from 'graphql'; + +import { JsonFragment } from '@ethersproject/abi'; +import { BaseProvider } from '@ethersproject/providers'; +import { MappingKey, StorageLayout } from '@cerc-io/solidity-mapper'; +import { + Indexer as BaseIndexer, + IndexerInterface, + ValueResult, + ServerConfig, + JobQueue, + Where, + QueryOptions, + BlockHeight, + ResultMeta, + updateSubgraphState, + dumpSubgraphState, + GraphWatcherInterface, + StateKind, + StateStatus, + ResultEvent, + getResultEvent, + DatabaseInterface, + Clients, + EthClient, + UpstreamConfig, + EthFullBlock, + EthFullTransaction, + ExtraEventData +} from '@cerc-io/util'; +import { GraphWatcher } from '@cerc-io/graph-node'; + +import FactoryArtifacts from './artifacts/Factory.json'; +import PairArtifacts from './artifacts/Pair.json'; +import { Database, ENTITIES, SUBGRAPH_ENTITIES } from './database'; +import { createInitialState, handleEvent, createStateDiff, createStateCheckpoint } from './hooks'; +import { Contract } from './entity/Contract'; +import { Event } from './entity/Event'; +import { SyncStatus } from './entity/SyncStatus'; +import { StateSyncStatus } from './entity/StateSyncStatus'; +import { BlockProgress } from './entity/BlockProgress'; +import { State } from './entity/State'; +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { Factory } from './entity/Factory'; +import { Bundle } from './entity/Bundle'; +import { Token } from './entity/Token'; +import { TokenPrice } from './entity/TokenPrice'; +import { _TokenPair } from './entity/_TokenPair'; +import { _WhitelistedTokenPair } from './entity/_WhitelistedTokenPair'; +import { Pair } from './entity/Pair'; +import { User } from './entity/User'; +import { LiquidityPosition } from './entity/LiquidityPosition'; +import { Mint } from './entity/Mint'; +import { Burn } from './entity/Burn'; +import { Swap } from './entity/Swap'; +import { Transaction } from './entity/Transaction'; +import { LiquidityPositionSnapshot } from './entity/LiquidityPositionSnapshot'; +import { PairHourSnapshot } from './entity/PairHourSnapshot'; +import { PairDaySnapshot } from './entity/PairDaySnapshot'; +import { TokenHourSnapshot } from './entity/TokenHourSnapshot'; +import { TokenDaySnapshot } from './entity/TokenDaySnapshot'; +import { FactoryHourSnapshot } from './entity/FactoryHourSnapshot'; +import { FactoryDaySnapshot } from './entity/FactoryDaySnapshot'; +/* eslint-enable @typescript-eslint/no-unused-vars */ + +import { FrothyEntity } from './entity/FrothyEntity'; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const log = debug('vulcanize:indexer'); + +const KIND_FACTORY = 'Factory'; + +const KIND_PAIR = 'Pair'; + +export class Indexer implements IndexerInterface { + _db: Database; + _ethClient: EthClient; + _ethProvider: BaseProvider; + _baseIndexer: BaseIndexer; + _serverConfig: ServerConfig; + _upstreamConfig: UpstreamConfig; + _graphWatcher: GraphWatcher; + + _abiMap: Map; + _storageLayoutMap: Map; + _contractMap: Map; + eventSignaturesMap: Map; + + _entityTypesMap: Map; + _relationsMap: Map; + + _subgraphStateMap: Map; + + constructor ( + config: { + server: ServerConfig; + upstream: UpstreamConfig; + }, + db: DatabaseInterface, + clients: Clients, + ethProvider: BaseProvider, + jobQueue: JobQueue, + graphWatcher?: GraphWatcherInterface + ) { + assert(db); + assert(clients.ethClient); + + this._db = db as Database; + this._ethClient = clients.ethClient; + this._ethProvider = ethProvider; + this._serverConfig = config.server; + this._upstreamConfig = config.upstream; + this._baseIndexer = new BaseIndexer(config, this._db, this._ethClient, this._ethProvider, jobQueue); + assert(graphWatcher); + this._graphWatcher = graphWatcher as GraphWatcher; + + this._abiMap = new Map(); + this._storageLayoutMap = new Map(); + this._contractMap = new Map(); + this.eventSignaturesMap = new Map(); + + const { abi: FactoryABI } = FactoryArtifacts; + + const { abi: PairABI } = PairArtifacts; + + assert(FactoryABI); + this._abiMap.set(KIND_FACTORY, FactoryABI); + + const FactoryContractInterface = new ethers.utils.Interface(FactoryABI); + this._contractMap.set(KIND_FACTORY, FactoryContractInterface); + + const FactoryEventSignatures = Object.values(FactoryContractInterface.events).map(value => { + return FactoryContractInterface.getEventTopic(value); + }); + this.eventSignaturesMap.set(KIND_FACTORY, FactoryEventSignatures); + + assert(PairABI); + this._abiMap.set(KIND_PAIR, PairABI); + + const PairContractInterface = new ethers.utils.Interface(PairABI); + this._contractMap.set(KIND_PAIR, PairContractInterface); + + const PairEventSignatures = Object.values(PairContractInterface.events).map(value => { + return PairContractInterface.getEventTopic(value); + }); + this.eventSignaturesMap.set(KIND_PAIR, PairEventSignatures); + + this._entityTypesMap = new Map(); + this._populateEntityTypesMap(); + + this._relationsMap = new Map(); + this._populateRelationsMap(); + + this._subgraphStateMap = new Map(); + } + + get serverConfig (): ServerConfig { + return this._serverConfig; + } + + get upstreamConfig (): UpstreamConfig { + return this._upstreamConfig; + } + + get storageLayoutMap (): Map { + return this._storageLayoutMap; + } + + get graphWatcher (): GraphWatcher { + return this._graphWatcher; + } + + async init (): Promise { + await this._baseIndexer.fetchContracts(); + await this._baseIndexer.fetchStateStatus(); + } + + switchClients ({ ethClient, ethProvider }: { ethClient: EthClient, ethProvider: BaseProvider }): void { + this._ethClient = ethClient; + this._ethProvider = ethProvider; + this._baseIndexer.switchClients({ ethClient, ethProvider }); + this._graphWatcher.switchClients({ ethClient, ethProvider }); + } + + async getMetaData (block: BlockHeight): Promise { + return this._baseIndexer.getMetaData(block); + } + + getResultEvent (event: Event): ResultEvent { + return getResultEvent(event); + } + + async getStorageValue (storageLayout: StorageLayout, blockHash: string, contractAddress: string, variable: string, ...mappingKeys: MappingKey[]): Promise { + return this._baseIndexer.getStorageValue( + storageLayout, + blockHash, + contractAddress, + variable, + ...mappingKeys + ); + } + + async getEntitiesForBlock (blockHash: string, tableName: string): Promise { + return this._db.getEntitiesForBlock(blockHash, tableName); + } + + async processInitialState (contractAddress: string, blockHash: string): Promise { + // Call initial state hook. + return createInitialState(this, contractAddress, blockHash); + } + + async processStateCheckpoint (contractAddress: string, blockHash: string): Promise { + // Call checkpoint hook. + return createStateCheckpoint(this, contractAddress, blockHash); + } + + async processCanonicalBlock (blockHash: string, blockNumber: number): Promise { + console.time('time:indexer#processCanonicalBlock-finalize_auto_diffs'); + // Finalize staged diff blocks if any. + await this._baseIndexer.finalizeDiffStaged(blockHash); + console.timeEnd('time:indexer#processCanonicalBlock-finalize_auto_diffs'); + + // Call custom stateDiff hook. + await createStateDiff(this, blockHash); + + this._graphWatcher.pruneEntityCacheFrothyBlocks(blockHash, blockNumber); + } + + async processCheckpoint (blockHash: string): Promise { + // Return if checkpointInterval is <= 0. + const checkpointInterval = this._serverConfig.checkpointInterval; + if (checkpointInterval <= 0) return; + + console.time('time:indexer#processCheckpoint-checkpoint'); + await this._baseIndexer.processCheckpoint(this, blockHash, checkpointInterval); + console.timeEnd('time:indexer#processCheckpoint-checkpoint'); + } + + async processCLICheckpoint (contractAddress: string, blockHash?: string): Promise { + return this._baseIndexer.processCLICheckpoint(this, contractAddress, blockHash); + } + + async getPrevState (blockHash: string, contractAddress: string, kind?: string): Promise { + return this._db.getPrevState(blockHash, contractAddress, kind); + } + + async getLatestState (contractAddress: string, kind: StateKind | null, blockNumber?: number): Promise { + return this._db.getLatestState(contractAddress, kind, blockNumber); + } + + async getStatesByHash (blockHash: string): Promise { + return this._baseIndexer.getStatesByHash(blockHash); + } + + async getStateByCID (cid: string): Promise { + return this._baseIndexer.getStateByCID(cid); + } + + async getStates (where: FindConditions): Promise { + return this._db.getStates(where); + } + + getStateData (state: State): any { + return this._baseIndexer.getStateData(state); + } + + // Method used to create auto diffs (diff_staged). + async createDiffStaged (contractAddress: string, blockHash: string, data: any): Promise { + console.time('time:indexer#createDiffStaged-auto_diff'); + await this._baseIndexer.createDiffStaged(contractAddress, blockHash, data); + console.timeEnd('time:indexer#createDiffStaged-auto_diff'); + } + + // Method to be used by createStateDiff hook. + async createDiff (contractAddress: string, blockHash: string, data: any): Promise { + const block = await this.getBlockProgress(blockHash); + assert(block); + + await this._baseIndexer.createDiff(contractAddress, block, data); + } + + // Method to be used by createStateCheckpoint hook. + async createStateCheckpoint (contractAddress: string, blockHash: string, data: any): Promise { + const block = await this.getBlockProgress(blockHash); + assert(block); + + return this._baseIndexer.createStateCheckpoint(contractAddress, block, data); + } + + // Method to be used by export-state CLI. + async createCheckpoint (contractAddress: string, blockHash: string): Promise { + const block = await this.getBlockProgress(blockHash); + assert(block); + + return this._baseIndexer.createCheckpoint(this, contractAddress, block); + } + + // Method to be used by fill-state CLI. + async createInit (blockHash: string, blockNumber: number): Promise { + // Create initial state for contracts. + await this._baseIndexer.createInit(this, blockHash, blockNumber); + } + + async saveOrUpdateState (state: State): Promise { + return this._baseIndexer.saveOrUpdateState(state); + } + + async removeStates (blockNumber: number, kind: StateKind): Promise { + await this._baseIndexer.removeStates(blockNumber, kind); + } + + async getSubgraphEntity ( + entity: new () => Entity, + id: string, + block: BlockHeight, + queryInfo: GraphQLResolveInfo + ): Promise { + const data = await this._graphWatcher.getEntity(entity, id, this._relationsMap, block, queryInfo); + + return data; + } + + async getSubgraphEntities ( + entity: new () => Entity, + block: BlockHeight, + where: { [key: string]: any } = {}, + queryOptions: QueryOptions = {}, + queryInfo: GraphQLResolveInfo + ): Promise { + return this._graphWatcher.getEntities(entity, this._relationsMap, block, where, queryOptions, queryInfo); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async triggerIndexingOnEvent (event: Event, extraData: ExtraEventData): Promise { + const resultEvent = this.getResultEvent(event); + + console.time('time:indexer#processEvent-mapping_code'); + // Call subgraph handler for event. + await this._graphWatcher.handleEvent(resultEvent, extraData); + console.timeEnd('time:indexer#processEvent-mapping_code'); + + // Call custom hook function for indexing on event. + await handleEvent(this, resultEvent); + } + + async processEvent (event: Event, extraData: ExtraEventData): Promise { + // Trigger indexing of data based on the event. + await this.triggerIndexingOnEvent(event, extraData); + } + + async processBlock (blockProgress: BlockProgress): Promise { + console.time('time:indexer#processBlock-init_state'); + // Call a function to create initial state for contracts. + await this._baseIndexer.createInit(this, blockProgress.blockHash, blockProgress.blockNumber); + console.timeEnd('time:indexer#processBlock-init_state'); + + this._graphWatcher.updateEntityCacheFrothyBlocks(blockProgress); + } + + async processBlockAfterEvents (blockHash: string, blockNumber: number, extraData: ExtraEventData): Promise { + console.time('time:indexer#processBlockAfterEvents-mapping_code'); + // Call subgraph handler for block. + await this._graphWatcher.handleBlock(blockHash, blockNumber, extraData); + console.timeEnd('time:indexer#processBlockAfterEvents-mapping_code'); + + console.time('time:indexer#processBlockAfterEvents-dump_subgraph_state'); + // Persist subgraph state to the DB. + await this.dumpSubgraphState(blockHash); + console.timeEnd('time:indexer#processBlockAfterEvents-dump_subgraph_state'); + } + + parseEventNameAndArgs (kind: string, logObj: any): { eventParsed: boolean, eventDetails: any } { + const { topics, data } = logObj; + + const contract = this._contractMap.get(kind); + assert(contract); + + let logDescription: ethers.utils.LogDescription; + try { + logDescription = contract.parseLog({ data, topics }); + } catch (err) { + // Return if no matching event found + if ((err as Error).message.includes('no matching event')) { + log(`WARNING: Skipping event for contract ${kind} as no matching event found in the ABI`); + return { eventParsed: false, eventDetails: {} }; + } + + throw err; + } + + const { eventName, eventInfo, eventSignature } = this._baseIndexer.parseEvent(logDescription); + + return { + eventParsed: true, + eventDetails: { + eventName, + eventInfo, + eventSignature + } + }; + } + + async getStateSyncStatus (): Promise { + return this._db.getStateSyncStatus(); + } + + async updateStateSyncStatusIndexedBlock (blockNumber: number, force?: boolean): Promise { + if (!this._serverConfig.enableState) { + return; + } + + const dbTx = await this._db.createTransactionRunner(); + let res; + + try { + res = await this._db.updateStateSyncStatusIndexedBlock(dbTx, blockNumber, force); + await dbTx.commitTransaction(); + } catch (error) { + await dbTx.rollbackTransaction(); + throw error; + } finally { + await dbTx.release(); + } + + return res; + } + + async updateStateSyncStatusCheckpointBlock (blockNumber: number, force?: boolean): Promise { + const dbTx = await this._db.createTransactionRunner(); + let res; + + try { + res = await this._db.updateStateSyncStatusCheckpointBlock(dbTx, blockNumber, force); + await dbTx.commitTransaction(); + } catch (error) { + await dbTx.rollbackTransaction(); + throw error; + } finally { + await dbTx.release(); + } + + return res; + } + + async getLatestCanonicalBlock (): Promise { + const syncStatus = await this.getSyncStatus(); + assert(syncStatus); + + if (syncStatus.latestCanonicalBlockHash === constants.HashZero) { + return; + } + + const latestCanonicalBlock = await this.getBlockProgress(syncStatus.latestCanonicalBlockHash); + assert(latestCanonicalBlock); + + return latestCanonicalBlock; + } + + async getLatestStateIndexedBlock (): Promise { + return this._baseIndexer.getLatestStateIndexedBlock(); + } + + async addContracts (): Promise { + // Watching all the contracts in the subgraph. + await this._graphWatcher.addContracts(); + } + + async watchContract (address: string, kind: string, checkpoint: boolean, startingBlock: number, context?: any): Promise { + return this._baseIndexer.watchContract(address, kind, checkpoint, startingBlock, context); + } + + updateStateStatusMap (address: string, stateStatus: StateStatus): void { + this._baseIndexer.updateStateStatusMap(address, stateStatus); + } + + cacheContract (contract: Contract): void { + return this._baseIndexer.cacheContract(contract); + } + + async saveEventEntity (dbEvent: Event): Promise { + return this._baseIndexer.saveEventEntity(dbEvent); + } + + async saveEvents (dbEvents: Event[]): Promise { + return this._baseIndexer.saveEvents(dbEvents); + } + + async getEventsByFilter (blockHash: string, contract?: string, name?: string): Promise> { + return this._baseIndexer.getEventsByFilter(blockHash, contract, name); + } + + isWatchedContract (address : string): Contract | undefined { + return this._baseIndexer.isWatchedContract(address); + } + + getWatchedContracts (): Contract[] { + return this._baseIndexer.getWatchedContracts(); + } + + getContractsByKind (kind: string): Contract[] { + return this._baseIndexer.getContractsByKind(kind); + } + + async getProcessedBlockCountForRange (fromBlockNumber: number, toBlockNumber: number): Promise<{ expected: number, actual: number }> { + return this._baseIndexer.getProcessedBlockCountForRange(fromBlockNumber, toBlockNumber); + } + + async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise> { + return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.gql.maxEventsBlockRange); + } + + async getSyncStatus (): Promise { + return this._baseIndexer.getSyncStatus(); + } + + async getBlocks (blockFilter: { blockHash?: string, blockNumber?: number }): Promise { + return this._baseIndexer.getBlocks(blockFilter); + } + + async updateSyncStatusIndexedBlock (blockHash: string, blockNumber: number, force = false): Promise { + return this._baseIndexer.updateSyncStatusIndexedBlock(blockHash, blockNumber, force); + } + + async updateSyncStatusChainHead (blockHash: string, blockNumber: number, force = false): Promise { + return this._baseIndexer.updateSyncStatusChainHead(blockHash, blockNumber, force); + } + + async updateSyncStatusCanonicalBlock (blockHash: string, blockNumber: number, force = false): Promise { + const syncStatus = this._baseIndexer.updateSyncStatusCanonicalBlock(blockHash, blockNumber, force); + await this.pruneFrothyEntities(blockNumber); + + return syncStatus; + } + + async updateSyncStatusProcessedBlock (blockHash: string, blockNumber: number, force = false): Promise { + return this._baseIndexer.updateSyncStatusProcessedBlock(blockHash, blockNumber, force); + } + + async updateSyncStatusIndexingError (hasIndexingError: boolean): Promise { + return this._baseIndexer.updateSyncStatusIndexingError(hasIndexingError); + } + + async updateSyncStatus (syncStatus: DeepPartial): Promise { + return this._baseIndexer.updateSyncStatus(syncStatus); + } + + async getEvent (id: string): Promise { + return this._baseIndexer.getEvent(id); + } + + async getBlockProgress (blockHash: string): Promise { + return this._baseIndexer.getBlockProgress(blockHash); + } + + async getBlockProgressEntities (where: FindConditions, options: FindManyOptions): Promise { + return this._baseIndexer.getBlockProgressEntities(where, options); + } + + async getBlocksAtHeight (height: number, isPruned: boolean): Promise { + return this._baseIndexer.getBlocksAtHeight(height, isPruned); + } + + async fetchAndSaveFilteredEventsAndBlocks (startBlock: number, endBlock: number): Promise<{ + blockProgress: BlockProgress, + events: DeepPartial[], + ethFullBlock: EthFullBlock; + ethFullTransactions: EthFullTransaction[]; + }[]> { + return this._baseIndexer.fetchAndSaveFilteredEventsAndBlocks(startBlock, endBlock, this.eventSignaturesMap, this.parseEventNameAndArgs.bind(this)); + } + + async fetchEventsForContracts (blockHash: string, blockNumber: number, addresses: string[]): Promise[]> { + return this._baseIndexer.fetchEventsForContracts(blockHash, blockNumber, addresses, this.eventSignaturesMap, this.parseEventNameAndArgs.bind(this)); + } + + async saveBlockAndFetchEvents (block: DeepPartial): Promise<[ + BlockProgress, + DeepPartial[], + EthFullTransaction[] + ]> { + return this._saveBlockAndFetchEvents(block); + } + + async getBlockEvents (blockHash: string, where: Where, queryOptions: QueryOptions): Promise> { + return this._baseIndexer.getBlockEvents(blockHash, where, queryOptions); + } + + async removeUnknownEvents (block: BlockProgress): Promise { + return this._baseIndexer.removeUnknownEvents(Event, block); + } + + async markBlocksAsPruned (blocks: BlockProgress[]): Promise { + await this._baseIndexer.markBlocksAsPruned(blocks); + + await this._graphWatcher.pruneEntities(FrothyEntity, blocks, SUBGRAPH_ENTITIES); + } + + async pruneFrothyEntities (blockNumber: number): Promise { + await this._graphWatcher.pruneFrothyEntities(FrothyEntity, blockNumber); + } + + async resetLatestEntities (blockNumber: number): Promise { + await this._graphWatcher.resetLatestEntities(blockNumber); + } + + async updateBlockProgress (block: BlockProgress, lastProcessedEventIndex: number): Promise { + return this._baseIndexer.updateBlockProgress(block, lastProcessedEventIndex); + } + + async getAncestorAtHeight (blockHash: string, height: number): Promise { + return this._baseIndexer.getAncestorAtHeight(blockHash, height); + } + + async resetWatcherToBlock (blockNumber: number): Promise { + const entities = [...ENTITIES, FrothyEntity]; + await this._baseIndexer.resetWatcherToBlock(blockNumber, entities); + + await this.resetLatestEntities(blockNumber); + } + + async clearProcessedBlockData (block: BlockProgress): Promise { + const entities = [...ENTITIES, FrothyEntity]; + await this._baseIndexer.clearProcessedBlockData(block, entities); + + await this.resetLatestEntities(block.blockNumber); + } + + getEntityTypesMap (): Map { + return this._entityTypesMap; + } + + getRelationsMap (): Map { + return this._relationsMap; + } + + updateSubgraphState (contractAddress: string, data: any): void { + return updateSubgraphState(this._subgraphStateMap, contractAddress, data); + } + + async dumpSubgraphState (blockHash: string, isStateFinalized = false): Promise { + return dumpSubgraphState(this, this._subgraphStateMap, blockHash, isStateFinalized); + } + + _populateEntityTypesMap (): void { + this._entityTypesMap.set('Factory', { + id: 'ID', + type: 'PairType', + volumeUSD: 'BigDecimal', + volumeNative: 'BigDecimal', + liquidityUSD: 'BigDecimal', + liquidityNative: 'BigDecimal', + feesUSD: 'BigDecimal', + feesNative: 'BigDecimal', + pairCount: 'BigInt', + transactionCount: 'BigInt', + tokenCount: 'BigInt', + userCount: 'BigInt' + }); + this._entityTypesMap.set('Bundle', { + id: 'ID', + nativePrice: 'BigDecimal' + }); + this._entityTypesMap.set('Token', { + id: 'ID', + price: 'TokenPrice', + symbol: 'String', + symbolSuccess: 'Boolean', + name: 'String', + nameSuccess: 'Boolean', + decimals: 'BigInt', + decimalsSuccess: 'Boolean', + liquidity: 'BigInt', + liquidityNative: 'BigDecimal', + liquidityUSD: 'BigDecimal', + volume: 'BigDecimal', + volumeNative: 'BigDecimal', + volumeUSD: 'BigDecimal', + feesNative: 'BigDecimal', + feesUSD: 'BigDecimal', + txCount: 'BigInt', + pairCount: 'BigInt', + whitelistedPairCount: 'BigInt' + }); + this._entityTypesMap.set('TokenPrice', { + id: 'ID', + token: 'Token', + derivedNative: 'BigDecimal', + lastUsdPrice: 'BigDecimal', + pricedOffToken: 'Token', + pricedOffPair: 'Pair' + }); + this._entityTypesMap.set('_TokenPair', { + id: 'ID', + pair: 'Pair', + token: 'Token' + }); + this._entityTypesMap.set('_WhitelistedTokenPair', { + id: 'ID', + pair: 'Pair', + token: 'Token' + }); + this._entityTypesMap.set('Pair', { + id: 'ID', + type: 'PairType', + swapFee: 'BigInt', + twapEnabled: 'Boolean', + name: 'String', + token0: 'Token', + token1: 'Token', + source: 'String', + createdAtBlock: 'BigInt', + createdAtTimestamp: 'BigInt', + reserve0: 'BigInt', + reserve1: 'BigInt', + liquidity: 'BigInt', + liquidityUSD: 'BigDecimal', + liquidityNative: 'BigDecimal', + trackedLiquidityNative: 'BigDecimal', + token0Price: 'BigDecimal', + token1Price: 'BigDecimal', + volumeNative: 'BigDecimal', + volumeUSD: 'BigDecimal', + volumeToken0: 'BigDecimal', + volumeToken1: 'BigDecimal', + feesNative: 'BigDecimal', + feesUSD: 'BigDecimal', + apr: 'BigDecimal', + aprUpdatedAtTimestamp: 'BigInt', + txCount: 'BigInt' + }); + this._entityTypesMap.set('User', { + id: 'ID', + lpSnapshotsCount: 'BigInt' + }); + this._entityTypesMap.set('LiquidityPosition', { + id: 'ID', + pair: 'Pair', + user: 'User', + balance: 'BigInt', + createdAtBlock: 'BigInt', + createdAtTimestamp: 'BigInt' + }); + this._entityTypesMap.set('Mint', { + id: 'ID', + transaction: 'Transaction', + timestamp: 'BigInt', + pair: 'Pair', + to: 'String', + liquidity: 'BigDecimal', + sender: 'Bytes', + amount0: 'BigDecimal', + amount1: 'BigDecimal', + logIndex: 'BigInt', + amountUSD: 'BigDecimal', + feeTo: 'Bytes', + feeLiquidity: 'BigDecimal' + }); + this._entityTypesMap.set('Burn', { + id: 'ID', + transaction: 'Transaction', + timestamp: 'BigInt', + pair: 'Pair', + liquidity: 'BigDecimal', + sender: 'String', + amount0: 'BigDecimal', + amount1: 'BigDecimal', + to: 'String', + logIndex: 'BigInt', + amountUSD: 'BigDecimal', + complete: 'Boolean', + feeTo: 'String', + feeLiquidity: 'BigDecimal' + }); + this._entityTypesMap.set('Swap', { + id: 'ID', + transaction: 'Transaction', + timestamp: 'BigInt', + pair: 'Pair', + sender: 'String', + tokenIn: 'Token', + tokenOut: 'Token', + amountIn: 'BigDecimal', + amountOut: 'BigDecimal', + to: 'String', + logIndex: 'BigInt', + amountUSD: 'BigDecimal' + }); + this._entityTypesMap.set('Transaction', { + id: 'ID', + gasLimit: 'BigInt', + gasPrice: 'BigInt', + mints: 'Mint', + burns: 'Burn', + swaps: 'Swap', + createdAtBlock: 'BigInt', + createdAtTimestamp: 'BigInt' + }); + this._entityTypesMap.set('LiquidityPositionSnapshot', { + id: 'ID', + liquidityPosition: 'LiquidityPosition', + timestamp: 'Int', + block: 'Int', + user: 'User', + pair: 'Pair', + token0PriceUSD: 'BigDecimal', + token1PriceUSD: 'BigDecimal', + reserve0: 'BigInt', + reserve1: 'BigInt', + reserveUSD: 'BigDecimal', + liquidityTokenTotalSupply: 'BigInt', + liquidityTokenBalance: 'BigInt' + }); + this._entityTypesMap.set('PairHourSnapshot', { + id: 'ID', + pair: 'Pair', + date: 'Int', + cumulativeVolumeUSD: 'BigDecimal', + volumeUSD: 'BigDecimal', + volumeNative: 'BigDecimal', + volumeToken0: 'BigDecimal', + volumeToken1: 'BigDecimal', + liquidity: 'BigDecimal', + liquidityNative: 'BigDecimal', + liquidityUSD: 'BigDecimal', + feesNative: 'BigDecimal', + feesUSD: 'BigDecimal', + apr: 'BigDecimal', + transactionCount: 'BigInt' + }); + this._entityTypesMap.set('PairDaySnapshot', { + id: 'ID', + pair: 'Pair', + date: 'Int', + cumulativeVolumeUSD: 'BigDecimal', + volumeUSD: 'BigDecimal', + volumeNative: 'BigDecimal', + volumeToken0: 'BigDecimal', + volumeToken1: 'BigDecimal', + liquidity: 'BigDecimal', + liquidityNative: 'BigDecimal', + liquidityUSD: 'BigDecimal', + feesNative: 'BigDecimal', + feesUSD: 'BigDecimal', + apr: 'BigDecimal', + transactionCount: 'BigInt' + }); + this._entityTypesMap.set('TokenHourSnapshot', { + id: 'ID', + date: 'Int', + token: 'Token', + liquidity: 'BigDecimal', + liquidityNative: 'BigDecimal', + liquidityUSD: 'BigDecimal', + volume: 'BigDecimal', + volumeNative: 'BigDecimal', + volumeUSD: 'BigDecimal', + priceNative: 'BigDecimal', + priceUSD: 'BigDecimal', + feesNative: 'BigDecimal', + feesUSD: 'BigDecimal', + transactionCount: 'BigInt' + }); + this._entityTypesMap.set('TokenDaySnapshot', { + id: 'ID', + date: 'Int', + token: 'Token', + liquidity: 'BigDecimal', + liquidityNative: 'BigDecimal', + liquidityUSD: 'BigDecimal', + volume: 'BigDecimal', + volumeNative: 'BigDecimal', + volumeUSD: 'BigDecimal', + priceNative: 'BigDecimal', + priceUSD: 'BigDecimal', + feesNative: 'BigDecimal', + feesUSD: 'BigDecimal', + transactionCount: 'BigInt' + }); + this._entityTypesMap.set('FactoryHourSnapshot', { + id: 'ID', + factory: 'Factory', + date: 'Int', + volumeUSD: 'BigDecimal', + volumeNative: 'BigDecimal', + liquidityNative: 'BigDecimal', + liquidityUSD: 'BigDecimal', + feesNative: 'BigDecimal', + feesUSD: 'BigDecimal', + transactionCount: 'BigInt' + }); + this._entityTypesMap.set('FactoryDaySnapshot', { + id: 'ID', + factory: 'Factory', + date: 'Int', + volumeUSD: 'BigDecimal', + volumeNative: 'BigDecimal', + liquidityNative: 'BigDecimal', + liquidityUSD: 'BigDecimal', + feesNative: 'BigDecimal', + feesUSD: 'BigDecimal', + transactionCount: 'BigInt' + }); + } + + _populateRelationsMap (): void { + this._relationsMap.set(Token, { + price: { + entity: TokenPrice, + isArray: false, + isDerived: false + }, + pairs: { + entity: _TokenPair, + isArray: true, + isDerived: true, + field: 'token' + }, + whitelistedPairs: { + entity: _WhitelistedTokenPair, + isArray: true, + isDerived: true, + field: 'token' + } + }); + this._relationsMap.set(TokenPrice, { + token: { + entity: Token, + isArray: false, + isDerived: false + }, + pricedOffToken: { + entity: Token, + isArray: false, + isDerived: false + }, + pricedOffPair: { + entity: Pair, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(_TokenPair, { + pair: { + entity: Pair, + isArray: false, + isDerived: false + }, + token: { + entity: Token, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(_WhitelistedTokenPair, { + pair: { + entity: Pair, + isArray: false, + isDerived: false + }, + token: { + entity: Token, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(Pair, { + token0: { + entity: Token, + isArray: false, + isDerived: false + }, + token1: { + entity: Token, + isArray: false, + isDerived: false + }, + liquidityPositions: { + entity: LiquidityPosition, + isArray: true, + isDerived: true, + field: 'pair' + }, + liquidityPositionSnapshots: { + entity: LiquidityPositionSnapshot, + isArray: true, + isDerived: true, + field: 'pair' + }, + hourSnapshots: { + entity: PairHourSnapshot, + isArray: true, + isDerived: true, + field: 'pair' + }, + daySnapshots: { + entity: PairDaySnapshot, + isArray: true, + isDerived: true, + field: 'pair' + } + }); + this._relationsMap.set(User, { + liquidityPositions: { + entity: LiquidityPosition, + isArray: true, + isDerived: true, + field: 'user' + } + }); + this._relationsMap.set(LiquidityPosition, { + pair: { + entity: Pair, + isArray: false, + isDerived: false + }, + user: { + entity: User, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(Mint, { + transaction: { + entity: Transaction, + isArray: false, + isDerived: false + }, + pair: { + entity: Pair, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(Burn, { + transaction: { + entity: Transaction, + isArray: false, + isDerived: false + }, + pair: { + entity: Pair, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(Swap, { + transaction: { + entity: Transaction, + isArray: false, + isDerived: false + }, + pair: { + entity: Pair, + isArray: false, + isDerived: false + }, + tokenIn: { + entity: Token, + isArray: false, + isDerived: false + }, + tokenOut: { + entity: Token, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(Transaction, { + mints: { + entity: Mint, + isArray: true, + isDerived: false + }, + burns: { + entity: Burn, + isArray: true, + isDerived: false + }, + swaps: { + entity: Swap, + isArray: true, + isDerived: false + } + }); + this._relationsMap.set(LiquidityPositionSnapshot, { + liquidityPosition: { + entity: LiquidityPosition, + isArray: false, + isDerived: false + }, + user: { + entity: User, + isArray: false, + isDerived: false + }, + pair: { + entity: Pair, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(PairHourSnapshot, { + pair: { + entity: Pair, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(PairDaySnapshot, { + pair: { + entity: Pair, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(TokenHourSnapshot, { + token: { + entity: Token, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(TokenDaySnapshot, { + token: { + entity: Token, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(FactoryHourSnapshot, { + factory: { + entity: Factory, + isArray: false, + isDerived: false + } + }); + this._relationsMap.set(FactoryDaySnapshot, { + factory: { + entity: Factory, + isArray: false, + isDerived: false + } + }); + } + + async _saveBlockAndFetchEvents ({ + cid: blockCid, + blockHash, + blockNumber, + blockTimestamp, + parentHash + }: DeepPartial): Promise<[ + BlockProgress, + DeepPartial[], + EthFullTransaction[] + ]> { + assert(blockHash); + assert(blockNumber); + + const { events: dbEvents, transactions } = await this._baseIndexer.fetchEvents(blockHash, blockNumber, this.eventSignaturesMap, this.parseEventNameAndArgs.bind(this)); + + const dbTx = await this._db.createTransactionRunner(); + try { + const block = { + cid: blockCid, + blockHash, + blockNumber, + blockTimestamp, + parentHash + }; + + console.time(`time:indexer#_saveBlockAndFetchEvents-db-save-${blockNumber}`); + const blockProgress = await this._db.saveBlockWithEvents(dbTx, block, dbEvents); + await dbTx.commitTransaction(); + console.timeEnd(`time:indexer#_saveBlockAndFetchEvents-db-save-${blockNumber}`); + + return [blockProgress, [], transactions]; + } catch (error) { + await dbTx.rollbackTransaction(); + throw error; + } finally { + await dbTx.release(); + } + } + + async getFullTransactions (txHashList: string[]): Promise { + return this._baseIndexer.getFullTransactions(txHashList); + } +} diff --git a/packages/sushiswap-watcher/src/job-runner.ts b/packages/sushiswap-watcher/src/job-runner.ts new file mode 100644 index 0000000..93d6820 --- /dev/null +++ b/packages/sushiswap-watcher/src/job-runner.ts @@ -0,0 +1,48 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import debug from 'debug'; + +import { JobRunnerCmd } from '@cerc-io/cli'; +import { JobRunner } from '@cerc-io/util'; +import { getGraphDbAndWatcher } from '@cerc-io/graph-node'; + +import { Indexer } from './indexer'; +import { Database, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP } from './database'; + +const log = debug('vulcanize:job-runner'); + +export const main = async (): Promise => { + const jobRunnerCmd = new JobRunnerCmd(); + await jobRunnerCmd.init(Database); + + const { graphWatcher } = await getGraphDbAndWatcher( + jobRunnerCmd.config.server, + jobRunnerCmd.clients.ethClient, + jobRunnerCmd.ethProvider, + jobRunnerCmd.database.baseDatabase, + ENTITY_QUERY_TYPE_MAP, + ENTITY_TO_LATEST_ENTITY_MAP + ); + + await jobRunnerCmd.initIndexer(Indexer, graphWatcher); + + await jobRunnerCmd.exec(async (jobRunner: JobRunner): Promise => { + await jobRunner.subscribeBlockProcessingQueue(); + await jobRunner.subscribeHistoricalProcessingQueue(); + await jobRunner.subscribeEventProcessingQueue(); + await jobRunner.subscribeBlockCheckpointQueue(); + await jobRunner.subscribeHooksQueue(); + }); +}; + +main().then(() => { + log('Starting job runner...'); +}).catch(err => { + log(err); +}); + +process.on('uncaughtException', err => { + log('uncaughtException', err); +}); diff --git a/packages/sushiswap-watcher/src/resolvers.ts b/packages/sushiswap-watcher/src/resolvers.ts new file mode 100644 index 0000000..96e248e --- /dev/null +++ b/packages/sushiswap-watcher/src/resolvers.ts @@ -0,0 +1,1179 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import assert from 'assert'; +import debug from 'debug'; +import { GraphQLResolveInfo } from 'graphql'; +import { ExpressContext } from 'apollo-server-express'; +import winston from 'winston'; + +import { + gqlTotalQueryCount, + gqlQueryCount, + gqlQueryDuration, + getResultState, + IndexerInterface, + GraphQLBigInt, + GraphQLBigDecimal, + BlockHeight, + OrderDirection, + jsonBigIntStringReplacer, + EventWatcher, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + setGQLCacheHints +} from '@cerc-io/util'; + +import { Indexer } from './indexer'; + +import { Factory } from './entity/Factory'; +import { Bundle } from './entity/Bundle'; +import { Token } from './entity/Token'; +import { TokenPrice } from './entity/TokenPrice'; +import { _TokenPair } from './entity/_TokenPair'; +import { _WhitelistedTokenPair } from './entity/_WhitelistedTokenPair'; +import { Pair } from './entity/Pair'; +import { User } from './entity/User'; +import { LiquidityPosition } from './entity/LiquidityPosition'; +import { Mint } from './entity/Mint'; +import { Burn } from './entity/Burn'; +import { Swap } from './entity/Swap'; +import { Transaction } from './entity/Transaction'; +import { LiquidityPositionSnapshot } from './entity/LiquidityPositionSnapshot'; +import { PairHourSnapshot } from './entity/PairHourSnapshot'; +import { PairDaySnapshot } from './entity/PairDaySnapshot'; +import { TokenHourSnapshot } from './entity/TokenHourSnapshot'; +import { TokenDaySnapshot } from './entity/TokenDaySnapshot'; +import { FactoryHourSnapshot } from './entity/FactoryHourSnapshot'; +import { FactoryDaySnapshot } from './entity/FactoryDaySnapshot'; + +const log = debug('vulcanize:resolver'); + +const executeAndRecordMetrics = async ( + indexer: Indexer, + gqlLogger: winston.Logger, + opName: string, + expressContext: ExpressContext, + operation: () => Promise +) => { + gqlTotalQueryCount.inc(1); + gqlQueryCount.labels(opName).inc(1); + const endTimer = gqlQueryDuration.labels(opName).startTimer(); + + try { + const [result, syncStatus] = await Promise.all([ + operation(), + indexer.getSyncStatus() + ]); + + gqlLogger.info({ + opName, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + latestIndexedBlockNumber: syncStatus?.latestIndexedBlockNumber, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + return result; + } catch (error) { + gqlLogger.error({ + opName, + error, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + } finally { + endTimer(); + } +}; + +export const createResolvers = async ( + indexerArg: IndexerInterface, + eventWatcher: EventWatcher, + gqlLogger: winston.Logger +): Promise => { + const indexer = indexerArg as Indexer; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const gqlCacheConfig = indexer.serverConfig.gql.cache; + + return { + BigInt: GraphQLBigInt, + + BigDecimal: GraphQLBigDecimal, + + Event: { + __resolveType: (obj: any) => { + assert(obj.__typename); + + return obj.__typename; + } + }, + + Subscription: { + onEvent: { + subscribe: () => eventWatcher.getEventIterator() + } + }, + + Mutation: { + watchContract: async (_: any, { address, kind, checkpoint, startingBlock = 1 }: { address: string, kind: string, checkpoint: boolean, startingBlock: number }): Promise => { + log('watchContract', address, kind, checkpoint, startingBlock); + await indexer.watchContract(address, kind, checkpoint, startingBlock); + + return true; + } + }, + + Query: { + factory: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('factory', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'factory', + expressContext, + async () => indexer.getSubgraphEntity(Factory, id, block, info) + ); + }, + + factories: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('factories', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'factories', + expressContext, + async () => indexer.getSubgraphEntities( + Factory, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + bundle: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('bundle', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'bundle', + expressContext, + async () => indexer.getSubgraphEntity(Bundle, id, block, info) + ); + }, + + bundles: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('bundles', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'bundles', + expressContext, + async () => indexer.getSubgraphEntities( + Bundle, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + token: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('token', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'token', + expressContext, + async () => indexer.getSubgraphEntity(Token, id, block, info) + ); + }, + + tokens: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('tokens', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'tokens', + expressContext, + async () => indexer.getSubgraphEntities( + Token, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + tokenPrice: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('tokenPrice', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'tokenPrice', + expressContext, + async () => indexer.getSubgraphEntity(TokenPrice, id, block, info) + ); + }, + + tokenPrices: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('tokenPrices', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'tokenPrices', + expressContext, + async () => indexer.getSubgraphEntities( + TokenPrice, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + _TokenPair: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('_TokenPair', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + '_TokenPair', + expressContext, + async () => indexer.getSubgraphEntity(_TokenPair, id, block, info) + ); + }, + + _TokenPairs: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('_TokenPairs', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + '_TokenPairs', + expressContext, + async () => indexer.getSubgraphEntities( + _TokenPair, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + _WhitelistedTokenPair: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('_WhitelistedTokenPair', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + '_WhitelistedTokenPair', + expressContext, + async () => indexer.getSubgraphEntity(_WhitelistedTokenPair, id, block, info) + ); + }, + + _WhitelistedTokenPairs: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('_WhitelistedTokenPairs', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + '_WhitelistedTokenPairs', + expressContext, + async () => indexer.getSubgraphEntities( + _WhitelistedTokenPair, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + pair: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('pair', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'pair', + expressContext, + async () => indexer.getSubgraphEntity(Pair, id, block, info) + ); + }, + + pairs: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('pairs', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'pairs', + expressContext, + async () => indexer.getSubgraphEntities( + Pair, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + user: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('user', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'user', + expressContext, + async () => indexer.getSubgraphEntity(User, id, block, info) + ); + }, + + users: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('users', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'users', + expressContext, + async () => indexer.getSubgraphEntities( + User, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + liquidityPosition: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('liquidityPosition', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'liquidityPosition', + expressContext, + async () => indexer.getSubgraphEntity(LiquidityPosition, id, block, info) + ); + }, + + liquidityPositions: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('liquidityPositions', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'liquidityPositions', + expressContext, + async () => indexer.getSubgraphEntities( + LiquidityPosition, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + mint: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('mint', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'mint', + expressContext, + async () => indexer.getSubgraphEntity(Mint, id, block, info) + ); + }, + + mints: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('mints', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'mints', + expressContext, + async () => indexer.getSubgraphEntities( + Mint, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + burn: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('burn', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'burn', + expressContext, + async () => indexer.getSubgraphEntity(Burn, id, block, info) + ); + }, + + burns: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('burns', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'burns', + expressContext, + async () => indexer.getSubgraphEntities( + Burn, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + swap: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('swap', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'swap', + expressContext, + async () => indexer.getSubgraphEntity(Swap, id, block, info) + ); + }, + + swaps: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('swaps', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'swaps', + expressContext, + async () => indexer.getSubgraphEntities( + Swap, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + transaction: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('transaction', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'transaction', + expressContext, + async () => indexer.getSubgraphEntity(Transaction, id, block, info) + ); + }, + + transactions: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('transactions', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'transactions', + expressContext, + async () => indexer.getSubgraphEntities( + Transaction, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + liquidityPositionSnapshot: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('liquidityPositionSnapshot', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'liquidityPositionSnapshot', + expressContext, + async () => indexer.getSubgraphEntity(LiquidityPositionSnapshot, id, block, info) + ); + }, + + liquidityPositionSnapshots: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('liquidityPositionSnapshots', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'liquidityPositionSnapshots', + expressContext, + async () => indexer.getSubgraphEntities( + LiquidityPositionSnapshot, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + pairHourSnapshot: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('pairHourSnapshot', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'pairHourSnapshot', + expressContext, + async () => indexer.getSubgraphEntity(PairHourSnapshot, id, block, info) + ); + }, + + pairHourSnapshots: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('pairHourSnapshots', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'pairHourSnapshots', + expressContext, + async () => indexer.getSubgraphEntities( + PairHourSnapshot, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + pairDaySnapshot: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('pairDaySnapshot', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'pairDaySnapshot', + expressContext, + async () => indexer.getSubgraphEntity(PairDaySnapshot, id, block, info) + ); + }, + + pairDaySnapshots: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('pairDaySnapshots', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'pairDaySnapshots', + expressContext, + async () => indexer.getSubgraphEntities( + PairDaySnapshot, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + tokenHourSnapshot: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('tokenHourSnapshot', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'tokenHourSnapshot', + expressContext, + async () => indexer.getSubgraphEntity(TokenHourSnapshot, id, block, info) + ); + }, + + tokenHourSnapshots: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('tokenHourSnapshots', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'tokenHourSnapshots', + expressContext, + async () => indexer.getSubgraphEntities( + TokenHourSnapshot, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + tokenDaySnapshot: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('tokenDaySnapshot', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'tokenDaySnapshot', + expressContext, + async () => indexer.getSubgraphEntity(TokenDaySnapshot, id, block, info) + ); + }, + + tokenDaySnapshots: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('tokenDaySnapshots', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'tokenDaySnapshots', + expressContext, + async () => indexer.getSubgraphEntities( + TokenDaySnapshot, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + factoryHourSnapshot: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('factoryHourSnapshot', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'factoryHourSnapshot', + expressContext, + async () => indexer.getSubgraphEntity(FactoryHourSnapshot, id, block, info) + ); + }, + + factoryHourSnapshots: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('factoryHourSnapshots', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'factoryHourSnapshots', + expressContext, + async () => indexer.getSubgraphEntities( + FactoryHourSnapshot, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + factoryDaySnapshot: async ( + _: any, + { id, block = {} }: { id: string, block: BlockHeight }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('factoryDaySnapshot', id, JSON.stringify(block, jsonBigIntStringReplacer)); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'factoryDaySnapshot', + expressContext, + async () => indexer.getSubgraphEntity(FactoryDaySnapshot, id, block, info) + ); + }, + + factoryDaySnapshots: async ( + _: any, + { block = {}, where, first, skip, orderBy, orderDirection }: { block: BlockHeight, where: { [key: string]: any }, first: number, skip: number, orderBy: string, orderDirection: OrderDirection }, + expressContext: ExpressContext, + info: GraphQLResolveInfo + ) => { + log('factoryDaySnapshots', JSON.stringify(block, jsonBigIntStringReplacer), JSON.stringify(where, jsonBigIntStringReplacer), first, skip, orderBy, orderDirection); + + // Set cache-control hints + // setGQLCacheHints(info, block, gqlCacheConfig); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'factoryDaySnapshots', + expressContext, + async () => indexer.getSubgraphEntities( + FactoryDaySnapshot, + block, + where, + { limit: first, skip, orderBy, orderDirection }, + info + ) + ); + }, + + events: async ( + _: any, + { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }, + expressContext: ExpressContext + ) => { + log('events', blockHash, contractAddress, name); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'events', + expressContext, + async () => { + const block = await indexer.getBlockProgress(blockHash); + if (!block || !block.isComplete) { + throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); + } + + const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); + return events.map(event => indexer.getResultEvent(event)); + } + ); + }, + + eventsInRange: async ( + _: any, + { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }, + expressContext: ExpressContext + ) => { + log('eventsInRange', fromBlockNumber, toBlockNumber); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'eventsInRange', + expressContext, + async () => { + const syncStatus = await indexer.getSyncStatus(); + + if (!syncStatus) { + throw new Error('No blocks processed yet'); + } + + if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { + throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); + } + + const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); + return events.map(event => indexer.getResultEvent(event)); + } + ); + }, + + getStateByCID: async ( + _: any, + { cid }: { cid: string }, + expressContext: ExpressContext + ) => { + log('getStateByCID', cid); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getStateByCID', + expressContext, + async () => { + const state = await indexer.getStateByCID(cid); + + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); + }, + + getState: async ( + _: any, + { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }, + expressContext: ExpressContext + ) => { + log('getState', blockHash, contractAddress, kind); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getState', + expressContext, + async () => { + const state = await indexer.getPrevState(blockHash, contractAddress, kind); + + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); + }, + + _meta: async ( + _: any, + { block = {} }: { block: BlockHeight }, + expressContext: ExpressContext + ) => { + log('_meta'); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + '_meta', + expressContext, + async () => indexer.getMetaData(block) + ); + }, + + getSyncStatus: async ( + _: any, + __: Record, + expressContext: ExpressContext + ) => { + log('getSyncStatus'); + + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSyncStatus', + expressContext, + async () => indexer.getSyncStatus() + ); + } + } + }; +}; diff --git a/packages/sushiswap-watcher/src/schema.gql b/packages/sushiswap-watcher/src/schema.gql new file mode 100644 index 0000000..978e951 --- /dev/null +++ b/packages/sushiswap-watcher/src/schema.gql @@ -0,0 +1,3798 @@ +directive @cacheControl(maxAge: Int, inheritMaxAge: Boolean, scope: CacheControlScope) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION + +enum CacheControlScope { + PUBLIC + PRIVATE +} + +scalar BigInt + +scalar BigDecimal + +scalar Bytes + +type Proof { + data: String! +} + +type _Block_ { + cid: String + hash: String! + number: Int! + timestamp: Int! + parentHash: String! +} + +type _Transaction_ { + hash: String! + index: Int! + from: String! + to: String! +} + +type ResultEvent { + block: _Block_! + tx: _Transaction_! + contract: String! + eventIndex: Int! + event: Event! + proof: Proof +} + +union Event = PairCreatedEvent | ApprovalEvent | BurnEvent | MintEvent | SwapEvent | SyncEvent | TransferEvent + +type PairCreatedEvent { + token0: String! + token1: String! + pair: String! + null: BigInt! +} + +type ApprovalEvent { + owner: String! + spender: String! + value: BigInt! +} + +type BurnEvent { + sender: String! + amount0: BigInt! + amount1: BigInt! + to: String! +} + +type MintEvent { + sender: String! + amount0: BigInt! + amount1: BigInt! +} + +type SwapEvent { + sender: String! + amount0In: BigInt! + amount1In: BigInt! + amount0Out: BigInt! + amount1Out: BigInt! + to: String! +} + +type SyncEvent { + reserve0: BigInt! + reserve1: BigInt! +} + +type TransferEvent { + from: String! + to: String! + value: BigInt! +} + +input Block_height { + hash: Bytes + number: Int +} + +input BlockChangedFilter { + number_gte: Int! +} + +enum OrderDirection { + asc + desc +} + +enum Factory_orderBy { + id + type + volumeUSD + volumeNative + liquidityUSD + liquidityNative + feesUSD + feesNative + pairCount + transactionCount + tokenCount + userCount +} + +input Factory_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + type: PairType + type_not: PairType + type_gt: PairType + type_lt: PairType + type_gte: PairType + type_lte: PairType + type_in: [PairType!] + type_not_in: [PairType!] + volumeUSD: BigDecimal + volumeUSD_not: BigDecimal + volumeUSD_gt: BigDecimal + volumeUSD_lt: BigDecimal + volumeUSD_gte: BigDecimal + volumeUSD_lte: BigDecimal + volumeUSD_in: [BigDecimal!] + volumeUSD_not_in: [BigDecimal!] + volumeNative: BigDecimal + volumeNative_not: BigDecimal + volumeNative_gt: BigDecimal + volumeNative_lt: BigDecimal + volumeNative_gte: BigDecimal + volumeNative_lte: BigDecimal + volumeNative_in: [BigDecimal!] + volumeNative_not_in: [BigDecimal!] + liquidityUSD: BigDecimal + liquidityUSD_not: BigDecimal + liquidityUSD_gt: BigDecimal + liquidityUSD_lt: BigDecimal + liquidityUSD_gte: BigDecimal + liquidityUSD_lte: BigDecimal + liquidityUSD_in: [BigDecimal!] + liquidityUSD_not_in: [BigDecimal!] + liquidityNative: BigDecimal + liquidityNative_not: BigDecimal + liquidityNative_gt: BigDecimal + liquidityNative_lt: BigDecimal + liquidityNative_gte: BigDecimal + liquidityNative_lte: BigDecimal + liquidityNative_in: [BigDecimal!] + liquidityNative_not_in: [BigDecimal!] + feesUSD: BigDecimal + feesUSD_not: BigDecimal + feesUSD_gt: BigDecimal + feesUSD_lt: BigDecimal + feesUSD_gte: BigDecimal + feesUSD_lte: BigDecimal + feesUSD_in: [BigDecimal!] + feesUSD_not_in: [BigDecimal!] + feesNative: BigDecimal + feesNative_not: BigDecimal + feesNative_gt: BigDecimal + feesNative_lt: BigDecimal + feesNative_gte: BigDecimal + feesNative_lte: BigDecimal + feesNative_in: [BigDecimal!] + feesNative_not_in: [BigDecimal!] + pairCount: BigInt + pairCount_not: BigInt + pairCount_gt: BigInt + pairCount_lt: BigInt + pairCount_gte: BigInt + pairCount_lte: BigInt + pairCount_in: [BigInt!] + pairCount_not_in: [BigInt!] + transactionCount: BigInt + transactionCount_not: BigInt + transactionCount_gt: BigInt + transactionCount_lt: BigInt + transactionCount_gte: BigInt + transactionCount_lte: BigInt + transactionCount_in: [BigInt!] + transactionCount_not_in: [BigInt!] + tokenCount: BigInt + tokenCount_not: BigInt + tokenCount_gt: BigInt + tokenCount_lt: BigInt + tokenCount_gte: BigInt + tokenCount_lte: BigInt + tokenCount_in: [BigInt!] + tokenCount_not_in: [BigInt!] + userCount: BigInt + userCount_not: BigInt + userCount_gt: BigInt + userCount_lt: BigInt + userCount_gte: BigInt + userCount_lte: BigInt + userCount_in: [BigInt!] + userCount_not_in: [BigInt!] + _change_block: BlockChangedFilter + and: [Factory_filter] + or: [Factory_filter] +} + +enum PairType { + CONSTANT_PRODUCT_POOL +} + +enum Bundle_orderBy { + id + nativePrice +} + +input Bundle_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + nativePrice: BigDecimal + nativePrice_not: BigDecimal + nativePrice_gt: BigDecimal + nativePrice_lt: BigDecimal + nativePrice_gte: BigDecimal + nativePrice_lte: BigDecimal + nativePrice_in: [BigDecimal!] + nativePrice_not_in: [BigDecimal!] + _change_block: BlockChangedFilter + and: [Bundle_filter] + or: [Bundle_filter] +} + +enum Token_orderBy { + id + price + price__id + price__derivedNative + price__lastUsdPrice + symbol + symbolSuccess + name + nameSuccess + decimals + decimalsSuccess + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + feesNative + feesUSD + txCount + pairCount + whitelistedPairCount + pairs + whitelistedPairs +} + +input Token_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + price: String + price_not: String + price_gt: String + price_lt: String + price_gte: String + price_lte: String + price_in: [String!] + price_not_in: [String!] + price_starts_with: String + price_starts_with_nocase: String + price_not_starts_with: String + price_not_starts_with_nocase: String + price_ends_with: String + price_ends_with_nocase: String + price_not_ends_with: String + price_not_ends_with_nocase: String + price_contains: String + price_not_contains: String + price_contains_nocase: String + price_not_contains_nocase: String + price_: TokenPrice_filter + symbol: String + symbol_not: String + symbol_gt: String + symbol_lt: String + symbol_gte: String + symbol_lte: String + symbol_in: [String!] + symbol_not_in: [String!] + symbol_starts_with: String + symbol_starts_with_nocase: String + symbol_not_starts_with: String + symbol_not_starts_with_nocase: String + symbol_ends_with: String + symbol_ends_with_nocase: String + symbol_not_ends_with: String + symbol_not_ends_with_nocase: String + symbol_contains: String + symbol_not_contains: String + symbol_contains_nocase: String + symbol_not_contains_nocase: String + symbolSuccess: Boolean + symbolSuccess_not: Boolean + symbolSuccess_gt: Boolean + symbolSuccess_lt: Boolean + symbolSuccess_gte: Boolean + symbolSuccess_lte: Boolean + symbolSuccess_in: [Boolean!] + symbolSuccess_not_in: [Boolean!] + name: String + name_not: String + name_gt: String + name_lt: String + name_gte: String + name_lte: String + name_in: [String!] + name_not_in: [String!] + name_starts_with: String + name_starts_with_nocase: String + name_not_starts_with: String + name_not_starts_with_nocase: String + name_ends_with: String + name_ends_with_nocase: String + name_not_ends_with: String + name_not_ends_with_nocase: String + name_contains: String + name_not_contains: String + name_contains_nocase: String + name_not_contains_nocase: String + nameSuccess: Boolean + nameSuccess_not: Boolean + nameSuccess_gt: Boolean + nameSuccess_lt: Boolean + nameSuccess_gte: Boolean + nameSuccess_lte: Boolean + nameSuccess_in: [Boolean!] + nameSuccess_not_in: [Boolean!] + decimals: BigInt + decimals_not: BigInt + decimals_gt: BigInt + decimals_lt: BigInt + decimals_gte: BigInt + decimals_lte: BigInt + decimals_in: [BigInt!] + decimals_not_in: [BigInt!] + decimalsSuccess: Boolean + decimalsSuccess_not: Boolean + decimalsSuccess_gt: Boolean + decimalsSuccess_lt: Boolean + decimalsSuccess_gte: Boolean + decimalsSuccess_lte: Boolean + decimalsSuccess_in: [Boolean!] + decimalsSuccess_not_in: [Boolean!] + liquidity: BigInt + liquidity_not: BigInt + liquidity_gt: BigInt + liquidity_lt: BigInt + liquidity_gte: BigInt + liquidity_lte: BigInt + liquidity_in: [BigInt!] + liquidity_not_in: [BigInt!] + liquidityNative: BigDecimal + liquidityNative_not: BigDecimal + liquidityNative_gt: BigDecimal + liquidityNative_lt: BigDecimal + liquidityNative_gte: BigDecimal + liquidityNative_lte: BigDecimal + liquidityNative_in: [BigDecimal!] + liquidityNative_not_in: [BigDecimal!] + liquidityUSD: BigDecimal + liquidityUSD_not: BigDecimal + liquidityUSD_gt: BigDecimal + liquidityUSD_lt: BigDecimal + liquidityUSD_gte: BigDecimal + liquidityUSD_lte: BigDecimal + liquidityUSD_in: [BigDecimal!] + liquidityUSD_not_in: [BigDecimal!] + volume: BigDecimal + volume_not: BigDecimal + volume_gt: BigDecimal + volume_lt: BigDecimal + volume_gte: BigDecimal + volume_lte: BigDecimal + volume_in: [BigDecimal!] + volume_not_in: [BigDecimal!] + volumeNative: BigDecimal + volumeNative_not: BigDecimal + volumeNative_gt: BigDecimal + volumeNative_lt: BigDecimal + volumeNative_gte: BigDecimal + volumeNative_lte: BigDecimal + volumeNative_in: [BigDecimal!] + volumeNative_not_in: [BigDecimal!] + volumeUSD: BigDecimal + volumeUSD_not: BigDecimal + volumeUSD_gt: BigDecimal + volumeUSD_lt: BigDecimal + volumeUSD_gte: BigDecimal + volumeUSD_lte: BigDecimal + volumeUSD_in: [BigDecimal!] + volumeUSD_not_in: [BigDecimal!] + feesNative: BigDecimal + feesNative_not: BigDecimal + feesNative_gt: BigDecimal + feesNative_lt: BigDecimal + feesNative_gte: BigDecimal + feesNative_lte: BigDecimal + feesNative_in: [BigDecimal!] + feesNative_not_in: [BigDecimal!] + feesUSD: BigDecimal + feesUSD_not: BigDecimal + feesUSD_gt: BigDecimal + feesUSD_lt: BigDecimal + feesUSD_gte: BigDecimal + feesUSD_lte: BigDecimal + feesUSD_in: [BigDecimal!] + feesUSD_not_in: [BigDecimal!] + txCount: BigInt + txCount_not: BigInt + txCount_gt: BigInt + txCount_lt: BigInt + txCount_gte: BigInt + txCount_lte: BigInt + txCount_in: [BigInt!] + txCount_not_in: [BigInt!] + pairCount: BigInt + pairCount_not: BigInt + pairCount_gt: BigInt + pairCount_lt: BigInt + pairCount_gte: BigInt + pairCount_lte: BigInt + pairCount_in: [BigInt!] + pairCount_not_in: [BigInt!] + whitelistedPairCount: BigInt + whitelistedPairCount_not: BigInt + whitelistedPairCount_gt: BigInt + whitelistedPairCount_lt: BigInt + whitelistedPairCount_gte: BigInt + whitelistedPairCount_lte: BigInt + whitelistedPairCount_in: [BigInt!] + whitelistedPairCount_not_in: [BigInt!] + pairs_: _TokenPair_filter + whitelistedPairs_: _WhitelistedTokenPair_filter + _change_block: BlockChangedFilter + and: [Token_filter] + or: [Token_filter] +} + +enum TokenPrice_orderBy { + id + token + token__id + token__symbol + token__symbolSuccess + token__name + token__nameSuccess + token__decimals + token__decimalsSuccess + token__liquidity + token__liquidityNative + token__liquidityUSD + token__volume + token__volumeNative + token__volumeUSD + token__feesNative + token__feesUSD + token__txCount + token__pairCount + token__whitelistedPairCount + derivedNative + lastUsdPrice + pricedOffToken + pricedOffToken__id + pricedOffToken__symbol + pricedOffToken__symbolSuccess + pricedOffToken__name + pricedOffToken__nameSuccess + pricedOffToken__decimals + pricedOffToken__decimalsSuccess + pricedOffToken__liquidity + pricedOffToken__liquidityNative + pricedOffToken__liquidityUSD + pricedOffToken__volume + pricedOffToken__volumeNative + pricedOffToken__volumeUSD + pricedOffToken__feesNative + pricedOffToken__feesUSD + pricedOffToken__txCount + pricedOffToken__pairCount + pricedOffToken__whitelistedPairCount + pricedOffPair + pricedOffPair__id + pricedOffPair__type + pricedOffPair__swapFee + pricedOffPair__twapEnabled + pricedOffPair__name + pricedOffPair__source + pricedOffPair__createdAtBlock + pricedOffPair__createdAtTimestamp + pricedOffPair__reserve0 + pricedOffPair__reserve1 + pricedOffPair__liquidity + pricedOffPair__liquidityUSD + pricedOffPair__liquidityNative + pricedOffPair__trackedLiquidityNative + pricedOffPair__token0Price + pricedOffPair__token1Price + pricedOffPair__volumeNative + pricedOffPair__volumeUSD + pricedOffPair__volumeToken0 + pricedOffPair__volumeToken1 + pricedOffPair__feesNative + pricedOffPair__feesUSD + pricedOffPair__apr + pricedOffPair__aprUpdatedAtTimestamp + pricedOffPair__txCount +} + +input TokenPrice_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + token: String + token_not: String + token_gt: String + token_lt: String + token_gte: String + token_lte: String + token_in: [String!] + token_not_in: [String!] + token_starts_with: String + token_starts_with_nocase: String + token_not_starts_with: String + token_not_starts_with_nocase: String + token_ends_with: String + token_ends_with_nocase: String + token_not_ends_with: String + token_not_ends_with_nocase: String + token_contains: String + token_not_contains: String + token_contains_nocase: String + token_not_contains_nocase: String + token_: Token_filter + derivedNative: BigDecimal + derivedNative_not: BigDecimal + derivedNative_gt: BigDecimal + derivedNative_lt: BigDecimal + derivedNative_gte: BigDecimal + derivedNative_lte: BigDecimal + derivedNative_in: [BigDecimal!] + derivedNative_not_in: [BigDecimal!] + lastUsdPrice: BigDecimal + lastUsdPrice_not: BigDecimal + lastUsdPrice_gt: BigDecimal + lastUsdPrice_lt: BigDecimal + lastUsdPrice_gte: BigDecimal + lastUsdPrice_lte: BigDecimal + lastUsdPrice_in: [BigDecimal!] + lastUsdPrice_not_in: [BigDecimal!] + pricedOffToken: String + pricedOffToken_not: String + pricedOffToken_gt: String + pricedOffToken_lt: String + pricedOffToken_gte: String + pricedOffToken_lte: String + pricedOffToken_in: [String!] + pricedOffToken_not_in: [String!] + pricedOffToken_starts_with: String + pricedOffToken_starts_with_nocase: String + pricedOffToken_not_starts_with: String + pricedOffToken_not_starts_with_nocase: String + pricedOffToken_ends_with: String + pricedOffToken_ends_with_nocase: String + pricedOffToken_not_ends_with: String + pricedOffToken_not_ends_with_nocase: String + pricedOffToken_contains: String + pricedOffToken_not_contains: String + pricedOffToken_contains_nocase: String + pricedOffToken_not_contains_nocase: String + pricedOffToken_: Token_filter + pricedOffPair: String + pricedOffPair_not: String + pricedOffPair_gt: String + pricedOffPair_lt: String + pricedOffPair_gte: String + pricedOffPair_lte: String + pricedOffPair_in: [String!] + pricedOffPair_not_in: [String!] + pricedOffPair_starts_with: String + pricedOffPair_starts_with_nocase: String + pricedOffPair_not_starts_with: String + pricedOffPair_not_starts_with_nocase: String + pricedOffPair_ends_with: String + pricedOffPair_ends_with_nocase: String + pricedOffPair_not_ends_with: String + pricedOffPair_not_ends_with_nocase: String + pricedOffPair_contains: String + pricedOffPair_not_contains: String + pricedOffPair_contains_nocase: String + pricedOffPair_not_contains_nocase: String + pricedOffPair_: Pair_filter + _change_block: BlockChangedFilter + and: [TokenPrice_filter] + or: [TokenPrice_filter] +} + +enum _TokenPair_orderBy { + id + pair + pair__id + pair__type + pair__swapFee + pair__twapEnabled + pair__name + pair__source + pair__createdAtBlock + pair__createdAtTimestamp + pair__reserve0 + pair__reserve1 + pair__liquidity + pair__liquidityUSD + pair__liquidityNative + pair__trackedLiquidityNative + pair__token0Price + pair__token1Price + pair__volumeNative + pair__volumeUSD + pair__volumeToken0 + pair__volumeToken1 + pair__feesNative + pair__feesUSD + pair__apr + pair__aprUpdatedAtTimestamp + pair__txCount + token + token__id + token__symbol + token__symbolSuccess + token__name + token__nameSuccess + token__decimals + token__decimalsSuccess + token__liquidity + token__liquidityNative + token__liquidityUSD + token__volume + token__volumeNative + token__volumeUSD + token__feesNative + token__feesUSD + token__txCount + token__pairCount + token__whitelistedPairCount +} + +input _TokenPair_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + pair: String + pair_not: String + pair_gt: String + pair_lt: String + pair_gte: String + pair_lte: String + pair_in: [String!] + pair_not_in: [String!] + pair_starts_with: String + pair_starts_with_nocase: String + pair_not_starts_with: String + pair_not_starts_with_nocase: String + pair_ends_with: String + pair_ends_with_nocase: String + pair_not_ends_with: String + pair_not_ends_with_nocase: String + pair_contains: String + pair_not_contains: String + pair_contains_nocase: String + pair_not_contains_nocase: String + pair_: Pair_filter + token: String + token_not: String + token_gt: String + token_lt: String + token_gte: String + token_lte: String + token_in: [String!] + token_not_in: [String!] + token_starts_with: String + token_starts_with_nocase: String + token_not_starts_with: String + token_not_starts_with_nocase: String + token_ends_with: String + token_ends_with_nocase: String + token_not_ends_with: String + token_not_ends_with_nocase: String + token_contains: String + token_not_contains: String + token_contains_nocase: String + token_not_contains_nocase: String + token_: Token_filter + _change_block: BlockChangedFilter + and: [_TokenPair_filter] + or: [_TokenPair_filter] +} + +enum _WhitelistedTokenPair_orderBy { + id + pair + pair__id + pair__type + pair__swapFee + pair__twapEnabled + pair__name + pair__source + pair__createdAtBlock + pair__createdAtTimestamp + pair__reserve0 + pair__reserve1 + pair__liquidity + pair__liquidityUSD + pair__liquidityNative + pair__trackedLiquidityNative + pair__token0Price + pair__token1Price + pair__volumeNative + pair__volumeUSD + pair__volumeToken0 + pair__volumeToken1 + pair__feesNative + pair__feesUSD + pair__apr + pair__aprUpdatedAtTimestamp + pair__txCount + token + token__id + token__symbol + token__symbolSuccess + token__name + token__nameSuccess + token__decimals + token__decimalsSuccess + token__liquidity + token__liquidityNative + token__liquidityUSD + token__volume + token__volumeNative + token__volumeUSD + token__feesNative + token__feesUSD + token__txCount + token__pairCount + token__whitelistedPairCount +} + +input _WhitelistedTokenPair_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + pair: String + pair_not: String + pair_gt: String + pair_lt: String + pair_gte: String + pair_lte: String + pair_in: [String!] + pair_not_in: [String!] + pair_starts_with: String + pair_starts_with_nocase: String + pair_not_starts_with: String + pair_not_starts_with_nocase: String + pair_ends_with: String + pair_ends_with_nocase: String + pair_not_ends_with: String + pair_not_ends_with_nocase: String + pair_contains: String + pair_not_contains: String + pair_contains_nocase: String + pair_not_contains_nocase: String + pair_: Pair_filter + token: String + token_not: String + token_gt: String + token_lt: String + token_gte: String + token_lte: String + token_in: [String!] + token_not_in: [String!] + token_starts_with: String + token_starts_with_nocase: String + token_not_starts_with: String + token_not_starts_with_nocase: String + token_ends_with: String + token_ends_with_nocase: String + token_not_ends_with: String + token_not_ends_with_nocase: String + token_contains: String + token_not_contains: String + token_contains_nocase: String + token_not_contains_nocase: String + token_: Token_filter + _change_block: BlockChangedFilter + and: [_WhitelistedTokenPair_filter] + or: [_WhitelistedTokenPair_filter] +} + +enum Pair_orderBy { + id + type + swapFee + twapEnabled + name + token0 + token0__id + token0__symbol + token0__symbolSuccess + token0__name + token0__nameSuccess + token0__decimals + token0__decimalsSuccess + token0__liquidity + token0__liquidityNative + token0__liquidityUSD + token0__volume + token0__volumeNative + token0__volumeUSD + token0__feesNative + token0__feesUSD + token0__txCount + token0__pairCount + token0__whitelistedPairCount + token1 + token1__id + token1__symbol + token1__symbolSuccess + token1__name + token1__nameSuccess + token1__decimals + token1__decimalsSuccess + token1__liquidity + token1__liquidityNative + token1__liquidityUSD + token1__volume + token1__volumeNative + token1__volumeUSD + token1__feesNative + token1__feesUSD + token1__txCount + token1__pairCount + token1__whitelistedPairCount + source + createdAtBlock + createdAtTimestamp + reserve0 + reserve1 + liquidity + liquidityUSD + liquidityNative + trackedLiquidityNative + token0Price + token1Price + volumeNative + volumeUSD + volumeToken0 + volumeToken1 + feesNative + feesUSD + apr + aprUpdatedAtTimestamp + txCount + liquidityPositions + liquidityPositionSnapshots + hourSnapshots + daySnapshots +} + +input Pair_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + type: PairType + type_not: PairType + type_gt: PairType + type_lt: PairType + type_gte: PairType + type_lte: PairType + type_in: [PairType!] + type_not_in: [PairType!] + swapFee: BigInt + swapFee_not: BigInt + swapFee_gt: BigInt + swapFee_lt: BigInt + swapFee_gte: BigInt + swapFee_lte: BigInt + swapFee_in: [BigInt!] + swapFee_not_in: [BigInt!] + twapEnabled: Boolean + twapEnabled_not: Boolean + twapEnabled_gt: Boolean + twapEnabled_lt: Boolean + twapEnabled_gte: Boolean + twapEnabled_lte: Boolean + twapEnabled_in: [Boolean!] + twapEnabled_not_in: [Boolean!] + name: String + name_not: String + name_gt: String + name_lt: String + name_gte: String + name_lte: String + name_in: [String!] + name_not_in: [String!] + name_starts_with: String + name_starts_with_nocase: String + name_not_starts_with: String + name_not_starts_with_nocase: String + name_ends_with: String + name_ends_with_nocase: String + name_not_ends_with: String + name_not_ends_with_nocase: String + name_contains: String + name_not_contains: String + name_contains_nocase: String + name_not_contains_nocase: String + token0: String + token0_not: String + token0_gt: String + token0_lt: String + token0_gte: String + token0_lte: String + token0_in: [String!] + token0_not_in: [String!] + token0_starts_with: String + token0_starts_with_nocase: String + token0_not_starts_with: String + token0_not_starts_with_nocase: String + token0_ends_with: String + token0_ends_with_nocase: String + token0_not_ends_with: String + token0_not_ends_with_nocase: String + token0_contains: String + token0_not_contains: String + token0_contains_nocase: String + token0_not_contains_nocase: String + token0_: Token_filter + token1: String + token1_not: String + token1_gt: String + token1_lt: String + token1_gte: String + token1_lte: String + token1_in: [String!] + token1_not_in: [String!] + token1_starts_with: String + token1_starts_with_nocase: String + token1_not_starts_with: String + token1_not_starts_with_nocase: String + token1_ends_with: String + token1_ends_with_nocase: String + token1_not_ends_with: String + token1_not_ends_with_nocase: String + token1_contains: String + token1_not_contains: String + token1_contains_nocase: String + token1_not_contains_nocase: String + token1_: Token_filter + source: String + source_not: String + source_gt: String + source_lt: String + source_gte: String + source_lte: String + source_in: [String!] + source_not_in: [String!] + source_starts_with: String + source_starts_with_nocase: String + source_not_starts_with: String + source_not_starts_with_nocase: String + source_ends_with: String + source_ends_with_nocase: String + source_not_ends_with: String + source_not_ends_with_nocase: String + source_contains: String + source_not_contains: String + source_contains_nocase: String + source_not_contains_nocase: String + createdAtBlock: BigInt + createdAtBlock_not: BigInt + createdAtBlock_gt: BigInt + createdAtBlock_lt: BigInt + createdAtBlock_gte: BigInt + createdAtBlock_lte: BigInt + createdAtBlock_in: [BigInt!] + createdAtBlock_not_in: [BigInt!] + createdAtTimestamp: BigInt + createdAtTimestamp_not: BigInt + createdAtTimestamp_gt: BigInt + createdAtTimestamp_lt: BigInt + createdAtTimestamp_gte: BigInt + createdAtTimestamp_lte: BigInt + createdAtTimestamp_in: [BigInt!] + createdAtTimestamp_not_in: [BigInt!] + reserve0: BigInt + reserve0_not: BigInt + reserve0_gt: BigInt + reserve0_lt: BigInt + reserve0_gte: BigInt + reserve0_lte: BigInt + reserve0_in: [BigInt!] + reserve0_not_in: [BigInt!] + reserve1: BigInt + reserve1_not: BigInt + reserve1_gt: BigInt + reserve1_lt: BigInt + reserve1_gte: BigInt + reserve1_lte: BigInt + reserve1_in: [BigInt!] + reserve1_not_in: [BigInt!] + liquidity: BigInt + liquidity_not: BigInt + liquidity_gt: BigInt + liquidity_lt: BigInt + liquidity_gte: BigInt + liquidity_lte: BigInt + liquidity_in: [BigInt!] + liquidity_not_in: [BigInt!] + liquidityUSD: BigDecimal + liquidityUSD_not: BigDecimal + liquidityUSD_gt: BigDecimal + liquidityUSD_lt: BigDecimal + liquidityUSD_gte: BigDecimal + liquidityUSD_lte: BigDecimal + liquidityUSD_in: [BigDecimal!] + liquidityUSD_not_in: [BigDecimal!] + liquidityNative: BigDecimal + liquidityNative_not: BigDecimal + liquidityNative_gt: BigDecimal + liquidityNative_lt: BigDecimal + liquidityNative_gte: BigDecimal + liquidityNative_lte: BigDecimal + liquidityNative_in: [BigDecimal!] + liquidityNative_not_in: [BigDecimal!] + trackedLiquidityNative: BigDecimal + trackedLiquidityNative_not: BigDecimal + trackedLiquidityNative_gt: BigDecimal + trackedLiquidityNative_lt: BigDecimal + trackedLiquidityNative_gte: BigDecimal + trackedLiquidityNative_lte: BigDecimal + trackedLiquidityNative_in: [BigDecimal!] + trackedLiquidityNative_not_in: [BigDecimal!] + token0Price: BigDecimal + token0Price_not: BigDecimal + token0Price_gt: BigDecimal + token0Price_lt: BigDecimal + token0Price_gte: BigDecimal + token0Price_lte: BigDecimal + token0Price_in: [BigDecimal!] + token0Price_not_in: [BigDecimal!] + token1Price: BigDecimal + token1Price_not: BigDecimal + token1Price_gt: BigDecimal + token1Price_lt: BigDecimal + token1Price_gte: BigDecimal + token1Price_lte: BigDecimal + token1Price_in: [BigDecimal!] + token1Price_not_in: [BigDecimal!] + volumeNative: BigDecimal + volumeNative_not: BigDecimal + volumeNative_gt: BigDecimal + volumeNative_lt: BigDecimal + volumeNative_gte: BigDecimal + volumeNative_lte: BigDecimal + volumeNative_in: [BigDecimal!] + volumeNative_not_in: [BigDecimal!] + volumeUSD: BigDecimal + volumeUSD_not: BigDecimal + volumeUSD_gt: BigDecimal + volumeUSD_lt: BigDecimal + volumeUSD_gte: BigDecimal + volumeUSD_lte: BigDecimal + volumeUSD_in: [BigDecimal!] + volumeUSD_not_in: [BigDecimal!] + volumeToken0: BigDecimal + volumeToken0_not: BigDecimal + volumeToken0_gt: BigDecimal + volumeToken0_lt: BigDecimal + volumeToken0_gte: BigDecimal + volumeToken0_lte: BigDecimal + volumeToken0_in: [BigDecimal!] + volumeToken0_not_in: [BigDecimal!] + volumeToken1: BigDecimal + volumeToken1_not: BigDecimal + volumeToken1_gt: BigDecimal + volumeToken1_lt: BigDecimal + volumeToken1_gte: BigDecimal + volumeToken1_lte: BigDecimal + volumeToken1_in: [BigDecimal!] + volumeToken1_not_in: [BigDecimal!] + feesNative: BigDecimal + feesNative_not: BigDecimal + feesNative_gt: BigDecimal + feesNative_lt: BigDecimal + feesNative_gte: BigDecimal + feesNative_lte: BigDecimal + feesNative_in: [BigDecimal!] + feesNative_not_in: [BigDecimal!] + feesUSD: BigDecimal + feesUSD_not: BigDecimal + feesUSD_gt: BigDecimal + feesUSD_lt: BigDecimal + feesUSD_gte: BigDecimal + feesUSD_lte: BigDecimal + feesUSD_in: [BigDecimal!] + feesUSD_not_in: [BigDecimal!] + apr: BigDecimal + apr_not: BigDecimal + apr_gt: BigDecimal + apr_lt: BigDecimal + apr_gte: BigDecimal + apr_lte: BigDecimal + apr_in: [BigDecimal!] + apr_not_in: [BigDecimal!] + aprUpdatedAtTimestamp: BigInt + aprUpdatedAtTimestamp_not: BigInt + aprUpdatedAtTimestamp_gt: BigInt + aprUpdatedAtTimestamp_lt: BigInt + aprUpdatedAtTimestamp_gte: BigInt + aprUpdatedAtTimestamp_lte: BigInt + aprUpdatedAtTimestamp_in: [BigInt!] + aprUpdatedAtTimestamp_not_in: [BigInt!] + txCount: BigInt + txCount_not: BigInt + txCount_gt: BigInt + txCount_lt: BigInt + txCount_gte: BigInt + txCount_lte: BigInt + txCount_in: [BigInt!] + txCount_not_in: [BigInt!] + liquidityPositions_: LiquidityPosition_filter + liquidityPositionSnapshots_: LiquidityPositionSnapshot_filter + hourSnapshots_: PairHourSnapshot_filter + daySnapshots_: PairDaySnapshot_filter + _change_block: BlockChangedFilter + and: [Pair_filter] + or: [Pair_filter] +} + +enum User_orderBy { + id + lpSnapshotsCount + liquidityPositions +} + +input User_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + lpSnapshotsCount: BigInt + lpSnapshotsCount_not: BigInt + lpSnapshotsCount_gt: BigInt + lpSnapshotsCount_lt: BigInt + lpSnapshotsCount_gte: BigInt + lpSnapshotsCount_lte: BigInt + lpSnapshotsCount_in: [BigInt!] + lpSnapshotsCount_not_in: [BigInt!] + liquidityPositions_: LiquidityPosition_filter + _change_block: BlockChangedFilter + and: [User_filter] + or: [User_filter] +} + +enum LiquidityPosition_orderBy { + id + pair + pair__id + pair__type + pair__swapFee + pair__twapEnabled + pair__name + pair__source + pair__createdAtBlock + pair__createdAtTimestamp + pair__reserve0 + pair__reserve1 + pair__liquidity + pair__liquidityUSD + pair__liquidityNative + pair__trackedLiquidityNative + pair__token0Price + pair__token1Price + pair__volumeNative + pair__volumeUSD + pair__volumeToken0 + pair__volumeToken1 + pair__feesNative + pair__feesUSD + pair__apr + pair__aprUpdatedAtTimestamp + pair__txCount + user + user__id + user__lpSnapshotsCount + balance + createdAtBlock + createdAtTimestamp +} + +input LiquidityPosition_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + pair: String + pair_not: String + pair_gt: String + pair_lt: String + pair_gte: String + pair_lte: String + pair_in: [String!] + pair_not_in: [String!] + pair_starts_with: String + pair_starts_with_nocase: String + pair_not_starts_with: String + pair_not_starts_with_nocase: String + pair_ends_with: String + pair_ends_with_nocase: String + pair_not_ends_with: String + pair_not_ends_with_nocase: String + pair_contains: String + pair_not_contains: String + pair_contains_nocase: String + pair_not_contains_nocase: String + pair_: Pair_filter + user: String + user_not: String + user_gt: String + user_lt: String + user_gte: String + user_lte: String + user_in: [String!] + user_not_in: [String!] + user_starts_with: String + user_starts_with_nocase: String + user_not_starts_with: String + user_not_starts_with_nocase: String + user_ends_with: String + user_ends_with_nocase: String + user_not_ends_with: String + user_not_ends_with_nocase: String + user_contains: String + user_not_contains: String + user_contains_nocase: String + user_not_contains_nocase: String + user_: User_filter + balance: BigInt + balance_not: BigInt + balance_gt: BigInt + balance_lt: BigInt + balance_gte: BigInt + balance_lte: BigInt + balance_in: [BigInt!] + balance_not_in: [BigInt!] + createdAtBlock: BigInt + createdAtBlock_not: BigInt + createdAtBlock_gt: BigInt + createdAtBlock_lt: BigInt + createdAtBlock_gte: BigInt + createdAtBlock_lte: BigInt + createdAtBlock_in: [BigInt!] + createdAtBlock_not_in: [BigInt!] + createdAtTimestamp: BigInt + createdAtTimestamp_not: BigInt + createdAtTimestamp_gt: BigInt + createdAtTimestamp_lt: BigInt + createdAtTimestamp_gte: BigInt + createdAtTimestamp_lte: BigInt + createdAtTimestamp_in: [BigInt!] + createdAtTimestamp_not_in: [BigInt!] + _change_block: BlockChangedFilter + and: [LiquidityPosition_filter] + or: [LiquidityPosition_filter] +} + +enum Mint_orderBy { + id + transaction + transaction__id + transaction__gasLimit + transaction__gasPrice + transaction__createdAtBlock + transaction__createdAtTimestamp + timestamp + pair + pair__id + pair__type + pair__swapFee + pair__twapEnabled + pair__name + pair__source + pair__createdAtBlock + pair__createdAtTimestamp + pair__reserve0 + pair__reserve1 + pair__liquidity + pair__liquidityUSD + pair__liquidityNative + pair__trackedLiquidityNative + pair__token0Price + pair__token1Price + pair__volumeNative + pair__volumeUSD + pair__volumeToken0 + pair__volumeToken1 + pair__feesNative + pair__feesUSD + pair__apr + pair__aprUpdatedAtTimestamp + pair__txCount + to + liquidity + sender + amount0 + amount1 + logIndex + amountUSD + feeTo + feeLiquidity +} + +input Mint_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + transaction: String + transaction_not: String + transaction_gt: String + transaction_lt: String + transaction_gte: String + transaction_lte: String + transaction_in: [String!] + transaction_not_in: [String!] + transaction_starts_with: String + transaction_starts_with_nocase: String + transaction_not_starts_with: String + transaction_not_starts_with_nocase: String + transaction_ends_with: String + transaction_ends_with_nocase: String + transaction_not_ends_with: String + transaction_not_ends_with_nocase: String + transaction_contains: String + transaction_not_contains: String + transaction_contains_nocase: String + transaction_not_contains_nocase: String + transaction_: Transaction_filter + timestamp: BigInt + timestamp_not: BigInt + timestamp_gt: BigInt + timestamp_lt: BigInt + timestamp_gte: BigInt + timestamp_lte: BigInt + timestamp_in: [BigInt!] + timestamp_not_in: [BigInt!] + pair: String + pair_not: String + pair_gt: String + pair_lt: String + pair_gte: String + pair_lte: String + pair_in: [String!] + pair_not_in: [String!] + pair_starts_with: String + pair_starts_with_nocase: String + pair_not_starts_with: String + pair_not_starts_with_nocase: String + pair_ends_with: String + pair_ends_with_nocase: String + pair_not_ends_with: String + pair_not_ends_with_nocase: String + pair_contains: String + pair_not_contains: String + pair_contains_nocase: String + pair_not_contains_nocase: String + pair_: Pair_filter + to: String + to_not: String + to_gt: String + to_lt: String + to_gte: String + to_lte: String + to_in: [String!] + to_not_in: [String!] + to_starts_with: String + to_starts_with_nocase: String + to_not_starts_with: String + to_not_starts_with_nocase: String + to_ends_with: String + to_ends_with_nocase: String + to_not_ends_with: String + to_not_ends_with_nocase: String + to_contains: String + to_not_contains: String + to_contains_nocase: String + to_not_contains_nocase: String + liquidity: BigDecimal + liquidity_not: BigDecimal + liquidity_gt: BigDecimal + liquidity_lt: BigDecimal + liquidity_gte: BigDecimal + liquidity_lte: BigDecimal + liquidity_in: [BigDecimal!] + liquidity_not_in: [BigDecimal!] + sender: Bytes + sender_not: Bytes + sender_gt: Bytes + sender_lt: Bytes + sender_gte: Bytes + sender_lte: Bytes + sender_in: [Bytes!] + sender_not_in: [Bytes!] + sender_contains: Bytes + sender_not_contains: Bytes + amount0: BigDecimal + amount0_not: BigDecimal + amount0_gt: BigDecimal + amount0_lt: BigDecimal + amount0_gte: BigDecimal + amount0_lte: BigDecimal + amount0_in: [BigDecimal!] + amount0_not_in: [BigDecimal!] + amount1: BigDecimal + amount1_not: BigDecimal + amount1_gt: BigDecimal + amount1_lt: BigDecimal + amount1_gte: BigDecimal + amount1_lte: BigDecimal + amount1_in: [BigDecimal!] + amount1_not_in: [BigDecimal!] + logIndex: BigInt + logIndex_not: BigInt + logIndex_gt: BigInt + logIndex_lt: BigInt + logIndex_gte: BigInt + logIndex_lte: BigInt + logIndex_in: [BigInt!] + logIndex_not_in: [BigInt!] + amountUSD: BigDecimal + amountUSD_not: BigDecimal + amountUSD_gt: BigDecimal + amountUSD_lt: BigDecimal + amountUSD_gte: BigDecimal + amountUSD_lte: BigDecimal + amountUSD_in: [BigDecimal!] + amountUSD_not_in: [BigDecimal!] + feeTo: Bytes + feeTo_not: Bytes + feeTo_gt: Bytes + feeTo_lt: Bytes + feeTo_gte: Bytes + feeTo_lte: Bytes + feeTo_in: [Bytes!] + feeTo_not_in: [Bytes!] + feeTo_contains: Bytes + feeTo_not_contains: Bytes + feeLiquidity: BigDecimal + feeLiquidity_not: BigDecimal + feeLiquidity_gt: BigDecimal + feeLiquidity_lt: BigDecimal + feeLiquidity_gte: BigDecimal + feeLiquidity_lte: BigDecimal + feeLiquidity_in: [BigDecimal!] + feeLiquidity_not_in: [BigDecimal!] + _change_block: BlockChangedFilter + and: [Mint_filter] + or: [Mint_filter] +} + +enum Burn_orderBy { + id + transaction + transaction__id + transaction__gasLimit + transaction__gasPrice + transaction__createdAtBlock + transaction__createdAtTimestamp + timestamp + pair + pair__id + pair__type + pair__swapFee + pair__twapEnabled + pair__name + pair__source + pair__createdAtBlock + pair__createdAtTimestamp + pair__reserve0 + pair__reserve1 + pair__liquidity + pair__liquidityUSD + pair__liquidityNative + pair__trackedLiquidityNative + pair__token0Price + pair__token1Price + pair__volumeNative + pair__volumeUSD + pair__volumeToken0 + pair__volumeToken1 + pair__feesNative + pair__feesUSD + pair__apr + pair__aprUpdatedAtTimestamp + pair__txCount + liquidity + sender + amount0 + amount1 + to + logIndex + amountUSD + complete + feeTo + feeLiquidity +} + +input Burn_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + transaction: String + transaction_not: String + transaction_gt: String + transaction_lt: String + transaction_gte: String + transaction_lte: String + transaction_in: [String!] + transaction_not_in: [String!] + transaction_starts_with: String + transaction_starts_with_nocase: String + transaction_not_starts_with: String + transaction_not_starts_with_nocase: String + transaction_ends_with: String + transaction_ends_with_nocase: String + transaction_not_ends_with: String + transaction_not_ends_with_nocase: String + transaction_contains: String + transaction_not_contains: String + transaction_contains_nocase: String + transaction_not_contains_nocase: String + transaction_: Transaction_filter + timestamp: BigInt + timestamp_not: BigInt + timestamp_gt: BigInt + timestamp_lt: BigInt + timestamp_gte: BigInt + timestamp_lte: BigInt + timestamp_in: [BigInt!] + timestamp_not_in: [BigInt!] + pair: String + pair_not: String + pair_gt: String + pair_lt: String + pair_gte: String + pair_lte: String + pair_in: [String!] + pair_not_in: [String!] + pair_starts_with: String + pair_starts_with_nocase: String + pair_not_starts_with: String + pair_not_starts_with_nocase: String + pair_ends_with: String + pair_ends_with_nocase: String + pair_not_ends_with: String + pair_not_ends_with_nocase: String + pair_contains: String + pair_not_contains: String + pair_contains_nocase: String + pair_not_contains_nocase: String + pair_: Pair_filter + liquidity: BigDecimal + liquidity_not: BigDecimal + liquidity_gt: BigDecimal + liquidity_lt: BigDecimal + liquidity_gte: BigDecimal + liquidity_lte: BigDecimal + liquidity_in: [BigDecimal!] + liquidity_not_in: [BigDecimal!] + sender: String + sender_not: String + sender_gt: String + sender_lt: String + sender_gte: String + sender_lte: String + sender_in: [String!] + sender_not_in: [String!] + sender_starts_with: String + sender_starts_with_nocase: String + sender_not_starts_with: String + sender_not_starts_with_nocase: String + sender_ends_with: String + sender_ends_with_nocase: String + sender_not_ends_with: String + sender_not_ends_with_nocase: String + sender_contains: String + sender_not_contains: String + sender_contains_nocase: String + sender_not_contains_nocase: String + amount0: BigDecimal + amount0_not: BigDecimal + amount0_gt: BigDecimal + amount0_lt: BigDecimal + amount0_gte: BigDecimal + amount0_lte: BigDecimal + amount0_in: [BigDecimal!] + amount0_not_in: [BigDecimal!] + amount1: BigDecimal + amount1_not: BigDecimal + amount1_gt: BigDecimal + amount1_lt: BigDecimal + amount1_gte: BigDecimal + amount1_lte: BigDecimal + amount1_in: [BigDecimal!] + amount1_not_in: [BigDecimal!] + to: String + to_not: String + to_gt: String + to_lt: String + to_gte: String + to_lte: String + to_in: [String!] + to_not_in: [String!] + to_starts_with: String + to_starts_with_nocase: String + to_not_starts_with: String + to_not_starts_with_nocase: String + to_ends_with: String + to_ends_with_nocase: String + to_not_ends_with: String + to_not_ends_with_nocase: String + to_contains: String + to_not_contains: String + to_contains_nocase: String + to_not_contains_nocase: String + logIndex: BigInt + logIndex_not: BigInt + logIndex_gt: BigInt + logIndex_lt: BigInt + logIndex_gte: BigInt + logIndex_lte: BigInt + logIndex_in: [BigInt!] + logIndex_not_in: [BigInt!] + amountUSD: BigDecimal + amountUSD_not: BigDecimal + amountUSD_gt: BigDecimal + amountUSD_lt: BigDecimal + amountUSD_gte: BigDecimal + amountUSD_lte: BigDecimal + amountUSD_in: [BigDecimal!] + amountUSD_not_in: [BigDecimal!] + complete: Boolean + complete_not: Boolean + complete_gt: Boolean + complete_lt: Boolean + complete_gte: Boolean + complete_lte: Boolean + complete_in: [Boolean!] + complete_not_in: [Boolean!] + feeTo: String + feeTo_not: String + feeTo_gt: String + feeTo_lt: String + feeTo_gte: String + feeTo_lte: String + feeTo_in: [String!] + feeTo_not_in: [String!] + feeTo_starts_with: String + feeTo_starts_with_nocase: String + feeTo_not_starts_with: String + feeTo_not_starts_with_nocase: String + feeTo_ends_with: String + feeTo_ends_with_nocase: String + feeTo_not_ends_with: String + feeTo_not_ends_with_nocase: String + feeTo_contains: String + feeTo_not_contains: String + feeTo_contains_nocase: String + feeTo_not_contains_nocase: String + feeLiquidity: BigDecimal + feeLiquidity_not: BigDecimal + feeLiquidity_gt: BigDecimal + feeLiquidity_lt: BigDecimal + feeLiquidity_gte: BigDecimal + feeLiquidity_lte: BigDecimal + feeLiquidity_in: [BigDecimal!] + feeLiquidity_not_in: [BigDecimal!] + _change_block: BlockChangedFilter + and: [Burn_filter] + or: [Burn_filter] +} + +enum Swap_orderBy { + id + transaction + transaction__id + transaction__gasLimit + transaction__gasPrice + transaction__createdAtBlock + transaction__createdAtTimestamp + timestamp + pair + pair__id + pair__type + pair__swapFee + pair__twapEnabled + pair__name + pair__source + pair__createdAtBlock + pair__createdAtTimestamp + pair__reserve0 + pair__reserve1 + pair__liquidity + pair__liquidityUSD + pair__liquidityNative + pair__trackedLiquidityNative + pair__token0Price + pair__token1Price + pair__volumeNative + pair__volumeUSD + pair__volumeToken0 + pair__volumeToken1 + pair__feesNative + pair__feesUSD + pair__apr + pair__aprUpdatedAtTimestamp + pair__txCount + sender + tokenIn + tokenIn__id + tokenIn__symbol + tokenIn__symbolSuccess + tokenIn__name + tokenIn__nameSuccess + tokenIn__decimals + tokenIn__decimalsSuccess + tokenIn__liquidity + tokenIn__liquidityNative + tokenIn__liquidityUSD + tokenIn__volume + tokenIn__volumeNative + tokenIn__volumeUSD + tokenIn__feesNative + tokenIn__feesUSD + tokenIn__txCount + tokenIn__pairCount + tokenIn__whitelistedPairCount + tokenOut + tokenOut__id + tokenOut__symbol + tokenOut__symbolSuccess + tokenOut__name + tokenOut__nameSuccess + tokenOut__decimals + tokenOut__decimalsSuccess + tokenOut__liquidity + tokenOut__liquidityNative + tokenOut__liquidityUSD + tokenOut__volume + tokenOut__volumeNative + tokenOut__volumeUSD + tokenOut__feesNative + tokenOut__feesUSD + tokenOut__txCount + tokenOut__pairCount + tokenOut__whitelistedPairCount + amountIn + amountOut + to + logIndex + amountUSD +} + +input Swap_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + transaction: String + transaction_not: String + transaction_gt: String + transaction_lt: String + transaction_gte: String + transaction_lte: String + transaction_in: [String!] + transaction_not_in: [String!] + transaction_starts_with: String + transaction_starts_with_nocase: String + transaction_not_starts_with: String + transaction_not_starts_with_nocase: String + transaction_ends_with: String + transaction_ends_with_nocase: String + transaction_not_ends_with: String + transaction_not_ends_with_nocase: String + transaction_contains: String + transaction_not_contains: String + transaction_contains_nocase: String + transaction_not_contains_nocase: String + transaction_: Transaction_filter + timestamp: BigInt + timestamp_not: BigInt + timestamp_gt: BigInt + timestamp_lt: BigInt + timestamp_gte: BigInt + timestamp_lte: BigInt + timestamp_in: [BigInt!] + timestamp_not_in: [BigInt!] + pair: String + pair_not: String + pair_gt: String + pair_lt: String + pair_gte: String + pair_lte: String + pair_in: [String!] + pair_not_in: [String!] + pair_starts_with: String + pair_starts_with_nocase: String + pair_not_starts_with: String + pair_not_starts_with_nocase: String + pair_ends_with: String + pair_ends_with_nocase: String + pair_not_ends_with: String + pair_not_ends_with_nocase: String + pair_contains: String + pair_not_contains: String + pair_contains_nocase: String + pair_not_contains_nocase: String + pair_: Pair_filter + sender: String + sender_not: String + sender_gt: String + sender_lt: String + sender_gte: String + sender_lte: String + sender_in: [String!] + sender_not_in: [String!] + sender_starts_with: String + sender_starts_with_nocase: String + sender_not_starts_with: String + sender_not_starts_with_nocase: String + sender_ends_with: String + sender_ends_with_nocase: String + sender_not_ends_with: String + sender_not_ends_with_nocase: String + sender_contains: String + sender_not_contains: String + sender_contains_nocase: String + sender_not_contains_nocase: String + tokenIn: String + tokenIn_not: String + tokenIn_gt: String + tokenIn_lt: String + tokenIn_gte: String + tokenIn_lte: String + tokenIn_in: [String!] + tokenIn_not_in: [String!] + tokenIn_starts_with: String + tokenIn_starts_with_nocase: String + tokenIn_not_starts_with: String + tokenIn_not_starts_with_nocase: String + tokenIn_ends_with: String + tokenIn_ends_with_nocase: String + tokenIn_not_ends_with: String + tokenIn_not_ends_with_nocase: String + tokenIn_contains: String + tokenIn_not_contains: String + tokenIn_contains_nocase: String + tokenIn_not_contains_nocase: String + tokenIn_: Token_filter + tokenOut: String + tokenOut_not: String + tokenOut_gt: String + tokenOut_lt: String + tokenOut_gte: String + tokenOut_lte: String + tokenOut_in: [String!] + tokenOut_not_in: [String!] + tokenOut_starts_with: String + tokenOut_starts_with_nocase: String + tokenOut_not_starts_with: String + tokenOut_not_starts_with_nocase: String + tokenOut_ends_with: String + tokenOut_ends_with_nocase: String + tokenOut_not_ends_with: String + tokenOut_not_ends_with_nocase: String + tokenOut_contains: String + tokenOut_not_contains: String + tokenOut_contains_nocase: String + tokenOut_not_contains_nocase: String + tokenOut_: Token_filter + amountIn: BigDecimal + amountIn_not: BigDecimal + amountIn_gt: BigDecimal + amountIn_lt: BigDecimal + amountIn_gte: BigDecimal + amountIn_lte: BigDecimal + amountIn_in: [BigDecimal!] + amountIn_not_in: [BigDecimal!] + amountOut: BigDecimal + amountOut_not: BigDecimal + amountOut_gt: BigDecimal + amountOut_lt: BigDecimal + amountOut_gte: BigDecimal + amountOut_lte: BigDecimal + amountOut_in: [BigDecimal!] + amountOut_not_in: [BigDecimal!] + to: String + to_not: String + to_gt: String + to_lt: String + to_gte: String + to_lte: String + to_in: [String!] + to_not_in: [String!] + to_starts_with: String + to_starts_with_nocase: String + to_not_starts_with: String + to_not_starts_with_nocase: String + to_ends_with: String + to_ends_with_nocase: String + to_not_ends_with: String + to_not_ends_with_nocase: String + to_contains: String + to_not_contains: String + to_contains_nocase: String + to_not_contains_nocase: String + logIndex: BigInt + logIndex_not: BigInt + logIndex_gt: BigInt + logIndex_lt: BigInt + logIndex_gte: BigInt + logIndex_lte: BigInt + logIndex_in: [BigInt!] + logIndex_not_in: [BigInt!] + amountUSD: BigDecimal + amountUSD_not: BigDecimal + amountUSD_gt: BigDecimal + amountUSD_lt: BigDecimal + amountUSD_gte: BigDecimal + amountUSD_lte: BigDecimal + amountUSD_in: [BigDecimal!] + amountUSD_not_in: [BigDecimal!] + _change_block: BlockChangedFilter + and: [Swap_filter] + or: [Swap_filter] +} + +enum Transaction_orderBy { + id + gasLimit + gasPrice + mints + burns + swaps + createdAtBlock + createdAtTimestamp +} + +input Transaction_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + gasLimit: BigInt + gasLimit_not: BigInt + gasLimit_gt: BigInt + gasLimit_lt: BigInt + gasLimit_gte: BigInt + gasLimit_lte: BigInt + gasLimit_in: [BigInt!] + gasLimit_not_in: [BigInt!] + gasPrice: BigInt + gasPrice_not: BigInt + gasPrice_gt: BigInt + gasPrice_lt: BigInt + gasPrice_gte: BigInt + gasPrice_lte: BigInt + gasPrice_in: [BigInt!] + gasPrice_not_in: [BigInt!] + mints: [String!] + mints_not: [String!] + mints_contains: [String!] + mints_not_contains: [String!] + mints_contains_nocase: [String!] + mints_not_contains_nocase: [String!] + mints_: Mint_filter + burns: [String!] + burns_not: [String!] + burns_contains: [String!] + burns_not_contains: [String!] + burns_contains_nocase: [String!] + burns_not_contains_nocase: [String!] + burns_: Burn_filter + swaps: [String!] + swaps_not: [String!] + swaps_contains: [String!] + swaps_not_contains: [String!] + swaps_contains_nocase: [String!] + swaps_not_contains_nocase: [String!] + swaps_: Swap_filter + createdAtBlock: BigInt + createdAtBlock_not: BigInt + createdAtBlock_gt: BigInt + createdAtBlock_lt: BigInt + createdAtBlock_gte: BigInt + createdAtBlock_lte: BigInt + createdAtBlock_in: [BigInt!] + createdAtBlock_not_in: [BigInt!] + createdAtTimestamp: BigInt + createdAtTimestamp_not: BigInt + createdAtTimestamp_gt: BigInt + createdAtTimestamp_lt: BigInt + createdAtTimestamp_gte: BigInt + createdAtTimestamp_lte: BigInt + createdAtTimestamp_in: [BigInt!] + createdAtTimestamp_not_in: [BigInt!] + _change_block: BlockChangedFilter + and: [Transaction_filter] + or: [Transaction_filter] +} + +enum LiquidityPositionSnapshot_orderBy { + id + liquidityPosition + liquidityPosition__id + liquidityPosition__balance + liquidityPosition__createdAtBlock + liquidityPosition__createdAtTimestamp + timestamp + block + user + user__id + user__lpSnapshotsCount + pair + pair__id + pair__type + pair__swapFee + pair__twapEnabled + pair__name + pair__source + pair__createdAtBlock + pair__createdAtTimestamp + pair__reserve0 + pair__reserve1 + pair__liquidity + pair__liquidityUSD + pair__liquidityNative + pair__trackedLiquidityNative + pair__token0Price + pair__token1Price + pair__volumeNative + pair__volumeUSD + pair__volumeToken0 + pair__volumeToken1 + pair__feesNative + pair__feesUSD + pair__apr + pair__aprUpdatedAtTimestamp + pair__txCount + token0PriceUSD + token1PriceUSD + reserve0 + reserve1 + reserveUSD + liquidityTokenTotalSupply + liquidityTokenBalance +} + +input LiquidityPositionSnapshot_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + liquidityPosition: String + liquidityPosition_not: String + liquidityPosition_gt: String + liquidityPosition_lt: String + liquidityPosition_gte: String + liquidityPosition_lte: String + liquidityPosition_in: [String!] + liquidityPosition_not_in: [String!] + liquidityPosition_starts_with: String + liquidityPosition_starts_with_nocase: String + liquidityPosition_not_starts_with: String + liquidityPosition_not_starts_with_nocase: String + liquidityPosition_ends_with: String + liquidityPosition_ends_with_nocase: String + liquidityPosition_not_ends_with: String + liquidityPosition_not_ends_with_nocase: String + liquidityPosition_contains: String + liquidityPosition_not_contains: String + liquidityPosition_contains_nocase: String + liquidityPosition_not_contains_nocase: String + liquidityPosition_: LiquidityPosition_filter + timestamp: Int + timestamp_not: Int + timestamp_gt: Int + timestamp_lt: Int + timestamp_gte: Int + timestamp_lte: Int + timestamp_in: [Int!] + timestamp_not_in: [Int!] + block: Int + block_not: Int + block_gt: Int + block_lt: Int + block_gte: Int + block_lte: Int + block_in: [Int!] + block_not_in: [Int!] + user: String + user_not: String + user_gt: String + user_lt: String + user_gte: String + user_lte: String + user_in: [String!] + user_not_in: [String!] + user_starts_with: String + user_starts_with_nocase: String + user_not_starts_with: String + user_not_starts_with_nocase: String + user_ends_with: String + user_ends_with_nocase: String + user_not_ends_with: String + user_not_ends_with_nocase: String + user_contains: String + user_not_contains: String + user_contains_nocase: String + user_not_contains_nocase: String + user_: User_filter + pair: String + pair_not: String + pair_gt: String + pair_lt: String + pair_gte: String + pair_lte: String + pair_in: [String!] + pair_not_in: [String!] + pair_starts_with: String + pair_starts_with_nocase: String + pair_not_starts_with: String + pair_not_starts_with_nocase: String + pair_ends_with: String + pair_ends_with_nocase: String + pair_not_ends_with: String + pair_not_ends_with_nocase: String + pair_contains: String + pair_not_contains: String + pair_contains_nocase: String + pair_not_contains_nocase: String + pair_: Pair_filter + token0PriceUSD: BigDecimal + token0PriceUSD_not: BigDecimal + token0PriceUSD_gt: BigDecimal + token0PriceUSD_lt: BigDecimal + token0PriceUSD_gte: BigDecimal + token0PriceUSD_lte: BigDecimal + token0PriceUSD_in: [BigDecimal!] + token0PriceUSD_not_in: [BigDecimal!] + token1PriceUSD: BigDecimal + token1PriceUSD_not: BigDecimal + token1PriceUSD_gt: BigDecimal + token1PriceUSD_lt: BigDecimal + token1PriceUSD_gte: BigDecimal + token1PriceUSD_lte: BigDecimal + token1PriceUSD_in: [BigDecimal!] + token1PriceUSD_not_in: [BigDecimal!] + reserve0: BigInt + reserve0_not: BigInt + reserve0_gt: BigInt + reserve0_lt: BigInt + reserve0_gte: BigInt + reserve0_lte: BigInt + reserve0_in: [BigInt!] + reserve0_not_in: [BigInt!] + reserve1: BigInt + reserve1_not: BigInt + reserve1_gt: BigInt + reserve1_lt: BigInt + reserve1_gte: BigInt + reserve1_lte: BigInt + reserve1_in: [BigInt!] + reserve1_not_in: [BigInt!] + reserveUSD: BigDecimal + reserveUSD_not: BigDecimal + reserveUSD_gt: BigDecimal + reserveUSD_lt: BigDecimal + reserveUSD_gte: BigDecimal + reserveUSD_lte: BigDecimal + reserveUSD_in: [BigDecimal!] + reserveUSD_not_in: [BigDecimal!] + liquidityTokenTotalSupply: BigInt + liquidityTokenTotalSupply_not: BigInt + liquidityTokenTotalSupply_gt: BigInt + liquidityTokenTotalSupply_lt: BigInt + liquidityTokenTotalSupply_gte: BigInt + liquidityTokenTotalSupply_lte: BigInt + liquidityTokenTotalSupply_in: [BigInt!] + liquidityTokenTotalSupply_not_in: [BigInt!] + liquidityTokenBalance: BigInt + liquidityTokenBalance_not: BigInt + liquidityTokenBalance_gt: BigInt + liquidityTokenBalance_lt: BigInt + liquidityTokenBalance_gte: BigInt + liquidityTokenBalance_lte: BigInt + liquidityTokenBalance_in: [BigInt!] + liquidityTokenBalance_not_in: [BigInt!] + _change_block: BlockChangedFilter + and: [LiquidityPositionSnapshot_filter] + or: [LiquidityPositionSnapshot_filter] +} + +enum PairHourSnapshot_orderBy { + id + pair + pair__id + pair__type + pair__swapFee + pair__twapEnabled + pair__name + pair__source + pair__createdAtBlock + pair__createdAtTimestamp + pair__reserve0 + pair__reserve1 + pair__liquidity + pair__liquidityUSD + pair__liquidityNative + pair__trackedLiquidityNative + pair__token0Price + pair__token1Price + pair__volumeNative + pair__volumeUSD + pair__volumeToken0 + pair__volumeToken1 + pair__feesNative + pair__feesUSD + pair__apr + pair__aprUpdatedAtTimestamp + pair__txCount + date + cumulativeVolumeUSD + volumeUSD + volumeNative + volumeToken0 + volumeToken1 + liquidity + liquidityNative + liquidityUSD + feesNative + feesUSD + apr + transactionCount +} + +input PairHourSnapshot_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + pair: String + pair_not: String + pair_gt: String + pair_lt: String + pair_gte: String + pair_lte: String + pair_in: [String!] + pair_not_in: [String!] + pair_starts_with: String + pair_starts_with_nocase: String + pair_not_starts_with: String + pair_not_starts_with_nocase: String + pair_ends_with: String + pair_ends_with_nocase: String + pair_not_ends_with: String + pair_not_ends_with_nocase: String + pair_contains: String + pair_not_contains: String + pair_contains_nocase: String + pair_not_contains_nocase: String + pair_: Pair_filter + date: Int + date_not: Int + date_gt: Int + date_lt: Int + date_gte: Int + date_lte: Int + date_in: [Int!] + date_not_in: [Int!] + cumulativeVolumeUSD: BigDecimal + cumulativeVolumeUSD_not: BigDecimal + cumulativeVolumeUSD_gt: BigDecimal + cumulativeVolumeUSD_lt: BigDecimal + cumulativeVolumeUSD_gte: BigDecimal + cumulativeVolumeUSD_lte: BigDecimal + cumulativeVolumeUSD_in: [BigDecimal!] + cumulativeVolumeUSD_not_in: [BigDecimal!] + volumeUSD: BigDecimal + volumeUSD_not: BigDecimal + volumeUSD_gt: BigDecimal + volumeUSD_lt: BigDecimal + volumeUSD_gte: BigDecimal + volumeUSD_lte: BigDecimal + volumeUSD_in: [BigDecimal!] + volumeUSD_not_in: [BigDecimal!] + volumeNative: BigDecimal + volumeNative_not: BigDecimal + volumeNative_gt: BigDecimal + volumeNative_lt: BigDecimal + volumeNative_gte: BigDecimal + volumeNative_lte: BigDecimal + volumeNative_in: [BigDecimal!] + volumeNative_not_in: [BigDecimal!] + volumeToken0: BigDecimal + volumeToken0_not: BigDecimal + volumeToken0_gt: BigDecimal + volumeToken0_lt: BigDecimal + volumeToken0_gte: BigDecimal + volumeToken0_lte: BigDecimal + volumeToken0_in: [BigDecimal!] + volumeToken0_not_in: [BigDecimal!] + volumeToken1: BigDecimal + volumeToken1_not: BigDecimal + volumeToken1_gt: BigDecimal + volumeToken1_lt: BigDecimal + volumeToken1_gte: BigDecimal + volumeToken1_lte: BigDecimal + volumeToken1_in: [BigDecimal!] + volumeToken1_not_in: [BigDecimal!] + liquidity: BigDecimal + liquidity_not: BigDecimal + liquidity_gt: BigDecimal + liquidity_lt: BigDecimal + liquidity_gte: BigDecimal + liquidity_lte: BigDecimal + liquidity_in: [BigDecimal!] + liquidity_not_in: [BigDecimal!] + liquidityNative: BigDecimal + liquidityNative_not: BigDecimal + liquidityNative_gt: BigDecimal + liquidityNative_lt: BigDecimal + liquidityNative_gte: BigDecimal + liquidityNative_lte: BigDecimal + liquidityNative_in: [BigDecimal!] + liquidityNative_not_in: [BigDecimal!] + liquidityUSD: BigDecimal + liquidityUSD_not: BigDecimal + liquidityUSD_gt: BigDecimal + liquidityUSD_lt: BigDecimal + liquidityUSD_gte: BigDecimal + liquidityUSD_lte: BigDecimal + liquidityUSD_in: [BigDecimal!] + liquidityUSD_not_in: [BigDecimal!] + feesNative: BigDecimal + feesNative_not: BigDecimal + feesNative_gt: BigDecimal + feesNative_lt: BigDecimal + feesNative_gte: BigDecimal + feesNative_lte: BigDecimal + feesNative_in: [BigDecimal!] + feesNative_not_in: [BigDecimal!] + feesUSD: BigDecimal + feesUSD_not: BigDecimal + feesUSD_gt: BigDecimal + feesUSD_lt: BigDecimal + feesUSD_gte: BigDecimal + feesUSD_lte: BigDecimal + feesUSD_in: [BigDecimal!] + feesUSD_not_in: [BigDecimal!] + apr: BigDecimal + apr_not: BigDecimal + apr_gt: BigDecimal + apr_lt: BigDecimal + apr_gte: BigDecimal + apr_lte: BigDecimal + apr_in: [BigDecimal!] + apr_not_in: [BigDecimal!] + transactionCount: BigInt + transactionCount_not: BigInt + transactionCount_gt: BigInt + transactionCount_lt: BigInt + transactionCount_gte: BigInt + transactionCount_lte: BigInt + transactionCount_in: [BigInt!] + transactionCount_not_in: [BigInt!] + _change_block: BlockChangedFilter + and: [PairHourSnapshot_filter] + or: [PairHourSnapshot_filter] +} + +enum PairDaySnapshot_orderBy { + id + pair + pair__id + pair__type + pair__swapFee + pair__twapEnabled + pair__name + pair__source + pair__createdAtBlock + pair__createdAtTimestamp + pair__reserve0 + pair__reserve1 + pair__liquidity + pair__liquidityUSD + pair__liquidityNative + pair__trackedLiquidityNative + pair__token0Price + pair__token1Price + pair__volumeNative + pair__volumeUSD + pair__volumeToken0 + pair__volumeToken1 + pair__feesNative + pair__feesUSD + pair__apr + pair__aprUpdatedAtTimestamp + pair__txCount + date + cumulativeVolumeUSD + volumeUSD + volumeNative + volumeToken0 + volumeToken1 + liquidity + liquidityNative + liquidityUSD + feesNative + feesUSD + apr + transactionCount +} + +input PairDaySnapshot_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + pair: String + pair_not: String + pair_gt: String + pair_lt: String + pair_gte: String + pair_lte: String + pair_in: [String!] + pair_not_in: [String!] + pair_starts_with: String + pair_starts_with_nocase: String + pair_not_starts_with: String + pair_not_starts_with_nocase: String + pair_ends_with: String + pair_ends_with_nocase: String + pair_not_ends_with: String + pair_not_ends_with_nocase: String + pair_contains: String + pair_not_contains: String + pair_contains_nocase: String + pair_not_contains_nocase: String + pair_: Pair_filter + date: Int + date_not: Int + date_gt: Int + date_lt: Int + date_gte: Int + date_lte: Int + date_in: [Int!] + date_not_in: [Int!] + cumulativeVolumeUSD: BigDecimal + cumulativeVolumeUSD_not: BigDecimal + cumulativeVolumeUSD_gt: BigDecimal + cumulativeVolumeUSD_lt: BigDecimal + cumulativeVolumeUSD_gte: BigDecimal + cumulativeVolumeUSD_lte: BigDecimal + cumulativeVolumeUSD_in: [BigDecimal!] + cumulativeVolumeUSD_not_in: [BigDecimal!] + volumeUSD: BigDecimal + volumeUSD_not: BigDecimal + volumeUSD_gt: BigDecimal + volumeUSD_lt: BigDecimal + volumeUSD_gte: BigDecimal + volumeUSD_lte: BigDecimal + volumeUSD_in: [BigDecimal!] + volumeUSD_not_in: [BigDecimal!] + volumeNative: BigDecimal + volumeNative_not: BigDecimal + volumeNative_gt: BigDecimal + volumeNative_lt: BigDecimal + volumeNative_gte: BigDecimal + volumeNative_lte: BigDecimal + volumeNative_in: [BigDecimal!] + volumeNative_not_in: [BigDecimal!] + volumeToken0: BigDecimal + volumeToken0_not: BigDecimal + volumeToken0_gt: BigDecimal + volumeToken0_lt: BigDecimal + volumeToken0_gte: BigDecimal + volumeToken0_lte: BigDecimal + volumeToken0_in: [BigDecimal!] + volumeToken0_not_in: [BigDecimal!] + volumeToken1: BigDecimal + volumeToken1_not: BigDecimal + volumeToken1_gt: BigDecimal + volumeToken1_lt: BigDecimal + volumeToken1_gte: BigDecimal + volumeToken1_lte: BigDecimal + volumeToken1_in: [BigDecimal!] + volumeToken1_not_in: [BigDecimal!] + liquidity: BigDecimal + liquidity_not: BigDecimal + liquidity_gt: BigDecimal + liquidity_lt: BigDecimal + liquidity_gte: BigDecimal + liquidity_lte: BigDecimal + liquidity_in: [BigDecimal!] + liquidity_not_in: [BigDecimal!] + liquidityNative: BigDecimal + liquidityNative_not: BigDecimal + liquidityNative_gt: BigDecimal + liquidityNative_lt: BigDecimal + liquidityNative_gte: BigDecimal + liquidityNative_lte: BigDecimal + liquidityNative_in: [BigDecimal!] + liquidityNative_not_in: [BigDecimal!] + liquidityUSD: BigDecimal + liquidityUSD_not: BigDecimal + liquidityUSD_gt: BigDecimal + liquidityUSD_lt: BigDecimal + liquidityUSD_gte: BigDecimal + liquidityUSD_lte: BigDecimal + liquidityUSD_in: [BigDecimal!] + liquidityUSD_not_in: [BigDecimal!] + feesNative: BigDecimal + feesNative_not: BigDecimal + feesNative_gt: BigDecimal + feesNative_lt: BigDecimal + feesNative_gte: BigDecimal + feesNative_lte: BigDecimal + feesNative_in: [BigDecimal!] + feesNative_not_in: [BigDecimal!] + feesUSD: BigDecimal + feesUSD_not: BigDecimal + feesUSD_gt: BigDecimal + feesUSD_lt: BigDecimal + feesUSD_gte: BigDecimal + feesUSD_lte: BigDecimal + feesUSD_in: [BigDecimal!] + feesUSD_not_in: [BigDecimal!] + apr: BigDecimal + apr_not: BigDecimal + apr_gt: BigDecimal + apr_lt: BigDecimal + apr_gte: BigDecimal + apr_lte: BigDecimal + apr_in: [BigDecimal!] + apr_not_in: [BigDecimal!] + transactionCount: BigInt + transactionCount_not: BigInt + transactionCount_gt: BigInt + transactionCount_lt: BigInt + transactionCount_gte: BigInt + transactionCount_lte: BigInt + transactionCount_in: [BigInt!] + transactionCount_not_in: [BigInt!] + _change_block: BlockChangedFilter + and: [PairDaySnapshot_filter] + or: [PairDaySnapshot_filter] +} + +enum TokenHourSnapshot_orderBy { + id + date + token + token__id + token__symbol + token__symbolSuccess + token__name + token__nameSuccess + token__decimals + token__decimalsSuccess + token__liquidity + token__liquidityNative + token__liquidityUSD + token__volume + token__volumeNative + token__volumeUSD + token__feesNative + token__feesUSD + token__txCount + token__pairCount + token__whitelistedPairCount + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + priceNative + priceUSD + feesNative + feesUSD + transactionCount +} + +input TokenHourSnapshot_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + date: Int + date_not: Int + date_gt: Int + date_lt: Int + date_gte: Int + date_lte: Int + date_in: [Int!] + date_not_in: [Int!] + token: String + token_not: String + token_gt: String + token_lt: String + token_gte: String + token_lte: String + token_in: [String!] + token_not_in: [String!] + token_starts_with: String + token_starts_with_nocase: String + token_not_starts_with: String + token_not_starts_with_nocase: String + token_ends_with: String + token_ends_with_nocase: String + token_not_ends_with: String + token_not_ends_with_nocase: String + token_contains: String + token_not_contains: String + token_contains_nocase: String + token_not_contains_nocase: String + token_: Token_filter + liquidity: BigDecimal + liquidity_not: BigDecimal + liquidity_gt: BigDecimal + liquidity_lt: BigDecimal + liquidity_gte: BigDecimal + liquidity_lte: BigDecimal + liquidity_in: [BigDecimal!] + liquidity_not_in: [BigDecimal!] + liquidityNative: BigDecimal + liquidityNative_not: BigDecimal + liquidityNative_gt: BigDecimal + liquidityNative_lt: BigDecimal + liquidityNative_gte: BigDecimal + liquidityNative_lte: BigDecimal + liquidityNative_in: [BigDecimal!] + liquidityNative_not_in: [BigDecimal!] + liquidityUSD: BigDecimal + liquidityUSD_not: BigDecimal + liquidityUSD_gt: BigDecimal + liquidityUSD_lt: BigDecimal + liquidityUSD_gte: BigDecimal + liquidityUSD_lte: BigDecimal + liquidityUSD_in: [BigDecimal!] + liquidityUSD_not_in: [BigDecimal!] + volume: BigDecimal + volume_not: BigDecimal + volume_gt: BigDecimal + volume_lt: BigDecimal + volume_gte: BigDecimal + volume_lte: BigDecimal + volume_in: [BigDecimal!] + volume_not_in: [BigDecimal!] + volumeNative: BigDecimal + volumeNative_not: BigDecimal + volumeNative_gt: BigDecimal + volumeNative_lt: BigDecimal + volumeNative_gte: BigDecimal + volumeNative_lte: BigDecimal + volumeNative_in: [BigDecimal!] + volumeNative_not_in: [BigDecimal!] + volumeUSD: BigDecimal + volumeUSD_not: BigDecimal + volumeUSD_gt: BigDecimal + volumeUSD_lt: BigDecimal + volumeUSD_gte: BigDecimal + volumeUSD_lte: BigDecimal + volumeUSD_in: [BigDecimal!] + volumeUSD_not_in: [BigDecimal!] + priceNative: BigDecimal + priceNative_not: BigDecimal + priceNative_gt: BigDecimal + priceNative_lt: BigDecimal + priceNative_gte: BigDecimal + priceNative_lte: BigDecimal + priceNative_in: [BigDecimal!] + priceNative_not_in: [BigDecimal!] + priceUSD: BigDecimal + priceUSD_not: BigDecimal + priceUSD_gt: BigDecimal + priceUSD_lt: BigDecimal + priceUSD_gte: BigDecimal + priceUSD_lte: BigDecimal + priceUSD_in: [BigDecimal!] + priceUSD_not_in: [BigDecimal!] + feesNative: BigDecimal + feesNative_not: BigDecimal + feesNative_gt: BigDecimal + feesNative_lt: BigDecimal + feesNative_gte: BigDecimal + feesNative_lte: BigDecimal + feesNative_in: [BigDecimal!] + feesNative_not_in: [BigDecimal!] + feesUSD: BigDecimal + feesUSD_not: BigDecimal + feesUSD_gt: BigDecimal + feesUSD_lt: BigDecimal + feesUSD_gte: BigDecimal + feesUSD_lte: BigDecimal + feesUSD_in: [BigDecimal!] + feesUSD_not_in: [BigDecimal!] + transactionCount: BigInt + transactionCount_not: BigInt + transactionCount_gt: BigInt + transactionCount_lt: BigInt + transactionCount_gte: BigInt + transactionCount_lte: BigInt + transactionCount_in: [BigInt!] + transactionCount_not_in: [BigInt!] + _change_block: BlockChangedFilter + and: [TokenHourSnapshot_filter] + or: [TokenHourSnapshot_filter] +} + +enum TokenDaySnapshot_orderBy { + id + date + token + token__id + token__symbol + token__symbolSuccess + token__name + token__nameSuccess + token__decimals + token__decimalsSuccess + token__liquidity + token__liquidityNative + token__liquidityUSD + token__volume + token__volumeNative + token__volumeUSD + token__feesNative + token__feesUSD + token__txCount + token__pairCount + token__whitelistedPairCount + liquidity + liquidityNative + liquidityUSD + volume + volumeNative + volumeUSD + priceNative + priceUSD + feesNative + feesUSD + transactionCount +} + +input TokenDaySnapshot_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + date: Int + date_not: Int + date_gt: Int + date_lt: Int + date_gte: Int + date_lte: Int + date_in: [Int!] + date_not_in: [Int!] + token: String + token_not: String + token_gt: String + token_lt: String + token_gte: String + token_lte: String + token_in: [String!] + token_not_in: [String!] + token_starts_with: String + token_starts_with_nocase: String + token_not_starts_with: String + token_not_starts_with_nocase: String + token_ends_with: String + token_ends_with_nocase: String + token_not_ends_with: String + token_not_ends_with_nocase: String + token_contains: String + token_not_contains: String + token_contains_nocase: String + token_not_contains_nocase: String + token_: Token_filter + liquidity: BigDecimal + liquidity_not: BigDecimal + liquidity_gt: BigDecimal + liquidity_lt: BigDecimal + liquidity_gte: BigDecimal + liquidity_lte: BigDecimal + liquidity_in: [BigDecimal!] + liquidity_not_in: [BigDecimal!] + liquidityNative: BigDecimal + liquidityNative_not: BigDecimal + liquidityNative_gt: BigDecimal + liquidityNative_lt: BigDecimal + liquidityNative_gte: BigDecimal + liquidityNative_lte: BigDecimal + liquidityNative_in: [BigDecimal!] + liquidityNative_not_in: [BigDecimal!] + liquidityUSD: BigDecimal + liquidityUSD_not: BigDecimal + liquidityUSD_gt: BigDecimal + liquidityUSD_lt: BigDecimal + liquidityUSD_gte: BigDecimal + liquidityUSD_lte: BigDecimal + liquidityUSD_in: [BigDecimal!] + liquidityUSD_not_in: [BigDecimal!] + volume: BigDecimal + volume_not: BigDecimal + volume_gt: BigDecimal + volume_lt: BigDecimal + volume_gte: BigDecimal + volume_lte: BigDecimal + volume_in: [BigDecimal!] + volume_not_in: [BigDecimal!] + volumeNative: BigDecimal + volumeNative_not: BigDecimal + volumeNative_gt: BigDecimal + volumeNative_lt: BigDecimal + volumeNative_gte: BigDecimal + volumeNative_lte: BigDecimal + volumeNative_in: [BigDecimal!] + volumeNative_not_in: [BigDecimal!] + volumeUSD: BigDecimal + volumeUSD_not: BigDecimal + volumeUSD_gt: BigDecimal + volumeUSD_lt: BigDecimal + volumeUSD_gte: BigDecimal + volumeUSD_lte: BigDecimal + volumeUSD_in: [BigDecimal!] + volumeUSD_not_in: [BigDecimal!] + priceNative: BigDecimal + priceNative_not: BigDecimal + priceNative_gt: BigDecimal + priceNative_lt: BigDecimal + priceNative_gte: BigDecimal + priceNative_lte: BigDecimal + priceNative_in: [BigDecimal!] + priceNative_not_in: [BigDecimal!] + priceUSD: BigDecimal + priceUSD_not: BigDecimal + priceUSD_gt: BigDecimal + priceUSD_lt: BigDecimal + priceUSD_gte: BigDecimal + priceUSD_lte: BigDecimal + priceUSD_in: [BigDecimal!] + priceUSD_not_in: [BigDecimal!] + feesNative: BigDecimal + feesNative_not: BigDecimal + feesNative_gt: BigDecimal + feesNative_lt: BigDecimal + feesNative_gte: BigDecimal + feesNative_lte: BigDecimal + feesNative_in: [BigDecimal!] + feesNative_not_in: [BigDecimal!] + feesUSD: BigDecimal + feesUSD_not: BigDecimal + feesUSD_gt: BigDecimal + feesUSD_lt: BigDecimal + feesUSD_gte: BigDecimal + feesUSD_lte: BigDecimal + feesUSD_in: [BigDecimal!] + feesUSD_not_in: [BigDecimal!] + transactionCount: BigInt + transactionCount_not: BigInt + transactionCount_gt: BigInt + transactionCount_lt: BigInt + transactionCount_gte: BigInt + transactionCount_lte: BigInt + transactionCount_in: [BigInt!] + transactionCount_not_in: [BigInt!] + _change_block: BlockChangedFilter + and: [TokenDaySnapshot_filter] + or: [TokenDaySnapshot_filter] +} + +enum FactoryHourSnapshot_orderBy { + id + factory + factory__id + factory__type + factory__volumeUSD + factory__volumeNative + factory__liquidityUSD + factory__liquidityNative + factory__feesUSD + factory__feesNative + factory__pairCount + factory__transactionCount + factory__tokenCount + factory__userCount + date + volumeUSD + volumeNative + liquidityNative + liquidityUSD + feesNative + feesUSD + transactionCount +} + +input FactoryHourSnapshot_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + factory: String + factory_not: String + factory_gt: String + factory_lt: String + factory_gte: String + factory_lte: String + factory_in: [String!] + factory_not_in: [String!] + factory_starts_with: String + factory_starts_with_nocase: String + factory_not_starts_with: String + factory_not_starts_with_nocase: String + factory_ends_with: String + factory_ends_with_nocase: String + factory_not_ends_with: String + factory_not_ends_with_nocase: String + factory_contains: String + factory_not_contains: String + factory_contains_nocase: String + factory_not_contains_nocase: String + factory_: Factory_filter + date: Int + date_not: Int + date_gt: Int + date_lt: Int + date_gte: Int + date_lte: Int + date_in: [Int!] + date_not_in: [Int!] + volumeUSD: BigDecimal + volumeUSD_not: BigDecimal + volumeUSD_gt: BigDecimal + volumeUSD_lt: BigDecimal + volumeUSD_gte: BigDecimal + volumeUSD_lte: BigDecimal + volumeUSD_in: [BigDecimal!] + volumeUSD_not_in: [BigDecimal!] + volumeNative: BigDecimal + volumeNative_not: BigDecimal + volumeNative_gt: BigDecimal + volumeNative_lt: BigDecimal + volumeNative_gte: BigDecimal + volumeNative_lte: BigDecimal + volumeNative_in: [BigDecimal!] + volumeNative_not_in: [BigDecimal!] + liquidityNative: BigDecimal + liquidityNative_not: BigDecimal + liquidityNative_gt: BigDecimal + liquidityNative_lt: BigDecimal + liquidityNative_gte: BigDecimal + liquidityNative_lte: BigDecimal + liquidityNative_in: [BigDecimal!] + liquidityNative_not_in: [BigDecimal!] + liquidityUSD: BigDecimal + liquidityUSD_not: BigDecimal + liquidityUSD_gt: BigDecimal + liquidityUSD_lt: BigDecimal + liquidityUSD_gte: BigDecimal + liquidityUSD_lte: BigDecimal + liquidityUSD_in: [BigDecimal!] + liquidityUSD_not_in: [BigDecimal!] + feesNative: BigDecimal + feesNative_not: BigDecimal + feesNative_gt: BigDecimal + feesNative_lt: BigDecimal + feesNative_gte: BigDecimal + feesNative_lte: BigDecimal + feesNative_in: [BigDecimal!] + feesNative_not_in: [BigDecimal!] + feesUSD: BigDecimal + feesUSD_not: BigDecimal + feesUSD_gt: BigDecimal + feesUSD_lt: BigDecimal + feesUSD_gte: BigDecimal + feesUSD_lte: BigDecimal + feesUSD_in: [BigDecimal!] + feesUSD_not_in: [BigDecimal!] + transactionCount: BigInt + transactionCount_not: BigInt + transactionCount_gt: BigInt + transactionCount_lt: BigInt + transactionCount_gte: BigInt + transactionCount_lte: BigInt + transactionCount_in: [BigInt!] + transactionCount_not_in: [BigInt!] + _change_block: BlockChangedFilter + and: [FactoryHourSnapshot_filter] + or: [FactoryHourSnapshot_filter] +} + +enum FactoryDaySnapshot_orderBy { + id + factory + factory__id + factory__type + factory__volumeUSD + factory__volumeNative + factory__liquidityUSD + factory__liquidityNative + factory__feesUSD + factory__feesNative + factory__pairCount + factory__transactionCount + factory__tokenCount + factory__userCount + date + volumeUSD + volumeNative + liquidityNative + liquidityUSD + feesNative + feesUSD + transactionCount +} + +input FactoryDaySnapshot_filter { + id: ID + id_not: ID + id_gt: ID + id_lt: ID + id_gte: ID + id_lte: ID + id_in: [ID!] + id_not_in: [ID!] + factory: String + factory_not: String + factory_gt: String + factory_lt: String + factory_gte: String + factory_lte: String + factory_in: [String!] + factory_not_in: [String!] + factory_starts_with: String + factory_starts_with_nocase: String + factory_not_starts_with: String + factory_not_starts_with_nocase: String + factory_ends_with: String + factory_ends_with_nocase: String + factory_not_ends_with: String + factory_not_ends_with_nocase: String + factory_contains: String + factory_not_contains: String + factory_contains_nocase: String + factory_not_contains_nocase: String + factory_: Factory_filter + date: Int + date_not: Int + date_gt: Int + date_lt: Int + date_gte: Int + date_lte: Int + date_in: [Int!] + date_not_in: [Int!] + volumeUSD: BigDecimal + volumeUSD_not: BigDecimal + volumeUSD_gt: BigDecimal + volumeUSD_lt: BigDecimal + volumeUSD_gte: BigDecimal + volumeUSD_lte: BigDecimal + volumeUSD_in: [BigDecimal!] + volumeUSD_not_in: [BigDecimal!] + volumeNative: BigDecimal + volumeNative_not: BigDecimal + volumeNative_gt: BigDecimal + volumeNative_lt: BigDecimal + volumeNative_gte: BigDecimal + volumeNative_lte: BigDecimal + volumeNative_in: [BigDecimal!] + volumeNative_not_in: [BigDecimal!] + liquidityNative: BigDecimal + liquidityNative_not: BigDecimal + liquidityNative_gt: BigDecimal + liquidityNative_lt: BigDecimal + liquidityNative_gte: BigDecimal + liquidityNative_lte: BigDecimal + liquidityNative_in: [BigDecimal!] + liquidityNative_not_in: [BigDecimal!] + liquidityUSD: BigDecimal + liquidityUSD_not: BigDecimal + liquidityUSD_gt: BigDecimal + liquidityUSD_lt: BigDecimal + liquidityUSD_gte: BigDecimal + liquidityUSD_lte: BigDecimal + liquidityUSD_in: [BigDecimal!] + liquidityUSD_not_in: [BigDecimal!] + feesNative: BigDecimal + feesNative_not: BigDecimal + feesNative_gt: BigDecimal + feesNative_lt: BigDecimal + feesNative_gte: BigDecimal + feesNative_lte: BigDecimal + feesNative_in: [BigDecimal!] + feesNative_not_in: [BigDecimal!] + feesUSD: BigDecimal + feesUSD_not: BigDecimal + feesUSD_gt: BigDecimal + feesUSD_lt: BigDecimal + feesUSD_gte: BigDecimal + feesUSD_lte: BigDecimal + feesUSD_in: [BigDecimal!] + feesUSD_not_in: [BigDecimal!] + transactionCount: BigInt + transactionCount_not: BigInt + transactionCount_gt: BigInt + transactionCount_lt: BigInt + transactionCount_gte: BigInt + transactionCount_lte: BigInt + transactionCount_in: [BigInt!] + transactionCount_not_in: [BigInt!] + _change_block: BlockChangedFilter + and: [FactoryDaySnapshot_filter] + or: [FactoryDaySnapshot_filter] +} + +type _MetaBlock_ { + hash: Bytes + number: Int! + timestamp: Int +} + +type _Meta_ { + block: _MetaBlock_! + deployment: String! + hasIndexingErrors: Boolean! +} + +type ResultState { + block: _Block_! + contractAddress: String! + cid: String! + kind: String! + data: String! +} + +type SyncStatus { + latestIndexedBlockHash: String! + latestIndexedBlockNumber: Int! + latestCanonicalBlockHash: String! + latestCanonicalBlockNumber: Int! + initialIndexedBlockHash: String! + initialIndexedBlockNumber: Int! + latestProcessedBlockHash: String! + latestProcessedBlockNumber: Int! +} + +type Query { + events(blockHash: String!, contractAddress: String!, name: String): [ResultEvent!] + eventsInRange(fromBlockNumber: Int!, toBlockNumber: Int!): [ResultEvent!] + factory(id: ID!, block: Block_height): Factory + factories(block: Block_height, where: Factory_filter, orderBy: Factory_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [Factory!]! + bundle(id: ID!, block: Block_height): Bundle + bundles(block: Block_height, where: Bundle_filter, orderBy: Bundle_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [Bundle!]! + token(id: ID!, block: Block_height): Token + tokens(block: Block_height, where: Token_filter, orderBy: Token_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [Token!]! + tokenPrice(id: ID!, block: Block_height): TokenPrice + tokenPrices(block: Block_height, where: TokenPrice_filter, orderBy: TokenPrice_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [TokenPrice!]! + _TokenPair(id: ID!, block: Block_height): _TokenPair + _TokenPairs(block: Block_height, where: _TokenPair_filter, orderBy: _TokenPair_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [_TokenPair!]! + _WhitelistedTokenPair(id: ID!, block: Block_height): _WhitelistedTokenPair + _WhitelistedTokenPairs(block: Block_height, where: _WhitelistedTokenPair_filter, orderBy: _WhitelistedTokenPair_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [_WhitelistedTokenPair!]! + pair(id: ID!, block: Block_height): Pair + pairs(block: Block_height, where: Pair_filter, orderBy: Pair_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [Pair!]! + user(id: ID!, block: Block_height): User + users(block: Block_height, where: User_filter, orderBy: User_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [User!]! + liquidityPosition(id: ID!, block: Block_height): LiquidityPosition + liquidityPositions(block: Block_height, where: LiquidityPosition_filter, orderBy: LiquidityPosition_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [LiquidityPosition!]! + mint(id: ID!, block: Block_height): Mint + mints(block: Block_height, where: Mint_filter, orderBy: Mint_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [Mint!]! + burn(id: ID!, block: Block_height): Burn + burns(block: Block_height, where: Burn_filter, orderBy: Burn_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [Burn!]! + swap(id: ID!, block: Block_height): Swap + swaps(block: Block_height, where: Swap_filter, orderBy: Swap_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [Swap!]! + transaction(id: ID!, block: Block_height): Transaction + transactions(block: Block_height, where: Transaction_filter, orderBy: Transaction_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [Transaction!]! + liquidityPositionSnapshot(id: ID!, block: Block_height): LiquidityPositionSnapshot + liquidityPositionSnapshots(block: Block_height, where: LiquidityPositionSnapshot_filter, orderBy: LiquidityPositionSnapshot_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [LiquidityPositionSnapshot!]! + pairHourSnapshot(id: ID!, block: Block_height): PairHourSnapshot + pairHourSnapshots(block: Block_height, where: PairHourSnapshot_filter, orderBy: PairHourSnapshot_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [PairHourSnapshot!]! + pairDaySnapshot(id: ID!, block: Block_height): PairDaySnapshot + pairDaySnapshots(block: Block_height, where: PairDaySnapshot_filter, orderBy: PairDaySnapshot_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [PairDaySnapshot!]! + tokenHourSnapshot(id: ID!, block: Block_height): TokenHourSnapshot + tokenHourSnapshots(block: Block_height, where: TokenHourSnapshot_filter, orderBy: TokenHourSnapshot_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [TokenHourSnapshot!]! + tokenDaySnapshot(id: ID!, block: Block_height): TokenDaySnapshot + tokenDaySnapshots(block: Block_height, where: TokenDaySnapshot_filter, orderBy: TokenDaySnapshot_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [TokenDaySnapshot!]! + factoryHourSnapshot(id: ID!, block: Block_height): FactoryHourSnapshot + factoryHourSnapshots(block: Block_height, where: FactoryHourSnapshot_filter, orderBy: FactoryHourSnapshot_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [FactoryHourSnapshot!]! + factoryDaySnapshot(id: ID!, block: Block_height): FactoryDaySnapshot + factoryDaySnapshots(block: Block_height, where: FactoryDaySnapshot_filter, orderBy: FactoryDaySnapshot_orderBy, orderDirection: OrderDirection, first: Int = 100, skip: Int = 0): [FactoryDaySnapshot!]! + _meta(block: Block_height): _Meta_ + getStateByCID(cid: String!): ResultState + getState(blockHash: String!, contractAddress: String!, kind: String): ResultState + getSyncStatus: SyncStatus +} + +type Factory { + """ Contract address """ + id: ID! + + """ Factory type """ + type: PairType! + + """ Volume USD """ + volumeUSD: BigDecimal! + + """ Volume Native """ + volumeNative: BigDecimal! + + """ Liquidity USD """ + liquidityUSD: BigDecimal! + + """ Liquidity NATIVE """ + liquidityNative: BigDecimal! + + """ Fees USD """ + feesUSD: BigDecimal! + + """ Fees NATIVE """ + feesNative: BigDecimal! + + """ Pair count """ + pairCount: BigInt! + + """ Transaction count """ + transactionCount: BigInt! + + """ Token count """ + tokenCount: BigInt! + + """ User count """ + userCount: BigInt! +} + +""" Bundle - should only ever be one created""" +type Bundle { + """ hardcoded to '1'""" + id: ID! + + """ Price of native """ + nativePrice: BigDecimal! +} + +type Token { + """ Token address """ + id: ID! + + """ Token Price """ + price: TokenPrice! + + """ Symbol of the token """ + symbol: String! + + """ if symbol was successfully retrieved """ + symbolSuccess: Boolean! + + """ Name of the token """ + name: String! + + """ if name was successfully retrieved """ + nameSuccess: Boolean! + + """ Decimals of the token """ + decimals: BigInt! + + """ if decimals were successfully retrieved """ + decimalsSuccess: Boolean! + + """ Liquidity """ + liquidity: BigInt! + + """ Liquidity in native """ + liquidityNative: BigDecimal! + + """ Liquidity in USD """ + liquidityUSD: BigDecimal! + + """ Volume """ + volume: BigDecimal! + + """ Volume in native """ + volumeNative: BigDecimal! + + """ Volume in USD """ + volumeUSD: BigDecimal! + + """ Fee in USD """ + feesNative: BigDecimal! + + """ Volume in USD """ + feesUSD: BigDecimal! + + """ Count of all the transactions """ + txCount: BigInt! + + """ Count of all the pairs """ + pairCount: BigInt! + + """ Count of all the whitelisted pairs """ + whitelistedPairCount: BigInt! + + """ All pairs where this token is involved in """ + pairs: [_TokenPair!]! + + """ All whitelisted pairs where this token is involved in """ + whitelistedPairs: [_WhitelistedTokenPair!]! +} + +type TokenPrice { + """ same as token entity id, address of token """ + id: ID! + + """ Token """ + token: Token! + + """ derived native, this is useful for calculating price. (derivedNative * bundle.nativePrice = USD price) + """ + derivedNative: BigDecimal! + + """ price in USD. NOTE: this will not always be up to date, it only updates when onSync event is emitted, bundle.nativePrice could have changed. + """ + lastUsdPrice: BigDecimal! + + """ Which token this price is based on """ + pricedOffToken: Token + + """ Which pair this price is based on """ + pricedOffPair: Pair +} + +type Pair { + """ Pair address (contract address) """ + id: ID! + + """ Pair type """ + type: PairType! + + """ Swap fee """ + swapFee: BigInt! + + """ TWAP - time weighted average price """ + twapEnabled: Boolean! + + """ name of the pair, this combines symbol of both tokens, e.g. WETH/SUSHI + """ + name: String! + + """ First Token """ + token0: Token! + + """ Second Token """ + token1: Token! + + """ Which source this pair comes from, in this case it will always be 'LEGACY' + """ + source: String! + + """ Which block this pair was created on """ + createdAtBlock: BigInt! + + """ When this pair was created """ + createdAtTimestamp: BigInt! + + """ Liquidity of first token """ + reserve0: BigInt! + + """ Liquidity of second token """ + reserve1: BigInt! + + """ Liquidity, Total supply of all LP in this pool """ + liquidity: BigInt! + + """ USD liquidity""" + liquidityUSD: BigDecimal! + + """ Native Liquidity """ + liquidityNative: BigDecimal! + + """ Tracked Liquidity native """ + trackedLiquidityNative: BigDecimal! + + """ Price of the first token in this pair, not to be confused with TokenPrice entity + """ + token0Price: BigDecimal! + + """ Price of the second token in this pair, not to be confused with TokenPrice entity + """ + token1Price: BigDecimal! + volumeNative: BigDecimal! + volumeUSD: BigDecimal! + volumeToken0: BigDecimal! + volumeToken1: BigDecimal! + + """ Fee in Native """ + feesNative: BigDecimal! + + """ Fee in USD """ + feesUSD: BigDecimal! + + """ APR """ + apr: BigDecimal! + + """ When APR was last updated """ + aprUpdatedAtTimestamp: BigInt! + + """ Transaction count """ + txCount: BigInt! + + """ Liquidity Positions """ + liquidityPositions: [LiquidityPosition!]! + + """ Liquidity position snapshots """ + liquidityPositionSnapshots: [LiquidityPositionSnapshot!]! + + """ Pair Hour Snapshot """ + hourSnapshots: [PairHourSnapshot!]! + + """ Pair Day Snapshot """ + daySnapshots: [PairDaySnapshot!]! +} + +type LiquidityPosition { + """pair.id:user.id""" + id: ID! + pair: Pair! + user: User! + balance: BigInt! + createdAtBlock: BigInt! + createdAtTimestamp: BigInt! +} + +type User { + id: ID! + lpSnapshotsCount: BigInt! + liquidityPositions: [LiquidityPosition!] +} + +type LiquidityPositionSnapshot { + """ {lp.id}-{timestamp} """ + id: ID! + liquidityPosition: LiquidityPosition! + + """ saved for fast historical lookups """ + timestamp: Int! + + """ saved for fast historical lookups """ + block: Int! + + """ reference to user """ + user: User! + + """ reference to pair """ + pair: Pair! + + """ snapshot of token0 price """ + token0PriceUSD: BigDecimal! + + """ snapshot of token1 price """ + token1PriceUSD: BigDecimal! + + """ snapshot of pair token0 reserves """ + reserve0: BigInt! + + """ snapshot of pair token1 reserves """ + reserve1: BigInt! + + """ snapshot of pair reserves in USD """ + reserveUSD: BigDecimal! + + """ snapshot of pool token supply """ + liquidityTokenTotalSupply: BigInt! + + """ snapshot of users pool token balance """ + liquidityTokenBalance: BigInt! +} + +type PairHourSnapshot { + """ {pairId}-hour-{timestamp} """ + id: ID! + pair: Pair! + date: Int! + + """ Used to calculate apr """ + cumulativeVolumeUSD: BigDecimal! + volumeUSD: BigDecimal! + volumeNative: BigDecimal! + volumeToken0: BigDecimal! + volumeToken1: BigDecimal! + liquidity: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + apr: BigDecimal! + transactionCount: BigInt! +} + +type PairDaySnapshot { + """ {pairId}-day-{timestamp} """ + id: ID! + pair: Pair! + date: Int! + + """ Used to calculate apr """ + cumulativeVolumeUSD: BigDecimal! + volumeUSD: BigDecimal! + volumeNative: BigDecimal! + volumeToken0: BigDecimal! + volumeToken1: BigDecimal! + liquidity: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + apr: BigDecimal! + transactionCount: BigInt! +} + +type _TokenPair { + """ id is created by combining token.id and count, e.g. 0x00x00:1 """ + id: ID! + + """ Pair """ + pair: Pair! + + """ Token """ + token: Token! +} + +type _WhitelistedTokenPair { + """ id is created by combining token.id and count, e.g. 0x00x00:1 """ + id: ID! + + """ Pair """ + pair: Pair! + + """ Token """ + token: Token! +} + +type Mint { + """ transaction.id:transaction.mints.length """ + id: ID! + transaction: Transaction! + timestamp: BigInt! + pair: Pair! + to: String! + liquidity: BigDecimal! + sender: Bytes + amount0: BigDecimal + amount1: BigDecimal + logIndex: BigInt + amountUSD: BigDecimal + feeTo: Bytes + feeLiquidity: BigDecimal +} + +type Transaction { + """ Tx hash """ + id: ID! + gasLimit: BigInt! + gasPrice: BigInt! + mints: [Mint!]! + burns: [Burn!]! + swaps: [Swap!]! + createdAtBlock: BigInt! + createdAtTimestamp: BigInt! +} + +type Burn { + """ transaction.id:transaction.burns.length """ + id: ID! + transaction: Transaction! + timestamp: BigInt! + pair: Pair! + liquidity: BigDecimal! + sender: String + amount0: BigDecimal + amount1: BigDecimal + to: String + logIndex: BigInt + amountUSD: BigDecimal + complete: Boolean! + feeTo: String + feeLiquidity: BigDecimal +} + +type Swap { + id: ID! + transaction: Transaction! + timestamp: BigInt! + pair: Pair! + sender: String! + tokenIn: Token! + tokenOut: Token! + amountIn: BigDecimal! + amountOut: BigDecimal! + to: String! + logIndex: BigInt + amountUSD: BigDecimal! +} + +type TokenHourSnapshot { + """ {tokenId}-hour-{timestamp} """ + id: ID! + date: Int! + token: Token! + liquidity: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + volume: BigDecimal! + volumeNative: BigDecimal! + volumeUSD: BigDecimal! + priceNative: BigDecimal! + priceUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + transactionCount: BigInt! +} + +type TokenDaySnapshot { + """ {tokenId}-day-{timestamp} """ + id: ID! + date: Int! + token: Token! + liquidity: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + volume: BigDecimal! + volumeNative: BigDecimal! + volumeUSD: BigDecimal! + priceNative: BigDecimal! + priceUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + transactionCount: BigInt! +} + +type FactoryHourSnapshot { + """ {factoryId}-hour-{timestamp} """ + id: ID! + factory: Factory! + date: Int! + volumeUSD: BigDecimal! + volumeNative: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + transactionCount: BigInt! +} + +type FactoryDaySnapshot { + """ {factoryId}-day-{timestamp} """ + id: ID! + factory: Factory! + date: Int! + volumeUSD: BigDecimal! + volumeNative: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + transactionCount: BigInt! +} + +type Mutation { + watchContract(address: String!, kind: String!, checkpoint: Boolean!, startingBlock: Int): Boolean! +} + +type Subscription { + onEvent: ResultEvent! +} diff --git a/packages/sushiswap-watcher/src/server.ts b/packages/sushiswap-watcher/src/server.ts new file mode 100644 index 0000000..679134f --- /dev/null +++ b/packages/sushiswap-watcher/src/server.ts @@ -0,0 +1,43 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +import fs from 'fs'; +import path from 'path'; +import 'reflect-metadata'; +import debug from 'debug'; + +import { ServerCmd } from '@cerc-io/cli'; +import { getGraphDbAndWatcher } from '@cerc-io/graph-node'; + +import { createResolvers } from './resolvers'; +import { Indexer } from './indexer'; +import { Database, ENTITY_QUERY_TYPE_MAP, ENTITY_TO_LATEST_ENTITY_MAP } from './database'; + +const log = debug('vulcanize:server'); + +export const main = async (): Promise => { + const serverCmd = new ServerCmd(); + await serverCmd.init(Database); + + const { graphWatcher } = await getGraphDbAndWatcher( + serverCmd.config.server, + serverCmd.clients.ethClient, + serverCmd.ethProvider, + serverCmd.database.baseDatabase, + ENTITY_QUERY_TYPE_MAP, + ENTITY_TO_LATEST_ENTITY_MAP + ); + + await serverCmd.initIndexer(Indexer, graphWatcher); + + const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString(); + + return serverCmd.exec(createResolvers, typeDefs); +}; + +main().then(() => { + log('Starting server...'); +}).catch(err => { + log(err); +}); diff --git a/packages/sushiswap-watcher/src/types.ts b/packages/sushiswap-watcher/src/types.ts new file mode 100644 index 0000000..6fcd13e --- /dev/null +++ b/packages/sushiswap-watcher/src/types.ts @@ -0,0 +1,7 @@ +// +// Copyright 2021 Vulcanize, Inc. +// + +export enum PairType { + CONSTANT_PRODUCT_POOL = 'CONSTANT_PRODUCT_POOL', +} diff --git a/packages/sushiswap-watcher/subgraph-build/Factory/Factory.wasm b/packages/sushiswap-watcher/subgraph-build/Factory/Factory.wasm new file mode 100644 index 0000000000000000000000000000000000000000..1e1b5f46270cdea979c6f5fdcd27303a8da564ad GIT binary patch literal 216887 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 literal 0 HcmV?d00001 diff --git a/packages/sushiswap-watcher/subgraph-build/schema.graphql b/packages/sushiswap-watcher/subgraph-build/schema.graphql new file mode 100644 index 0000000..79847c9 --- /dev/null +++ b/packages/sushiswap-watcher/subgraph-build/schema.graphql @@ -0,0 +1,421 @@ +enum PairType { + CONSTANT_PRODUCT_POOL +} + +type Factory @entity { + " Contract address " + id: ID! + " Factory type " + type: PairType! + " Volume USD " + volumeUSD: BigDecimal! + " Volume Native " + volumeNative: BigDecimal! + " Liquidity USD " + liquidityUSD: BigDecimal! + " Liquidity NATIVE " + liquidityNative: BigDecimal! + " Fees USD " + feesUSD: BigDecimal! + " Fees NATIVE " + feesNative: BigDecimal! + " Pair count " + pairCount: BigInt! + " Transaction count " + transactionCount: BigInt! + " Token count " + tokenCount: BigInt! + " User count " + userCount: BigInt! +} + +" Bundle - should only ever be one created" +type Bundle @entity { + " hardcoded to '1'" + id: ID! + " Price of native " + nativePrice: BigDecimal! +} + +type Token @entity { + " Token address " + id: ID! + " Token Price " + price: TokenPrice! + " Symbol of the token " + symbol: String! + " if symbol was successfully retrieved " + symbolSuccess: Boolean! + " Name of the token " + name: String! + " if name was successfully retrieved " + nameSuccess: Boolean! + " Decimals of the token " + decimals: BigInt! + " if decimals were successfully retrieved " + decimalsSuccess: Boolean! + + " Liquidity " + liquidity: BigInt! + " Liquidity in native " + liquidityNative: BigDecimal! + " Liquidity in USD " + liquidityUSD: BigDecimal! + " Volume " + volume: BigDecimal! + " Volume in native " + volumeNative: BigDecimal! + " Volume in USD " + volumeUSD: BigDecimal! + " Fee in USD " + feesNative: BigDecimal! + " Volume in USD " + feesUSD: BigDecimal! + " Count of all the transactions " + txCount: BigInt! + " Count of all the pairs " + pairCount: BigInt! + " Count of all the whitelisted pairs " + whitelistedPairCount: BigInt! + " All pairs where this token is involved in " + pairs: [_TokenPair!]! @derivedFrom(field: "token") + " All whitelisted pairs where this token is involved in " + whitelistedPairs: [_WhitelistedTokenPair!]! @derivedFrom(field: "token") +} + +type TokenPrice @entity { + " same as token entity id, address of token " + id: ID! + " Token " + token: Token! + " derived native, this is useful for calculating price. (derivedNative * bundle.nativePrice = USD price) " + derivedNative: BigDecimal! + " price in USD. NOTE: this will not always be up to date, it only updates when onSync event is emitted, bundle.nativePrice could have changed. " + lastUsdPrice: BigDecimal! + " Which token this price is based on " + pricedOffToken: Token + " Which pair this price is based on " + pricedOffPair: Pair +} + +type _TokenPair @entity(immutable: true) { + " id is created by combining token.id and count, e.g. 0x00x00:1 " + id: ID! + " Pair " + pair: Pair! + " Token " + token: Token! +} + +type _WhitelistedTokenPair @entity(immutable: true) { + " id is created by combining token.id and count, e.g. 0x00x00:1 " + id: ID! + " Pair " + pair: Pair! + " Token " + token: Token! +} + +type Pair @entity { + " Pair address (contract address) " + id: ID! + " Pair type " + type: PairType! + " Swap fee " + swapFee: BigInt! + " TWAP - time weighted average price " + twapEnabled: Boolean! + " name of the pair, this combines symbol of both tokens, e.g. WETH/SUSHI " + name: String! + " First Token " + token0: Token! + " Second Token " + token1: Token! + " Which source this pair comes from, in this case it will always be 'LEGACY' " + source: String! + " Which block this pair was created on " + createdAtBlock: BigInt! + " When this pair was created " + createdAtTimestamp: BigInt! + + " Liquidity of first token " + reserve0: BigInt! + " Liquidity of second token " + reserve1: BigInt! + " Liquidity, Total supply of all LP in this pool " + liquidity: BigInt! + " USD liquidity" + liquidityUSD: BigDecimal! + " Native Liquidity " + liquidityNative: BigDecimal! + " Tracked Liquidity native " + trackedLiquidityNative: BigDecimal! + " Price of the first token in this pair, not to be confused with TokenPrice entity " + token0Price: BigDecimal! + " Price of the second token in this pair, not to be confused with TokenPrice entity " + token1Price: BigDecimal! + # volume in Native + volumeNative: BigDecimal! + # volume in USD + volumeUSD: BigDecimal! + # volume token0 + volumeToken0: BigDecimal! + # volume token1 + volumeToken1: BigDecimal! + " Fee in Native " + feesNative: BigDecimal! + " Fee in USD " + feesUSD: BigDecimal! + " APR " + apr: BigDecimal! + " When APR was last updated " + aprUpdatedAtTimestamp: BigInt! + " Transaction count " + txCount: BigInt! + + " Liquidity Positions " + liquidityPositions: [LiquidityPosition!]! @derivedFrom(field: "pair") + " Liquidity position snapshots " + liquidityPositionSnapshots: [LiquidityPositionSnapshot!]! @derivedFrom(field: "pair") + " Pair Hour Snapshot " + hourSnapshots: [PairHourSnapshot!]! @derivedFrom(field: "pair") + " Pair Day Snapshot " + daySnapshots: [PairDaySnapshot!]! @derivedFrom(field: "pair") +} + + +type User @entity { + id: ID! + lpSnapshotsCount: BigInt! + liquidityPositions: [LiquidityPosition!] @derivedFrom(field: "user") +} + +type LiquidityPosition @entity { + "pair.id:user.id" + id: ID! + pair: Pair! + user: User! + balance: BigInt! + createdAtBlock: BigInt! + createdAtTimestamp: BigInt! +} + +type Mint @entity { + " transaction.id:transaction.mints.length " + id: ID! + transaction: Transaction! + timestamp: BigInt! # need this to pull recent txns for specific token or pair + pair: Pair! + + # populated from the primary Transfer event + to: String! + liquidity: BigDecimal! + + # populated from the Mint event + sender: Bytes + amount0: BigDecimal + amount1: BigDecimal + logIndex: BigInt + # derived amount based on available prices of tokens + amountUSD: BigDecimal + + # optional fee fields, if a Transfer event is fired in _mintFee + feeTo: Bytes + feeLiquidity: BigDecimal +} + +type Burn @entity { + " transaction.id:transaction.burns.length " + id: ID! + transaction: Transaction! + timestamp: BigInt! # need this to pull recent txns for specific token or pair + pair: Pair! + + # populated from the primary Transfer event + liquidity: BigDecimal! + + # populated from the Burn event + sender: String + amount0: BigDecimal + amount1: BigDecimal + to: String + logIndex: BigInt + # derived amount based on available prices of tokens + amountUSD: BigDecimal + + # mark uncomplete in ETH case + complete: Boolean! + + # optional fee fields, if a Transfer event is fired in _mintFee + feeTo: String + feeLiquidity: BigDecimal +} + +type Swap @entity { + # transaction hash - index of swap in transaction swaps array + id: ID! + transaction: Transaction! + timestamp: BigInt! # need this to pull recent txns for specific token or pair + pair: Pair! + + # populated from the Swap event + sender: String! + tokenIn: Token! + tokenOut: Token! + amountIn: BigDecimal! + amountOut: BigDecimal! + to: String! + logIndex: BigInt + + # derived info + amountUSD: BigDecimal! +} + + +type Transaction @entity { + " Tx hash " + id: ID! + gasLimit: BigInt! + gasPrice: BigInt! + # This is not the reverse of Mint.transaction; it is only used to + # track incomplete mints (similar for burns and swaps) + mints: [Mint!]! + burns: [Burn!]! + swaps: [Swap!]! + createdAtBlock: BigInt! + createdAtTimestamp: BigInt! +} + +# saved over time for return calculations, gets created and never updated +type LiquidityPositionSnapshot @entity(immutable: true) { + " {lp.id}-{timestamp} " + id: ID! + liquidityPosition: LiquidityPosition! + " saved for fast historical lookups " + timestamp: Int! + " saved for fast historical lookups " + block: Int! + " reference to user " + user: User! + " reference to pair " + pair: Pair! + " snapshot of token0 price " + token0PriceUSD: BigDecimal! + " snapshot of token1 price " + token1PriceUSD: BigDecimal! + " snapshot of pair token0 reserves " + reserve0: BigInt! + " snapshot of pair token1 reserves " + reserve1: BigInt! + " snapshot of pair reserves in USD " + reserveUSD: BigDecimal! + " snapshot of pool token supply " + liquidityTokenTotalSupply: BigInt! + " snapshot of users pool token balance " + liquidityTokenBalance: BigInt! +} + +type PairHourSnapshot @entity { + " {pairId}-hour-{timestamp} " + id: ID! + pair: Pair! + date: Int! + " Used to calculate apr " + cumulativeVolumeUSD: BigDecimal! + volumeUSD: BigDecimal! + volumeNative: BigDecimal! + volumeToken0: BigDecimal! + volumeToken1: BigDecimal! + liquidity: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + apr: BigDecimal! + transactionCount: BigInt! +} + +type PairDaySnapshot @entity { + " {pairId}-day-{timestamp} " + id: ID! + pair: Pair! + date: Int! + " Used to calculate apr " + cumulativeVolumeUSD: BigDecimal! + volumeUSD: BigDecimal! + volumeNative: BigDecimal! + volumeToken0: BigDecimal! + volumeToken1: BigDecimal! + liquidity: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + apr: BigDecimal! + transactionCount: BigInt! +} + +type TokenHourSnapshot @entity { + " {tokenId}-hour-{timestamp} " + id: ID! + date: Int! + token: Token! + liquidity: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + volume: BigDecimal! + volumeNative: BigDecimal! + volumeUSD: BigDecimal! + priceNative: BigDecimal! + priceUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + transactionCount: BigInt! +} + +type TokenDaySnapshot @entity { + " {tokenId}-day-{timestamp} " + id: ID! + date: Int! + token: Token! + liquidity: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + volume: BigDecimal! + volumeNative: BigDecimal! + volumeUSD: BigDecimal! + priceNative: BigDecimal! + priceUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + transactionCount: BigInt! +} + +type FactoryHourSnapshot @entity { + " {factoryId}-hour-{timestamp} " + id: ID! + factory: Factory! + date: Int! + volumeUSD: BigDecimal! + volumeNative: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + transactionCount: BigInt! +} + +type FactoryDaySnapshot @entity { + " {factoryId}-day-{timestamp} " + id: ID! + factory: Factory! + date: Int! + volumeUSD: BigDecimal! + volumeNative: BigDecimal! + liquidityNative: BigDecimal! + liquidityUSD: BigDecimal! + feesNative: BigDecimal! + feesUSD: BigDecimal! + transactionCount: BigInt! +} + diff --git a/packages/sushiswap-watcher/subgraph-build/subgraph.yaml b/packages/sushiswap-watcher/subgraph-build/subgraph.yaml new file mode 100644 index 0000000..9df365a --- /dev/null +++ b/packages/sushiswap-watcher/subgraph-build/subgraph.yaml @@ -0,0 +1,69 @@ +specVersion: 0.0.6 +description: Sushiswap +repository: https://github.com/sushiswap/sushiswap-subgraph +schema: + file: schema.graphql +dataSources: + - kind: ethereum/contract + name: Factory + network: mainnet + source: + address: "0x9b3336186a38e1b6c21955d112dbb0343ee061ee" + abi: Factory + startBlock: 3328632 + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + file: Factory/Factory.wasm + entities: [] + abis: + - name: Factory + file: Factory/node_modules/abi/Uniswap/UniswapV2Factory.json + - name: Pair + file: Factory/node_modules/abi/Uniswap/UniswapV2Pair.json + - name: SushiToken + file: Factory/node_modules/abi/SUSHI/SushiToken.json + - name: ERC20 + file: Factory/node_modules/abi/ERC20/ERC20.json + - name: SymbolBytes32 + file: Factory/node_modules/abi/ERC20/SymbolBytes32.json + - name: NameBytes32 + file: Factory/node_modules/abi/ERC20/NameBytes32.json + eventHandlers: + - event: PairCreated(indexed address,indexed address,address,uint256) + handler: onPairCreated +templates: + - kind: ethereum/contract + name: Pair + network: mainnet + source: + abi: Pair + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + file: templates/Pair/Pair.wasm + entities: [] + abis: + - name: Factory + file: Pair/node_modules/abi/Uniswap/UniswapV2Factory.json + - name: Pair + file: Pair/node_modules/abi/Uniswap/UniswapV2Pair.json + - name: ERC20 + file: Pair/node_modules/abi/ERC20/ERC20.json + - name: SymbolBytes32 + file: Pair/node_modules/abi/ERC20/SymbolBytes32.json + - name: NameBytes32 + file: Pair/node_modules/abi/ERC20/NameBytes32.json + eventHandlers: + - event: Sync(uint112,uint112) + handler: onSync + - event: Transfer(indexed address,indexed address,uint256) + handler: onTransfer + - event: Mint(indexed address,uint256,uint256) + handler: onMint + - event: Burn(indexed address,uint256,uint256,indexed address) + handler: onBurn + - event: Swap(indexed address,uint256,uint256,uint256,uint256,indexed address) + handler: onSwap diff --git a/packages/sushiswap-watcher/subgraph-build/templates/Pair/Pair.wasm b/packages/sushiswap-watcher/subgraph-build/templates/Pair/Pair.wasm new file mode 100644 index 0000000000000000000000000000000000000000..b12d8734330d68cd054efa61c7df7409263d827f GIT binary patch literal 326868 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

3#4b<7Q(Y8dRwjZ-fnA0BD;Hg2vh7f6gVYu z>|H9|oK5Vtb~M+ZvCWwO*ll&Vke?Z1kmKU%UMjn@7W~+-yT5nuW-Eb{SDQn0zJi_H zX>+}G01M1}l9>z+@E1O}gisL$mv;dOs9hmE7=qfE#L{Vj781~o{x;0ppshEC1mtq_ zSuNoNZj*iCJUw-f0XKzkd`PfOILgr!zIrg99kR&^9%%QR5dOMwQ?qq9hcp4|HbbDs zWA>GidTvPCZM6EjQwKJr;OJuYzk>O^9kt`Kg4i0;rpSmBr+q1Cw}jMHv7HK3A|!XQ z#0D~=V6}zp$u?TND#ZoY9#U8oxEx{W2oaC)jGVp1-3kfJzFrK>$o6MCLkfu3%{JjO zo34lcZgsB zWlh@H6VejdqRN#+HYtWIw0&sqmU^k_LilO0&yY(2j=;I?9!bW?9<>YqXkMK$c?)SEN` z-d-F0TK20z(F42Yp)A$m)wQ>9gx90tCfnaKFEK*I-4!(2rI#8ZCQlqNv`H^BLK{n5 zhp3F(%fr(iu=*11*k6MMZRi*d)dD-*3EP|L&)iiD>}*(ym#;8(RX=J{OxTE{=aM`d z`-W83YTT!c<|~cR3c9gBjq8$Ay#wJDRQiWk8KEs(Za?$t@SGKZj4R$hBZ zu<2hXZO$GL!x1TChL~m=fRD8lhc4oZ4`2cuS;)HLnY=p9;|S-tGfp!(VTN z_O`YpyLM*<*c*(S!YaD}+t8iFGF|vviT1q?dk=>fI>$qLqY>JpaX@Xwp(DvRg_k-9 zUNf%F?Y7<=9zlq2w%N)mZV!f6em7|P79&JxO=&Frz`hiEiS<@jvR4G>J|jd_Cl@H< zJGiPo-Ln6!Mu<^zF3iT>4ngz&@YafMHJMGX>20+EXLTqpzdbw=#p&^~B|zi8RNKGkvH zz2S|fGi-Zbc;>l7toMf}y9-P8(?j7^$%P2B`2izz+&~AXhLo8P*09+M+c-=pteR28)dd+m)aBbnJNp*Uf0i7Sy;I7 zIit)eG)2(Xx~_+e>W&fCK-jVW&l_b9>nMT@!tGx$%IxMZQk2u?U#yaVGC+RVD02W# zk&30TSk5n1xnZwhIrEo|>h{Ow0Q!ni-R>O@B)_y&hannx{Q70aa8p}&#oNL_d124W& zPQCo`l#b+^MxeWfbW~-U>05@nnyd_M_IYQWJQA#Js)T!AX;%MZvP+Z0+2}n literal 0 HcmV?d00001 diff --git a/packages/sushiswap-watcher/tsconfig.json b/packages/sushiswap-watcher/tsconfig.json new file mode 100644 index 0000000..f4b8852 --- /dev/null +++ b/packages/sushiswap-watcher/tsconfig.json @@ -0,0 +1,74 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "lib": ["es2019"], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "dist", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ + "resolveJsonModule": true /* Enabling the option allows importing JSON, and validating the types in that JSON file. */ + }, + "include": ["src/**/*"] +}