├── .dockerignore ├── .github └── workflows │ └── docker-publish.yml ├── .gitignore ├── .kubernetes.prod └── deployment.yaml ├── .kubernetes └── deployment.yaml ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE.md ├── new-readme.md ├── package-lock.json ├── package.json ├── readme.md ├── src ├── artifacts │ ├── example-contract-alternative.yaml │ ├── example-contract.yaml │ └── indexes.yaml ├── dao │ ├── EventDAOImpl.ts │ ├── OptimisticContractDAOImpl.ts │ └── interface │ │ ├── EventDAO.ts │ │ └── OptimisticContractDAO.ts ├── helpers │ ├── ContractRegistrationHelper.ts │ ├── ContractRegistrationServiceLocalImpl.ts │ └── ContractRegistrationServiceRemoteImpl.ts ├── indexer.ts ├── models │ ├── OptimisticContract.ts │ ├── OptimisticEventDBO.ts │ └── ReconcilerType.ts ├── queue │ └── queue.service.ts └── services │ ├── ChainServiceImpl.ts │ ├── EventReconciliationServiceImpl.ts │ ├── EventServiceImpl.ts │ ├── IndexManagerImpl.ts │ ├── OptimisticContractServiceImpl.ts │ ├── PointToPointIndexerServiceImpl.ts │ ├── SweepingIndexerServiceImpl.ts │ └── interfaces │ ├── ChainService.ts │ ├── EventReconciliationService.ts │ ├── EventService.ts │ ├── IndexManager.ts │ ├── OptimisticContractService.ts │ ├── PointToPointIndexerService.ts │ └── SweepingIndexerService.ts └── tsconfig.json /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Build and Push to GHCR 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - v* 9 | 10 | env: 11 | IMAGE_NAME: intergalactic_indexer 12 | 13 | jobs: 14 | push: 15 | runs-on: ubuntu-latest 16 | permissions: 17 | packages: write 18 | contents: read 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | 23 | - name: Build image 24 | run: docker build . --file Dockerfile --tag $IMAGE_NAME --label "runnumber=${GITHUB_RUN_ID}" 25 | 26 | - name: Log in to registry 27 | # This is where you will update the PAT to GITHUB_TOKEN 28 | run: echo "${{ secrets.CR_PAT }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin 29 | 30 | - name: Push image 31 | run: | 32 | IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME 33 | # Change all uppercase to lowercase 34 | IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') 35 | VERSION=${GITHUB_SHA:0:6} 36 | 37 | echo IMAGE_ID=$IMAGE_ID 38 | echo VERSION=$VERSION 39 | docker tag $IMAGE_NAME $IMAGE_ID:$VERSION 40 | docker push $IMAGE_ID:$VERSION -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dest 3 | .env 4 | .idea 5 | build 6 | .env.test 7 | .kubernetes/secrets/* 8 | .kubernetes.prod/secrets/* 9 | **/.DS_Store -------------------------------------------------------------------------------- /.kubernetes.prod/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: indexer 5 | namespace: polynomial-prod 6 | labels: 7 | app: indexer 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: indexer 13 | template: 14 | metadata: 15 | labels: 16 | app: indexer 17 | spec: 18 | containers: 19 | - name: indexer 20 | image: ghcr.io/polynomial-protocol/intergalactic_indexer:cf542b 21 | resources: 22 | requests: 23 | memory: "300Mi" 24 | cpu: "300m" 25 | limits: 26 | memory: "600Mi" 27 | cpu: "500m" 28 | ports: 29 | - containerPort: 80 30 | env: 31 | - name: MONGO_URI 32 | valueFrom: 33 | secretKeyRef: 34 | name: indexer-env 35 | key: MONGO_URI 36 | - name: APP_NAME 37 | valueFrom: 38 | secretKeyRef: 39 | name: indexer-env 40 | key: APP_NAME 41 | - name: CONTRACTS_RELATIVE_PATH 42 | valueFrom: 43 | secretKeyRef: 44 | name: indexer-env 45 | key: CONTRACTS_RELATIVE_PATH 46 | - name: NETWORK 47 | valueFrom: 48 | secretKeyRef: 49 | name: indexer-env 50 | key: NETWORK 51 | - name: INDEXES_RELATIVE_PATH 52 | valueFrom: 53 | secretKeyRef: 54 | name: indexer-env 55 | key: INDEXES_RELATIVE_PATH 56 | - name: DB_NAME 57 | valueFrom: 58 | secretKeyRef: 59 | name: indexer-env 60 | key: DB_NAME 61 | - name: ALCHEMY_API_KEY_FOR_INDEXER 62 | valueFrom: 63 | secretKeyRef: 64 | name: indexer-env 65 | key: ALCHEMY_API_KEY_FOR_INDEXER 66 | - name: ALCHEMY_API_KEY_FOR_RECONCILIATION 67 | valueFrom: 68 | secretKeyRef: 69 | name: indexer-env 70 | key: ALCHEMY_API_KEY_FOR_RECONCILIATION 71 | 72 | imagePullSecrets: 73 | - name: ghcr-secret -------------------------------------------------------------------------------- /.kubernetes/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: indexer 5 | namespace: polynomial 6 | labels: 7 | app: indexer 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: indexer 13 | template: 14 | metadata: 15 | labels: 16 | app: indexer 17 | spec: 18 | containers: 19 | - name: indexer 20 | image: ghcr.io/polynomial-protocol/intergalactic_indexer:fb03d4 21 | resources: 22 | requests: 23 | memory: "300Mi" 24 | cpu: "300m" 25 | limits: 26 | memory: "600Mi" 27 | cpu: "500m" 28 | ports: 29 | - containerPort: 80 30 | env: 31 | - name: MONGO_URI 32 | valueFrom: 33 | secretKeyRef: 34 | name: indexer-env 35 | key: MONGO_URI 36 | - name: APP_NAME 37 | valueFrom: 38 | secretKeyRef: 39 | name: indexer-env 40 | key: APP_NAME 41 | - name: CONTRACTS_RELATIVE_PATH 42 | valueFrom: 43 | secretKeyRef: 44 | name: indexer-env 45 | key: CONTRACTS_RELATIVE_PATH 46 | - name: NETWORK 47 | valueFrom: 48 | secretKeyRef: 49 | name: indexer-env 50 | key: NETWORK 51 | - name: INDEXES_RELATIVE_PATH 52 | valueFrom: 53 | secretKeyRef: 54 | name: indexer-env 55 | key: INDEXES_RELATIVE_PATH 56 | - name: DB_NAME 57 | valueFrom: 58 | secretKeyRef: 59 | name: indexer-env 60 | key: DB_NAME 61 | - name: ALCHEMY_API_KEY_FOR_INDEXER 62 | valueFrom: 63 | secretKeyRef: 64 | name: indexer-env 65 | key: ALCHEMY_API_KEY_FOR_INDEXER 66 | - name: ALCHEMY_API_KEY_FOR_RECONCILIATION 67 | valueFrom: 68 | secretKeyRef: 69 | name: indexer-env 70 | key: ALCHEMY_API_KEY_FOR_RECONCILIATION 71 | 72 | imagePullSecrets: 73 | - name: ghcr-secret -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to Optimistic Indexer 2 | 3 | Thanks for your interest in improving Optimistic Indexer! 4 | 5 | There are multiple opportunities to contribute at any level. It doesn't matter if you are just getting started with Typescrit or are the most weathered expert, we can use your help. 6 | 7 | **No contribution is too small and all contributions are valued.** 8 | 9 | This document will help you get started. **Do not let the document intimidate you**. 10 | It should be considered as a guide to help you navigate the process. 11 | 12 | The [Polynomial Discord](https://discord.com/invite/polynomial) is available for any concerns you may have that are not covered in this guide. 13 | 14 | If you contribute to this project, your contributions will be made to the project under the MIT license. 15 | 16 | ### Code of Conduct 17 | 18 | The Optimistic Indexer project adheres to the [Typescript Code of Conduct](https://github.com/microsoft/TypeScript/blob/main/CODE_OF_CONDUCT.md). This code of conduct describes the _minimum_ behavior expected from all contributors. 19 | 20 | Instances of violations of the Code of Conduct can be reported by contacting the team at [Polynomial Protocol](https://twitter.com/PolynomialFi). 21 | 22 | ### Ways to contribute 23 | 24 | There are fundamentally three ways an individual can contribute: 25 | 26 | 1. **By opening an issue:** For example, if you believe that you have uncovered a bug 27 | in Optimistic Indexer or see a way to improve it further, creating a new issue in the issue tracker is the way to get started. 28 | 2. **By adding context:** Providing additional context to existing issues, 29 | such as screenshots and code snippets to help resolve issues. 30 | 3. **By resolving issues:** Typically this is done in the form of either 31 | demonstrating that the issue reported is not a problem after all, or more often, 32 | by opening a pull request that fixes the underlying problem, in a concrete and 33 | reviewable manner. 34 | 35 | **Anybody can participate in any stage of contribution**. We urge you to participate in the discussion around bugs and participate in reviewing PRs. 36 | 37 | ### Asking for help 38 | 39 | If you have reviewed existing documentation and still have questions, or you are having problems, you can get help by **opening a discussion**. This repository comes with a discussions board where you can also ask for help. Click the "Discussions" tab at the top. 40 | 41 | ### Submitting a bug report 42 | 43 | When filing a new bug report in the issue tracker, you will be presented with a basic form to fill out. 44 | 45 | If you believe that you have uncovered a bug, please fill out the form to the best of your ability. Do not worry if you cannot answer every detail, just fill in what you can. Contributors will ask follow-up questions if something is unclear. 46 | 47 | In order to rule out the possibility of the bug being in your project, the code snippets should be as minimal as possible. It is better if you can reproduce the bug with a small snippet as opposed to an entire project! 48 | 49 | See [this guide][mcve] on how to create a minimal, complete, and verifiable example. 50 | 51 | ### Submitting a feature request 52 | 53 | When adding a feature request in the issue tracker, you will be presented with a basic form to fill out. 54 | 55 | Please include as detailed of an explanation as possible of the feature you would like, adding additional context if necessary. 56 | 57 | If you have examples of other tools that have the feature you are requesting, please include them as well. 58 | 59 | ### Resolving an issue 60 | 61 | Pull requests are the way concrete changes are made to the code, documentation, and dependencies of Optimistic Indexer. 62 | 63 | Even tiny pull requests, like fixing wording, are greatly appreciated. Before making a large change, it is usually a good idea to first open an issue describing the change to solicit feedback and guidance. This will increase the likelihood of the PR getting merged. 64 | 65 | #### Commits 66 | 67 | It is a recommended best practice to keep your changes as logically grouped as possible within individual commits. There is no limit to the number of commits any single pull request may have, and many contributors find it easier to review changes that are split across multiple commits. 68 | 69 | That said, if you have a number of commits that are "checkpoints" and don't represent a single logical change, please squash those together. 70 | 71 | #### Opening the pull request 72 | 73 | From within GitHub, opening a new pull request will present you with a template that should be filled out. Please try your best at filling out the details, but feel free to skip parts if you're not sure what to put. 74 | 75 | #### Reviewing pull requests 76 | 77 | **Any Optimistic Indexer community member is welcome to review any pull request**. 78 | 79 | All contributors who choose to review and provide feedback on pull requests have a responsibility to both the project and individual making the contribution. Reviews and feedback must be helpful, insightful, and geared towards improving the contribution as opposed to simply blocking it. If there are reasons why you feel the PR should not be merged, explain what those are. Do not expect to be able to block a PR from advancing simply because you say "no" without giving an explanation. Be open to having your mind changed. Be open to working _with_ the contributor to make the pull request better. 80 | 81 | Reviews that are dismissive or disrespectful of the contributor or any other reviewers are strictly counter to the Code of Conduct. 82 | 83 | When reviewing a pull request, the primary goals are for the codebase to improve and for the person submitting the request to succeed. **Even if a pull request is not merged, the submitter should come away from the experience feeling like their effort was not unappreciated**. Every PR from a new contributor is an opportunity to grow the community. 84 | 85 | ##### Be aware of the person behind the code 86 | 87 | Be aware that _how_ you communicate requests and reviews in your feedback can have a significant impact on the success of the pull request. Yes, we may merge a particular change that makes Optimistic Indexer better, but the individual might just not want to have anything to do with Optimistic Indexer ever again. The goal is not just having good code. 88 | 89 | ##### Abandoned or stale pull requests 90 | 91 | If a pull request appears to be abandoned or stalled, it is polite to first check with the contributor to see if they intend to continue the work before checking if they would mind if you took it over (especially if it just has nits left). When doing so, it is courteous to give the original contributor credit for the work they started, either by preserving their name and e-mail address in the commit log, or by using the `Author: ` or `Co-authored-by: ` metadata tag in the commits. 92 | 93 | _Adapted from the [Foundry contributing guide][foundry-contributing]_. 94 | 95 | [foundry-contributing]: https://github.com/foundry-rs/foundry/blob/master/CONTRIBUTING.md 96 | [mcve]: https://stackoverflow.com/help/mcve 97 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-alpine3.14 2 | WORKDIR /usr/intergalactic-indexer 3 | COPY package*.json ./ 4 | RUN npm install 5 | COPY . ./ 6 | RUN npm run build 7 | RUN mkdir -p build/artifacts 8 | COPY src/artifacts/* build/artifacts/ 9 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Optimistic Indexer contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /new-readme.md: -------------------------------------------------------------------------------- 1 | # why an indexer is needed - it’s importance 2 | 3 | An indexer is critical for building the backend (APIs) for an application that runs on the blockchain. This is because 4 | blockchains (e.g.- all EVM chains like Ethereum, Optimism, etc.) emits events in the transaction receipts of each block 5 | mined which contains necessary information about the state change that happened due to the transactions inside the 6 | block. 7 | 8 | e.g. If we submit a transaction to transfer an NFT, the block in which the transaction gets mined emits an event with 9 | the following structure: 10 | 11 | Transfer(address from, address to, uint256 tokenId) 12 | 13 | This event contains information like the sender, receiver, and the id of the token transferred. This information is 14 | useful for any application that wants to take some action for any token transfer for the NFT contract. 15 | 16 | # Limitations of the current indexer 17 | 18 | 1. It only supports Optimism chain for now. 19 | 2. Ordering of events is not guaranteed. This is because it uses websockets to listen for current events and a 20 | reconciliation loop to index events that were missed (due to downtime or disconnected websockets or any other reason) 21 | 3. The current indexer is not optimized for performance. It is a single-threaded application that reads events from 22 | the chain and saves them into the database. This is not scalable for a large number of events. 23 | 24 | # examples of applications in a chain that would benefit the most from an indexer 25 | 26 | Almost all applications that have a smart contract deployed on a blockchain use one of the 2 approaches to index events. 27 | a. Use a decentralised indexer like Graph to index events of the smart contract 28 | b. Run an in-house indexer to index events 29 | 30 | Application that use the second approach will benefit the most from this indexer. 31 | 32 | # how an open sourced model will help all the developers on-chain in their dev process 33 | 34 | An open-source indexer will save significant development time spent in writing an indexer from scratch. 35 | 36 | # if dev using anything other than kubernetes - changes to do 37 | 38 | It has a Dockerfile that can be used to build and run the indexer in any environment which supports containers. -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "intergalatic-indexer", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "intergalatic-indexer", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "dotenv": "^16.0.0", 13 | "ethers": "^5.5.4", 14 | "js-yaml": "^4.1.0", 15 | "kafkajs": "^1.16.0", 16 | "mongodb": "^4.3.1", 17 | "node-cron": "^3.0.0", 18 | "prettier": "^2.7.1" 19 | }, 20 | "devDependencies": { 21 | "ts-node": "^10.4.0", 22 | "typescript": "^4.5.5" 23 | } 24 | }, 25 | "node_modules/@cspotcode/source-map-consumer": { 26 | "version": "0.8.0", 27 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", 28 | "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", 29 | "dev": true, 30 | "engines": { 31 | "node": ">= 12" 32 | } 33 | }, 34 | "node_modules/@cspotcode/source-map-support": { 35 | "version": "0.7.0", 36 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", 37 | "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", 38 | "dev": true, 39 | "dependencies": { 40 | "@cspotcode/source-map-consumer": "0.8.0" 41 | }, 42 | "engines": { 43 | "node": ">=12" 44 | } 45 | }, 46 | "node_modules/@ethersproject/abi": { 47 | "version": "5.5.0", 48 | "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.5.0.tgz", 49 | "integrity": "sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w==", 50 | "funding": [ 51 | { 52 | "type": "individual", 53 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 54 | }, 55 | { 56 | "type": "individual", 57 | "url": "https://www.buymeacoffee.com/ricmoo" 58 | } 59 | ], 60 | "dependencies": { 61 | "@ethersproject/address": "^5.5.0", 62 | "@ethersproject/bignumber": "^5.5.0", 63 | "@ethersproject/bytes": "^5.5.0", 64 | "@ethersproject/constants": "^5.5.0", 65 | "@ethersproject/hash": "^5.5.0", 66 | "@ethersproject/keccak256": "^5.5.0", 67 | "@ethersproject/logger": "^5.5.0", 68 | "@ethersproject/properties": "^5.5.0", 69 | "@ethersproject/strings": "^5.5.0" 70 | } 71 | }, 72 | "node_modules/@ethersproject/abstract-provider": { 73 | "version": "5.5.1", 74 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", 75 | "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", 76 | "funding": [ 77 | { 78 | "type": "individual", 79 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 80 | }, 81 | { 82 | "type": "individual", 83 | "url": "https://www.buymeacoffee.com/ricmoo" 84 | } 85 | ], 86 | "dependencies": { 87 | "@ethersproject/bignumber": "^5.5.0", 88 | "@ethersproject/bytes": "^5.5.0", 89 | "@ethersproject/logger": "^5.5.0", 90 | "@ethersproject/networks": "^5.5.0", 91 | "@ethersproject/properties": "^5.5.0", 92 | "@ethersproject/transactions": "^5.5.0", 93 | "@ethersproject/web": "^5.5.0" 94 | } 95 | }, 96 | "node_modules/@ethersproject/abstract-signer": { 97 | "version": "5.5.0", 98 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", 99 | "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", 100 | "funding": [ 101 | { 102 | "type": "individual", 103 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 104 | }, 105 | { 106 | "type": "individual", 107 | "url": "https://www.buymeacoffee.com/ricmoo" 108 | } 109 | ], 110 | "dependencies": { 111 | "@ethersproject/abstract-provider": "^5.5.0", 112 | "@ethersproject/bignumber": "^5.5.0", 113 | "@ethersproject/bytes": "^5.5.0", 114 | "@ethersproject/logger": "^5.5.0", 115 | "@ethersproject/properties": "^5.5.0" 116 | } 117 | }, 118 | "node_modules/@ethersproject/address": { 119 | "version": "5.5.0", 120 | "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", 121 | "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", 122 | "funding": [ 123 | { 124 | "type": "individual", 125 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 126 | }, 127 | { 128 | "type": "individual", 129 | "url": "https://www.buymeacoffee.com/ricmoo" 130 | } 131 | ], 132 | "dependencies": { 133 | "@ethersproject/bignumber": "^5.5.0", 134 | "@ethersproject/bytes": "^5.5.0", 135 | "@ethersproject/keccak256": "^5.5.0", 136 | "@ethersproject/logger": "^5.5.0", 137 | "@ethersproject/rlp": "^5.5.0" 138 | } 139 | }, 140 | "node_modules/@ethersproject/base64": { 141 | "version": "5.5.0", 142 | "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", 143 | "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", 144 | "funding": [ 145 | { 146 | "type": "individual", 147 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 148 | }, 149 | { 150 | "type": "individual", 151 | "url": "https://www.buymeacoffee.com/ricmoo" 152 | } 153 | ], 154 | "dependencies": { 155 | "@ethersproject/bytes": "^5.5.0" 156 | } 157 | }, 158 | "node_modules/@ethersproject/basex": { 159 | "version": "5.5.0", 160 | "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.5.0.tgz", 161 | "integrity": "sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ==", 162 | "funding": [ 163 | { 164 | "type": "individual", 165 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 166 | }, 167 | { 168 | "type": "individual", 169 | "url": "https://www.buymeacoffee.com/ricmoo" 170 | } 171 | ], 172 | "dependencies": { 173 | "@ethersproject/bytes": "^5.5.0", 174 | "@ethersproject/properties": "^5.5.0" 175 | } 176 | }, 177 | "node_modules/@ethersproject/bignumber": { 178 | "version": "5.5.0", 179 | "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", 180 | "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", 181 | "funding": [ 182 | { 183 | "type": "individual", 184 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 185 | }, 186 | { 187 | "type": "individual", 188 | "url": "https://www.buymeacoffee.com/ricmoo" 189 | } 190 | ], 191 | "dependencies": { 192 | "@ethersproject/bytes": "^5.5.0", 193 | "@ethersproject/logger": "^5.5.0", 194 | "bn.js": "^4.11.9" 195 | } 196 | }, 197 | "node_modules/@ethersproject/bytes": { 198 | "version": "5.5.0", 199 | "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", 200 | "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", 201 | "funding": [ 202 | { 203 | "type": "individual", 204 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 205 | }, 206 | { 207 | "type": "individual", 208 | "url": "https://www.buymeacoffee.com/ricmoo" 209 | } 210 | ], 211 | "dependencies": { 212 | "@ethersproject/logger": "^5.5.0" 213 | } 214 | }, 215 | "node_modules/@ethersproject/constants": { 216 | "version": "5.5.0", 217 | "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", 218 | "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", 219 | "funding": [ 220 | { 221 | "type": "individual", 222 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 223 | }, 224 | { 225 | "type": "individual", 226 | "url": "https://www.buymeacoffee.com/ricmoo" 227 | } 228 | ], 229 | "dependencies": { 230 | "@ethersproject/bignumber": "^5.5.0" 231 | } 232 | }, 233 | "node_modules/@ethersproject/contracts": { 234 | "version": "5.5.0", 235 | "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.5.0.tgz", 236 | "integrity": "sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg==", 237 | "funding": [ 238 | { 239 | "type": "individual", 240 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 241 | }, 242 | { 243 | "type": "individual", 244 | "url": "https://www.buymeacoffee.com/ricmoo" 245 | } 246 | ], 247 | "dependencies": { 248 | "@ethersproject/abi": "^5.5.0", 249 | "@ethersproject/abstract-provider": "^5.5.0", 250 | "@ethersproject/abstract-signer": "^5.5.0", 251 | "@ethersproject/address": "^5.5.0", 252 | "@ethersproject/bignumber": "^5.5.0", 253 | "@ethersproject/bytes": "^5.5.0", 254 | "@ethersproject/constants": "^5.5.0", 255 | "@ethersproject/logger": "^5.5.0", 256 | "@ethersproject/properties": "^5.5.0", 257 | "@ethersproject/transactions": "^5.5.0" 258 | } 259 | }, 260 | "node_modules/@ethersproject/hash": { 261 | "version": "5.5.0", 262 | "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", 263 | "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", 264 | "funding": [ 265 | { 266 | "type": "individual", 267 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 268 | }, 269 | { 270 | "type": "individual", 271 | "url": "https://www.buymeacoffee.com/ricmoo" 272 | } 273 | ], 274 | "dependencies": { 275 | "@ethersproject/abstract-signer": "^5.5.0", 276 | "@ethersproject/address": "^5.5.0", 277 | "@ethersproject/bignumber": "^5.5.0", 278 | "@ethersproject/bytes": "^5.5.0", 279 | "@ethersproject/keccak256": "^5.5.0", 280 | "@ethersproject/logger": "^5.5.0", 281 | "@ethersproject/properties": "^5.5.0", 282 | "@ethersproject/strings": "^5.5.0" 283 | } 284 | }, 285 | "node_modules/@ethersproject/hdnode": { 286 | "version": "5.5.0", 287 | "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.5.0.tgz", 288 | "integrity": "sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q==", 289 | "funding": [ 290 | { 291 | "type": "individual", 292 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 293 | }, 294 | { 295 | "type": "individual", 296 | "url": "https://www.buymeacoffee.com/ricmoo" 297 | } 298 | ], 299 | "dependencies": { 300 | "@ethersproject/abstract-signer": "^5.5.0", 301 | "@ethersproject/basex": "^5.5.0", 302 | "@ethersproject/bignumber": "^5.5.0", 303 | "@ethersproject/bytes": "^5.5.0", 304 | "@ethersproject/logger": "^5.5.0", 305 | "@ethersproject/pbkdf2": "^5.5.0", 306 | "@ethersproject/properties": "^5.5.0", 307 | "@ethersproject/sha2": "^5.5.0", 308 | "@ethersproject/signing-key": "^5.5.0", 309 | "@ethersproject/strings": "^5.5.0", 310 | "@ethersproject/transactions": "^5.5.0", 311 | "@ethersproject/wordlists": "^5.5.0" 312 | } 313 | }, 314 | "node_modules/@ethersproject/json-wallets": { 315 | "version": "5.5.0", 316 | "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz", 317 | "integrity": "sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ==", 318 | "funding": [ 319 | { 320 | "type": "individual", 321 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 322 | }, 323 | { 324 | "type": "individual", 325 | "url": "https://www.buymeacoffee.com/ricmoo" 326 | } 327 | ], 328 | "dependencies": { 329 | "@ethersproject/abstract-signer": "^5.5.0", 330 | "@ethersproject/address": "^5.5.0", 331 | "@ethersproject/bytes": "^5.5.0", 332 | "@ethersproject/hdnode": "^5.5.0", 333 | "@ethersproject/keccak256": "^5.5.0", 334 | "@ethersproject/logger": "^5.5.0", 335 | "@ethersproject/pbkdf2": "^5.5.0", 336 | "@ethersproject/properties": "^5.5.0", 337 | "@ethersproject/random": "^5.5.0", 338 | "@ethersproject/strings": "^5.5.0", 339 | "@ethersproject/transactions": "^5.5.0", 340 | "aes-js": "3.0.0", 341 | "scrypt-js": "3.0.1" 342 | } 343 | }, 344 | "node_modules/@ethersproject/keccak256": { 345 | "version": "5.5.0", 346 | "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", 347 | "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", 348 | "funding": [ 349 | { 350 | "type": "individual", 351 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 352 | }, 353 | { 354 | "type": "individual", 355 | "url": "https://www.buymeacoffee.com/ricmoo" 356 | } 357 | ], 358 | "dependencies": { 359 | "@ethersproject/bytes": "^5.5.0", 360 | "js-sha3": "0.8.0" 361 | } 362 | }, 363 | "node_modules/@ethersproject/logger": { 364 | "version": "5.5.0", 365 | "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", 366 | "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==", 367 | "funding": [ 368 | { 369 | "type": "individual", 370 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 371 | }, 372 | { 373 | "type": "individual", 374 | "url": "https://www.buymeacoffee.com/ricmoo" 375 | } 376 | ] 377 | }, 378 | "node_modules/@ethersproject/networks": { 379 | "version": "5.5.2", 380 | "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.2.tgz", 381 | "integrity": "sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==", 382 | "funding": [ 383 | { 384 | "type": "individual", 385 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 386 | }, 387 | { 388 | "type": "individual", 389 | "url": "https://www.buymeacoffee.com/ricmoo" 390 | } 391 | ], 392 | "dependencies": { 393 | "@ethersproject/logger": "^5.5.0" 394 | } 395 | }, 396 | "node_modules/@ethersproject/pbkdf2": { 397 | "version": "5.5.0", 398 | "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz", 399 | "integrity": "sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg==", 400 | "funding": [ 401 | { 402 | "type": "individual", 403 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 404 | }, 405 | { 406 | "type": "individual", 407 | "url": "https://www.buymeacoffee.com/ricmoo" 408 | } 409 | ], 410 | "dependencies": { 411 | "@ethersproject/bytes": "^5.5.0", 412 | "@ethersproject/sha2": "^5.5.0" 413 | } 414 | }, 415 | "node_modules/@ethersproject/properties": { 416 | "version": "5.5.0", 417 | "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", 418 | "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", 419 | "funding": [ 420 | { 421 | "type": "individual", 422 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 423 | }, 424 | { 425 | "type": "individual", 426 | "url": "https://www.buymeacoffee.com/ricmoo" 427 | } 428 | ], 429 | "dependencies": { 430 | "@ethersproject/logger": "^5.5.0" 431 | } 432 | }, 433 | "node_modules/@ethersproject/providers": { 434 | "version": "5.5.3", 435 | "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.5.3.tgz", 436 | "integrity": "sha512-ZHXxXXXWHuwCQKrgdpIkbzMNJMvs+9YWemanwp1fA7XZEv7QlilseysPvQe0D7Q7DlkJX/w/bGA1MdgK2TbGvA==", 437 | "funding": [ 438 | { 439 | "type": "individual", 440 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 441 | }, 442 | { 443 | "type": "individual", 444 | "url": "https://www.buymeacoffee.com/ricmoo" 445 | } 446 | ], 447 | "dependencies": { 448 | "@ethersproject/abstract-provider": "^5.5.0", 449 | "@ethersproject/abstract-signer": "^5.5.0", 450 | "@ethersproject/address": "^5.5.0", 451 | "@ethersproject/basex": "^5.5.0", 452 | "@ethersproject/bignumber": "^5.5.0", 453 | "@ethersproject/bytes": "^5.5.0", 454 | "@ethersproject/constants": "^5.5.0", 455 | "@ethersproject/hash": "^5.5.0", 456 | "@ethersproject/logger": "^5.5.0", 457 | "@ethersproject/networks": "^5.5.0", 458 | "@ethersproject/properties": "^5.5.0", 459 | "@ethersproject/random": "^5.5.0", 460 | "@ethersproject/rlp": "^5.5.0", 461 | "@ethersproject/sha2": "^5.5.0", 462 | "@ethersproject/strings": "^5.5.0", 463 | "@ethersproject/transactions": "^5.5.0", 464 | "@ethersproject/web": "^5.5.0", 465 | "bech32": "1.1.4", 466 | "ws": "7.4.6" 467 | } 468 | }, 469 | "node_modules/@ethersproject/random": { 470 | "version": "5.5.1", 471 | "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.5.1.tgz", 472 | "integrity": "sha512-YaU2dQ7DuhL5Au7KbcQLHxcRHfgyNgvFV4sQOo0HrtW3Zkrc9ctWNz8wXQ4uCSfSDsqX2vcjhroxU5RQRV0nqA==", 473 | "funding": [ 474 | { 475 | "type": "individual", 476 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 477 | }, 478 | { 479 | "type": "individual", 480 | "url": "https://www.buymeacoffee.com/ricmoo" 481 | } 482 | ], 483 | "dependencies": { 484 | "@ethersproject/bytes": "^5.5.0", 485 | "@ethersproject/logger": "^5.5.0" 486 | } 487 | }, 488 | "node_modules/@ethersproject/rlp": { 489 | "version": "5.5.0", 490 | "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", 491 | "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", 492 | "funding": [ 493 | { 494 | "type": "individual", 495 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 496 | }, 497 | { 498 | "type": "individual", 499 | "url": "https://www.buymeacoffee.com/ricmoo" 500 | } 501 | ], 502 | "dependencies": { 503 | "@ethersproject/bytes": "^5.5.0", 504 | "@ethersproject/logger": "^5.5.0" 505 | } 506 | }, 507 | "node_modules/@ethersproject/sha2": { 508 | "version": "5.5.0", 509 | "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.5.0.tgz", 510 | "integrity": "sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA==", 511 | "funding": [ 512 | { 513 | "type": "individual", 514 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 515 | }, 516 | { 517 | "type": "individual", 518 | "url": "https://www.buymeacoffee.com/ricmoo" 519 | } 520 | ], 521 | "dependencies": { 522 | "@ethersproject/bytes": "^5.5.0", 523 | "@ethersproject/logger": "^5.5.0", 524 | "hash.js": "1.1.7" 525 | } 526 | }, 527 | "node_modules/@ethersproject/signing-key": { 528 | "version": "5.5.0", 529 | "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", 530 | "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", 531 | "funding": [ 532 | { 533 | "type": "individual", 534 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 535 | }, 536 | { 537 | "type": "individual", 538 | "url": "https://www.buymeacoffee.com/ricmoo" 539 | } 540 | ], 541 | "dependencies": { 542 | "@ethersproject/bytes": "^5.5.0", 543 | "@ethersproject/logger": "^5.5.0", 544 | "@ethersproject/properties": "^5.5.0", 545 | "bn.js": "^4.11.9", 546 | "elliptic": "6.5.4", 547 | "hash.js": "1.1.7" 548 | } 549 | }, 550 | "node_modules/@ethersproject/solidity": { 551 | "version": "5.5.0", 552 | "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.5.0.tgz", 553 | "integrity": "sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw==", 554 | "funding": [ 555 | { 556 | "type": "individual", 557 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 558 | }, 559 | { 560 | "type": "individual", 561 | "url": "https://www.buymeacoffee.com/ricmoo" 562 | } 563 | ], 564 | "dependencies": { 565 | "@ethersproject/bignumber": "^5.5.0", 566 | "@ethersproject/bytes": "^5.5.0", 567 | "@ethersproject/keccak256": "^5.5.0", 568 | "@ethersproject/logger": "^5.5.0", 569 | "@ethersproject/sha2": "^5.5.0", 570 | "@ethersproject/strings": "^5.5.0" 571 | } 572 | }, 573 | "node_modules/@ethersproject/strings": { 574 | "version": "5.5.0", 575 | "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", 576 | "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", 577 | "funding": [ 578 | { 579 | "type": "individual", 580 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 581 | }, 582 | { 583 | "type": "individual", 584 | "url": "https://www.buymeacoffee.com/ricmoo" 585 | } 586 | ], 587 | "dependencies": { 588 | "@ethersproject/bytes": "^5.5.0", 589 | "@ethersproject/constants": "^5.5.0", 590 | "@ethersproject/logger": "^5.5.0" 591 | } 592 | }, 593 | "node_modules/@ethersproject/transactions": { 594 | "version": "5.5.0", 595 | "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", 596 | "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", 597 | "funding": [ 598 | { 599 | "type": "individual", 600 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 601 | }, 602 | { 603 | "type": "individual", 604 | "url": "https://www.buymeacoffee.com/ricmoo" 605 | } 606 | ], 607 | "dependencies": { 608 | "@ethersproject/address": "^5.5.0", 609 | "@ethersproject/bignumber": "^5.5.0", 610 | "@ethersproject/bytes": "^5.5.0", 611 | "@ethersproject/constants": "^5.5.0", 612 | "@ethersproject/keccak256": "^5.5.0", 613 | "@ethersproject/logger": "^5.5.0", 614 | "@ethersproject/properties": "^5.5.0", 615 | "@ethersproject/rlp": "^5.5.0", 616 | "@ethersproject/signing-key": "^5.5.0" 617 | } 618 | }, 619 | "node_modules/@ethersproject/units": { 620 | "version": "5.5.0", 621 | "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.5.0.tgz", 622 | "integrity": "sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag==", 623 | "funding": [ 624 | { 625 | "type": "individual", 626 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 627 | }, 628 | { 629 | "type": "individual", 630 | "url": "https://www.buymeacoffee.com/ricmoo" 631 | } 632 | ], 633 | "dependencies": { 634 | "@ethersproject/bignumber": "^5.5.0", 635 | "@ethersproject/constants": "^5.5.0", 636 | "@ethersproject/logger": "^5.5.0" 637 | } 638 | }, 639 | "node_modules/@ethersproject/wallet": { 640 | "version": "5.5.0", 641 | "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.5.0.tgz", 642 | "integrity": "sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q==", 643 | "funding": [ 644 | { 645 | "type": "individual", 646 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 647 | }, 648 | { 649 | "type": "individual", 650 | "url": "https://www.buymeacoffee.com/ricmoo" 651 | } 652 | ], 653 | "dependencies": { 654 | "@ethersproject/abstract-provider": "^5.5.0", 655 | "@ethersproject/abstract-signer": "^5.5.0", 656 | "@ethersproject/address": "^5.5.0", 657 | "@ethersproject/bignumber": "^5.5.0", 658 | "@ethersproject/bytes": "^5.5.0", 659 | "@ethersproject/hash": "^5.5.0", 660 | "@ethersproject/hdnode": "^5.5.0", 661 | "@ethersproject/json-wallets": "^5.5.0", 662 | "@ethersproject/keccak256": "^5.5.0", 663 | "@ethersproject/logger": "^5.5.0", 664 | "@ethersproject/properties": "^5.5.0", 665 | "@ethersproject/random": "^5.5.0", 666 | "@ethersproject/signing-key": "^5.5.0", 667 | "@ethersproject/transactions": "^5.5.0", 668 | "@ethersproject/wordlists": "^5.5.0" 669 | } 670 | }, 671 | "node_modules/@ethersproject/web": { 672 | "version": "5.5.1", 673 | "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", 674 | "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", 675 | "funding": [ 676 | { 677 | "type": "individual", 678 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 679 | }, 680 | { 681 | "type": "individual", 682 | "url": "https://www.buymeacoffee.com/ricmoo" 683 | } 684 | ], 685 | "dependencies": { 686 | "@ethersproject/base64": "^5.5.0", 687 | "@ethersproject/bytes": "^5.5.0", 688 | "@ethersproject/logger": "^5.5.0", 689 | "@ethersproject/properties": "^5.5.0", 690 | "@ethersproject/strings": "^5.5.0" 691 | } 692 | }, 693 | "node_modules/@ethersproject/wordlists": { 694 | "version": "5.5.0", 695 | "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.5.0.tgz", 696 | "integrity": "sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q==", 697 | "funding": [ 698 | { 699 | "type": "individual", 700 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 701 | }, 702 | { 703 | "type": "individual", 704 | "url": "https://www.buymeacoffee.com/ricmoo" 705 | } 706 | ], 707 | "dependencies": { 708 | "@ethersproject/bytes": "^5.5.0", 709 | "@ethersproject/hash": "^5.5.0", 710 | "@ethersproject/logger": "^5.5.0", 711 | "@ethersproject/properties": "^5.5.0", 712 | "@ethersproject/strings": "^5.5.0" 713 | } 714 | }, 715 | "node_modules/@tsconfig/node10": { 716 | "version": "1.0.8", 717 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", 718 | "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", 719 | "dev": true 720 | }, 721 | "node_modules/@tsconfig/node12": { 722 | "version": "1.0.9", 723 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", 724 | "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", 725 | "dev": true 726 | }, 727 | "node_modules/@tsconfig/node14": { 728 | "version": "1.0.1", 729 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", 730 | "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", 731 | "dev": true 732 | }, 733 | "node_modules/@tsconfig/node16": { 734 | "version": "1.0.2", 735 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", 736 | "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", 737 | "dev": true 738 | }, 739 | "node_modules/@types/node": { 740 | "version": "17.0.5", 741 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.5.tgz", 742 | "integrity": "sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw==" 743 | }, 744 | "node_modules/@types/webidl-conversions": { 745 | "version": "6.1.1", 746 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", 747 | "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==" 748 | }, 749 | "node_modules/@types/whatwg-url": { 750 | "version": "8.2.1", 751 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz", 752 | "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==", 753 | "dependencies": { 754 | "@types/node": "*", 755 | "@types/webidl-conversions": "*" 756 | } 757 | }, 758 | "node_modules/acorn": { 759 | "version": "8.7.0", 760 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", 761 | "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", 762 | "dev": true, 763 | "bin": { 764 | "acorn": "bin/acorn" 765 | }, 766 | "engines": { 767 | "node": ">=0.4.0" 768 | } 769 | }, 770 | "node_modules/acorn-walk": { 771 | "version": "8.2.0", 772 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 773 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 774 | "dev": true, 775 | "engines": { 776 | "node": ">=0.4.0" 777 | } 778 | }, 779 | "node_modules/aes-js": { 780 | "version": "3.0.0", 781 | "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", 782 | "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" 783 | }, 784 | "node_modules/arg": { 785 | "version": "4.1.3", 786 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 787 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 788 | "dev": true 789 | }, 790 | "node_modules/argparse": { 791 | "version": "2.0.1", 792 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 793 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 794 | }, 795 | "node_modules/base64-js": { 796 | "version": "1.5.1", 797 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 798 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 799 | "funding": [ 800 | { 801 | "type": "github", 802 | "url": "https://github.com/sponsors/feross" 803 | }, 804 | { 805 | "type": "patreon", 806 | "url": "https://www.patreon.com/feross" 807 | }, 808 | { 809 | "type": "consulting", 810 | "url": "https://feross.org/support" 811 | } 812 | ] 813 | }, 814 | "node_modules/bech32": { 815 | "version": "1.1.4", 816 | "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", 817 | "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" 818 | }, 819 | "node_modules/bn.js": { 820 | "version": "4.12.0", 821 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", 822 | "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" 823 | }, 824 | "node_modules/brorand": { 825 | "version": "1.1.0", 826 | "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", 827 | "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" 828 | }, 829 | "node_modules/bson": { 830 | "version": "4.6.1", 831 | "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.1.tgz", 832 | "integrity": "sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==", 833 | "dependencies": { 834 | "buffer": "^5.6.0" 835 | }, 836 | "engines": { 837 | "node": ">=6.9.0" 838 | } 839 | }, 840 | "node_modules/buffer": { 841 | "version": "5.7.1", 842 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 843 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 844 | "funding": [ 845 | { 846 | "type": "github", 847 | "url": "https://github.com/sponsors/feross" 848 | }, 849 | { 850 | "type": "patreon", 851 | "url": "https://www.patreon.com/feross" 852 | }, 853 | { 854 | "type": "consulting", 855 | "url": "https://feross.org/support" 856 | } 857 | ], 858 | "dependencies": { 859 | "base64-js": "^1.3.1", 860 | "ieee754": "^1.1.13" 861 | } 862 | }, 863 | "node_modules/create-require": { 864 | "version": "1.1.1", 865 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 866 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 867 | "dev": true 868 | }, 869 | "node_modules/denque": { 870 | "version": "2.0.1", 871 | "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", 872 | "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==", 873 | "engines": { 874 | "node": ">=0.10" 875 | } 876 | }, 877 | "node_modules/diff": { 878 | "version": "4.0.2", 879 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 880 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 881 | "dev": true, 882 | "engines": { 883 | "node": ">=0.3.1" 884 | } 885 | }, 886 | "node_modules/dotenv": { 887 | "version": "16.0.0", 888 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", 889 | "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==", 890 | "engines": { 891 | "node": ">=12" 892 | } 893 | }, 894 | "node_modules/elliptic": { 895 | "version": "6.5.4", 896 | "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", 897 | "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", 898 | "dependencies": { 899 | "bn.js": "^4.11.9", 900 | "brorand": "^1.1.0", 901 | "hash.js": "^1.0.0", 902 | "hmac-drbg": "^1.0.1", 903 | "inherits": "^2.0.4", 904 | "minimalistic-assert": "^1.0.1", 905 | "minimalistic-crypto-utils": "^1.0.1" 906 | } 907 | }, 908 | "node_modules/ethers": { 909 | "version": "5.5.4", 910 | "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.5.4.tgz", 911 | "integrity": "sha512-N9IAXsF8iKhgHIC6pquzRgPBJEzc9auw3JoRkaKe+y4Wl/LFBtDDunNe7YmdomontECAcC5APaAgWZBiu1kirw==", 912 | "funding": [ 913 | { 914 | "type": "individual", 915 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 916 | }, 917 | { 918 | "type": "individual", 919 | "url": "https://www.buymeacoffee.com/ricmoo" 920 | } 921 | ], 922 | "dependencies": { 923 | "@ethersproject/abi": "5.5.0", 924 | "@ethersproject/abstract-provider": "5.5.1", 925 | "@ethersproject/abstract-signer": "5.5.0", 926 | "@ethersproject/address": "5.5.0", 927 | "@ethersproject/base64": "5.5.0", 928 | "@ethersproject/basex": "5.5.0", 929 | "@ethersproject/bignumber": "5.5.0", 930 | "@ethersproject/bytes": "5.5.0", 931 | "@ethersproject/constants": "5.5.0", 932 | "@ethersproject/contracts": "5.5.0", 933 | "@ethersproject/hash": "5.5.0", 934 | "@ethersproject/hdnode": "5.5.0", 935 | "@ethersproject/json-wallets": "5.5.0", 936 | "@ethersproject/keccak256": "5.5.0", 937 | "@ethersproject/logger": "5.5.0", 938 | "@ethersproject/networks": "5.5.2", 939 | "@ethersproject/pbkdf2": "5.5.0", 940 | "@ethersproject/properties": "5.5.0", 941 | "@ethersproject/providers": "5.5.3", 942 | "@ethersproject/random": "5.5.1", 943 | "@ethersproject/rlp": "5.5.0", 944 | "@ethersproject/sha2": "5.5.0", 945 | "@ethersproject/signing-key": "5.5.0", 946 | "@ethersproject/solidity": "5.5.0", 947 | "@ethersproject/strings": "5.5.0", 948 | "@ethersproject/transactions": "5.5.0", 949 | "@ethersproject/units": "5.5.0", 950 | "@ethersproject/wallet": "5.5.0", 951 | "@ethersproject/web": "5.5.1", 952 | "@ethersproject/wordlists": "5.5.0" 953 | } 954 | }, 955 | "node_modules/hash.js": { 956 | "version": "1.1.7", 957 | "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", 958 | "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", 959 | "dependencies": { 960 | "inherits": "^2.0.3", 961 | "minimalistic-assert": "^1.0.1" 962 | } 963 | }, 964 | "node_modules/hmac-drbg": { 965 | "version": "1.0.1", 966 | "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", 967 | "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", 968 | "dependencies": { 969 | "hash.js": "^1.0.3", 970 | "minimalistic-assert": "^1.0.0", 971 | "minimalistic-crypto-utils": "^1.0.1" 972 | } 973 | }, 974 | "node_modules/ieee754": { 975 | "version": "1.2.1", 976 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 977 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 978 | "funding": [ 979 | { 980 | "type": "github", 981 | "url": "https://github.com/sponsors/feross" 982 | }, 983 | { 984 | "type": "patreon", 985 | "url": "https://www.patreon.com/feross" 986 | }, 987 | { 988 | "type": "consulting", 989 | "url": "https://feross.org/support" 990 | } 991 | ] 992 | }, 993 | "node_modules/inherits": { 994 | "version": "2.0.4", 995 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 996 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 997 | }, 998 | "node_modules/ip": { 999 | "version": "1.1.5", 1000 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 1001 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" 1002 | }, 1003 | "node_modules/js-sha3": { 1004 | "version": "0.8.0", 1005 | "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", 1006 | "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" 1007 | }, 1008 | "node_modules/js-yaml": { 1009 | "version": "4.1.0", 1010 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1011 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1012 | "dependencies": { 1013 | "argparse": "^2.0.1" 1014 | }, 1015 | "bin": { 1016 | "js-yaml": "bin/js-yaml.js" 1017 | } 1018 | }, 1019 | "node_modules/kafkajs": { 1020 | "version": "1.16.0", 1021 | "resolved": "https://registry.npmjs.org/kafkajs/-/kafkajs-1.16.0.tgz", 1022 | "integrity": "sha512-+Rcfu2hyQ/jv5skqRY8xA7Ra+mmRkDAzCaLDYbkGtgsNKpzxPWiLbk8ub0dgr4EbWrN1Zb4BCXHUkD6+zYfdWg==", 1023 | "engines": { 1024 | "node": ">=10.13.0" 1025 | } 1026 | }, 1027 | "node_modules/make-error": { 1028 | "version": "1.3.6", 1029 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 1030 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 1031 | "dev": true 1032 | }, 1033 | "node_modules/memory-pager": { 1034 | "version": "1.5.0", 1035 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 1036 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 1037 | "optional": true 1038 | }, 1039 | "node_modules/minimalistic-assert": { 1040 | "version": "1.0.1", 1041 | "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", 1042 | "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" 1043 | }, 1044 | "node_modules/minimalistic-crypto-utils": { 1045 | "version": "1.0.1", 1046 | "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", 1047 | "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" 1048 | }, 1049 | "node_modules/moment": { 1050 | "version": "2.29.1", 1051 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", 1052 | "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", 1053 | "engines": { 1054 | "node": "*" 1055 | } 1056 | }, 1057 | "node_modules/moment-timezone": { 1058 | "version": "0.5.34", 1059 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz", 1060 | "integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==", 1061 | "dependencies": { 1062 | "moment": ">= 2.9.0" 1063 | }, 1064 | "engines": { 1065 | "node": "*" 1066 | } 1067 | }, 1068 | "node_modules/mongodb": { 1069 | "version": "4.3.1", 1070 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.3.1.tgz", 1071 | "integrity": "sha512-sNa8APSIk+r4x31ZwctKjuPSaeKuvUeNb/fu/3B6dRM02HpEgig7hTHM8A/PJQTlxuC/KFWlDlQjhsk/S43tBg==", 1072 | "dependencies": { 1073 | "bson": "^4.6.1", 1074 | "denque": "^2.0.1", 1075 | "mongodb-connection-string-url": "^2.4.1", 1076 | "socks": "^2.6.1" 1077 | }, 1078 | "engines": { 1079 | "node": ">=12.9.0" 1080 | }, 1081 | "optionalDependencies": { 1082 | "saslprep": "^1.0.3" 1083 | } 1084 | }, 1085 | "node_modules/mongodb-connection-string-url": { 1086 | "version": "2.4.1", 1087 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.4.1.tgz", 1088 | "integrity": "sha512-d5Kd2bVsKcSA7YI/yo57fSTtMwRQdFkvc5IZwod1RRxJtECeWPPSo7zqcUGJELifRA//Igs4spVtYAmvFCatug==", 1089 | "dependencies": { 1090 | "@types/whatwg-url": "^8.2.1", 1091 | "whatwg-url": "^11.0.0" 1092 | } 1093 | }, 1094 | "node_modules/node-cron": { 1095 | "version": "3.0.0", 1096 | "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.0.tgz", 1097 | "integrity": "sha512-DDwIvvuCwrNiaU7HEivFDULcaQualDv7KoNlB/UU1wPW0n1tDEmBJKhEIE6DlF2FuoOHcNbLJ8ITL2Iv/3AWmA==", 1098 | "dependencies": { 1099 | "moment-timezone": "^0.5.31" 1100 | }, 1101 | "engines": { 1102 | "node": ">=6.0.0" 1103 | } 1104 | }, 1105 | "node_modules/prettier": { 1106 | "version": "2.7.1", 1107 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", 1108 | "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", 1109 | "bin": { 1110 | "prettier": "bin-prettier.js" 1111 | }, 1112 | "engines": { 1113 | "node": ">=10.13.0" 1114 | }, 1115 | "funding": { 1116 | "url": "https://github.com/prettier/prettier?sponsor=1" 1117 | } 1118 | }, 1119 | "node_modules/punycode": { 1120 | "version": "2.1.1", 1121 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1122 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 1123 | "engines": { 1124 | "node": ">=6" 1125 | } 1126 | }, 1127 | "node_modules/saslprep": { 1128 | "version": "1.0.3", 1129 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 1130 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 1131 | "optional": true, 1132 | "dependencies": { 1133 | "sparse-bitfield": "^3.0.3" 1134 | }, 1135 | "engines": { 1136 | "node": ">=6" 1137 | } 1138 | }, 1139 | "node_modules/scrypt-js": { 1140 | "version": "3.0.1", 1141 | "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", 1142 | "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" 1143 | }, 1144 | "node_modules/smart-buffer": { 1145 | "version": "4.2.0", 1146 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 1147 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", 1148 | "engines": { 1149 | "node": ">= 6.0.0", 1150 | "npm": ">= 3.0.0" 1151 | } 1152 | }, 1153 | "node_modules/socks": { 1154 | "version": "2.6.1", 1155 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", 1156 | "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", 1157 | "dependencies": { 1158 | "ip": "^1.1.5", 1159 | "smart-buffer": "^4.1.0" 1160 | }, 1161 | "engines": { 1162 | "node": ">= 10.13.0", 1163 | "npm": ">= 3.0.0" 1164 | } 1165 | }, 1166 | "node_modules/sparse-bitfield": { 1167 | "version": "3.0.3", 1168 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 1169 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", 1170 | "optional": true, 1171 | "dependencies": { 1172 | "memory-pager": "^1.0.2" 1173 | } 1174 | }, 1175 | "node_modules/tr46": { 1176 | "version": "3.0.0", 1177 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", 1178 | "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", 1179 | "dependencies": { 1180 | "punycode": "^2.1.1" 1181 | }, 1182 | "engines": { 1183 | "node": ">=12" 1184 | } 1185 | }, 1186 | "node_modules/ts-node": { 1187 | "version": "10.4.0", 1188 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", 1189 | "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", 1190 | "dev": true, 1191 | "dependencies": { 1192 | "@cspotcode/source-map-support": "0.7.0", 1193 | "@tsconfig/node10": "^1.0.7", 1194 | "@tsconfig/node12": "^1.0.7", 1195 | "@tsconfig/node14": "^1.0.0", 1196 | "@tsconfig/node16": "^1.0.2", 1197 | "acorn": "^8.4.1", 1198 | "acorn-walk": "^8.1.1", 1199 | "arg": "^4.1.0", 1200 | "create-require": "^1.1.0", 1201 | "diff": "^4.0.1", 1202 | "make-error": "^1.1.1", 1203 | "yn": "3.1.1" 1204 | }, 1205 | "bin": { 1206 | "ts-node": "dist/bin.js", 1207 | "ts-node-cwd": "dist/bin-cwd.js", 1208 | "ts-node-script": "dist/bin-script.js", 1209 | "ts-node-transpile-only": "dist/bin-transpile.js", 1210 | "ts-script": "dist/bin-script-deprecated.js" 1211 | }, 1212 | "peerDependencies": { 1213 | "@swc/core": ">=1.2.50", 1214 | "@swc/wasm": ">=1.2.50", 1215 | "@types/node": "*", 1216 | "typescript": ">=2.7" 1217 | }, 1218 | "peerDependenciesMeta": { 1219 | "@swc/core": { 1220 | "optional": true 1221 | }, 1222 | "@swc/wasm": { 1223 | "optional": true 1224 | } 1225 | } 1226 | }, 1227 | "node_modules/typescript": { 1228 | "version": "4.5.5", 1229 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", 1230 | "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", 1231 | "dev": true, 1232 | "bin": { 1233 | "tsc": "bin/tsc", 1234 | "tsserver": "bin/tsserver" 1235 | }, 1236 | "engines": { 1237 | "node": ">=4.2.0" 1238 | } 1239 | }, 1240 | "node_modules/webidl-conversions": { 1241 | "version": "7.0.0", 1242 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 1243 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", 1244 | "engines": { 1245 | "node": ">=12" 1246 | } 1247 | }, 1248 | "node_modules/whatwg-url": { 1249 | "version": "11.0.0", 1250 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", 1251 | "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", 1252 | "dependencies": { 1253 | "tr46": "^3.0.0", 1254 | "webidl-conversions": "^7.0.0" 1255 | }, 1256 | "engines": { 1257 | "node": ">=12" 1258 | } 1259 | }, 1260 | "node_modules/ws": { 1261 | "version": "7.4.6", 1262 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", 1263 | "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", 1264 | "engines": { 1265 | "node": ">=8.3.0" 1266 | }, 1267 | "peerDependencies": { 1268 | "bufferutil": "^4.0.1", 1269 | "utf-8-validate": "^5.0.2" 1270 | }, 1271 | "peerDependenciesMeta": { 1272 | "bufferutil": { 1273 | "optional": true 1274 | }, 1275 | "utf-8-validate": { 1276 | "optional": true 1277 | } 1278 | } 1279 | }, 1280 | "node_modules/yn": { 1281 | "version": "3.1.1", 1282 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 1283 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 1284 | "dev": true, 1285 | "engines": { 1286 | "node": ">=6" 1287 | } 1288 | } 1289 | }, 1290 | "dependencies": { 1291 | "@cspotcode/source-map-consumer": { 1292 | "version": "0.8.0", 1293 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", 1294 | "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", 1295 | "dev": true 1296 | }, 1297 | "@cspotcode/source-map-support": { 1298 | "version": "0.7.0", 1299 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", 1300 | "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", 1301 | "dev": true, 1302 | "requires": { 1303 | "@cspotcode/source-map-consumer": "0.8.0" 1304 | } 1305 | }, 1306 | "@ethersproject/abi": { 1307 | "version": "5.5.0", 1308 | "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.5.0.tgz", 1309 | "integrity": "sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w==", 1310 | "requires": { 1311 | "@ethersproject/address": "^5.5.0", 1312 | "@ethersproject/bignumber": "^5.5.0", 1313 | "@ethersproject/bytes": "^5.5.0", 1314 | "@ethersproject/constants": "^5.5.0", 1315 | "@ethersproject/hash": "^5.5.0", 1316 | "@ethersproject/keccak256": "^5.5.0", 1317 | "@ethersproject/logger": "^5.5.0", 1318 | "@ethersproject/properties": "^5.5.0", 1319 | "@ethersproject/strings": "^5.5.0" 1320 | } 1321 | }, 1322 | "@ethersproject/abstract-provider": { 1323 | "version": "5.5.1", 1324 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", 1325 | "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", 1326 | "requires": { 1327 | "@ethersproject/bignumber": "^5.5.0", 1328 | "@ethersproject/bytes": "^5.5.0", 1329 | "@ethersproject/logger": "^5.5.0", 1330 | "@ethersproject/networks": "^5.5.0", 1331 | "@ethersproject/properties": "^5.5.0", 1332 | "@ethersproject/transactions": "^5.5.0", 1333 | "@ethersproject/web": "^5.5.0" 1334 | } 1335 | }, 1336 | "@ethersproject/abstract-signer": { 1337 | "version": "5.5.0", 1338 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", 1339 | "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", 1340 | "requires": { 1341 | "@ethersproject/abstract-provider": "^5.5.0", 1342 | "@ethersproject/bignumber": "^5.5.0", 1343 | "@ethersproject/bytes": "^5.5.0", 1344 | "@ethersproject/logger": "^5.5.0", 1345 | "@ethersproject/properties": "^5.5.0" 1346 | } 1347 | }, 1348 | "@ethersproject/address": { 1349 | "version": "5.5.0", 1350 | "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", 1351 | "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", 1352 | "requires": { 1353 | "@ethersproject/bignumber": "^5.5.0", 1354 | "@ethersproject/bytes": "^5.5.0", 1355 | "@ethersproject/keccak256": "^5.5.0", 1356 | "@ethersproject/logger": "^5.5.0", 1357 | "@ethersproject/rlp": "^5.5.0" 1358 | } 1359 | }, 1360 | "@ethersproject/base64": { 1361 | "version": "5.5.0", 1362 | "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", 1363 | "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", 1364 | "requires": { 1365 | "@ethersproject/bytes": "^5.5.0" 1366 | } 1367 | }, 1368 | "@ethersproject/basex": { 1369 | "version": "5.5.0", 1370 | "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.5.0.tgz", 1371 | "integrity": "sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ==", 1372 | "requires": { 1373 | "@ethersproject/bytes": "^5.5.0", 1374 | "@ethersproject/properties": "^5.5.0" 1375 | } 1376 | }, 1377 | "@ethersproject/bignumber": { 1378 | "version": "5.5.0", 1379 | "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", 1380 | "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", 1381 | "requires": { 1382 | "@ethersproject/bytes": "^5.5.0", 1383 | "@ethersproject/logger": "^5.5.0", 1384 | "bn.js": "^4.11.9" 1385 | } 1386 | }, 1387 | "@ethersproject/bytes": { 1388 | "version": "5.5.0", 1389 | "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", 1390 | "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", 1391 | "requires": { 1392 | "@ethersproject/logger": "^5.5.0" 1393 | } 1394 | }, 1395 | "@ethersproject/constants": { 1396 | "version": "5.5.0", 1397 | "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", 1398 | "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", 1399 | "requires": { 1400 | "@ethersproject/bignumber": "^5.5.0" 1401 | } 1402 | }, 1403 | "@ethersproject/contracts": { 1404 | "version": "5.5.0", 1405 | "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.5.0.tgz", 1406 | "integrity": "sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg==", 1407 | "requires": { 1408 | "@ethersproject/abi": "^5.5.0", 1409 | "@ethersproject/abstract-provider": "^5.5.0", 1410 | "@ethersproject/abstract-signer": "^5.5.0", 1411 | "@ethersproject/address": "^5.5.0", 1412 | "@ethersproject/bignumber": "^5.5.0", 1413 | "@ethersproject/bytes": "^5.5.0", 1414 | "@ethersproject/constants": "^5.5.0", 1415 | "@ethersproject/logger": "^5.5.0", 1416 | "@ethersproject/properties": "^5.5.0", 1417 | "@ethersproject/transactions": "^5.5.0" 1418 | } 1419 | }, 1420 | "@ethersproject/hash": { 1421 | "version": "5.5.0", 1422 | "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", 1423 | "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", 1424 | "requires": { 1425 | "@ethersproject/abstract-signer": "^5.5.0", 1426 | "@ethersproject/address": "^5.5.0", 1427 | "@ethersproject/bignumber": "^5.5.0", 1428 | "@ethersproject/bytes": "^5.5.0", 1429 | "@ethersproject/keccak256": "^5.5.0", 1430 | "@ethersproject/logger": "^5.5.0", 1431 | "@ethersproject/properties": "^5.5.0", 1432 | "@ethersproject/strings": "^5.5.0" 1433 | } 1434 | }, 1435 | "@ethersproject/hdnode": { 1436 | "version": "5.5.0", 1437 | "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.5.0.tgz", 1438 | "integrity": "sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q==", 1439 | "requires": { 1440 | "@ethersproject/abstract-signer": "^5.5.0", 1441 | "@ethersproject/basex": "^5.5.0", 1442 | "@ethersproject/bignumber": "^5.5.0", 1443 | "@ethersproject/bytes": "^5.5.0", 1444 | "@ethersproject/logger": "^5.5.0", 1445 | "@ethersproject/pbkdf2": "^5.5.0", 1446 | "@ethersproject/properties": "^5.5.0", 1447 | "@ethersproject/sha2": "^5.5.0", 1448 | "@ethersproject/signing-key": "^5.5.0", 1449 | "@ethersproject/strings": "^5.5.0", 1450 | "@ethersproject/transactions": "^5.5.0", 1451 | "@ethersproject/wordlists": "^5.5.0" 1452 | } 1453 | }, 1454 | "@ethersproject/json-wallets": { 1455 | "version": "5.5.0", 1456 | "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz", 1457 | "integrity": "sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ==", 1458 | "requires": { 1459 | "@ethersproject/abstract-signer": "^5.5.0", 1460 | "@ethersproject/address": "^5.5.0", 1461 | "@ethersproject/bytes": "^5.5.0", 1462 | "@ethersproject/hdnode": "^5.5.0", 1463 | "@ethersproject/keccak256": "^5.5.0", 1464 | "@ethersproject/logger": "^5.5.0", 1465 | "@ethersproject/pbkdf2": "^5.5.0", 1466 | "@ethersproject/properties": "^5.5.0", 1467 | "@ethersproject/random": "^5.5.0", 1468 | "@ethersproject/strings": "^5.5.0", 1469 | "@ethersproject/transactions": "^5.5.0", 1470 | "aes-js": "3.0.0", 1471 | "scrypt-js": "3.0.1" 1472 | } 1473 | }, 1474 | "@ethersproject/keccak256": { 1475 | "version": "5.5.0", 1476 | "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", 1477 | "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", 1478 | "requires": { 1479 | "@ethersproject/bytes": "^5.5.0", 1480 | "js-sha3": "0.8.0" 1481 | } 1482 | }, 1483 | "@ethersproject/logger": { 1484 | "version": "5.5.0", 1485 | "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", 1486 | "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" 1487 | }, 1488 | "@ethersproject/networks": { 1489 | "version": "5.5.2", 1490 | "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.2.tgz", 1491 | "integrity": "sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ==", 1492 | "requires": { 1493 | "@ethersproject/logger": "^5.5.0" 1494 | } 1495 | }, 1496 | "@ethersproject/pbkdf2": { 1497 | "version": "5.5.0", 1498 | "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz", 1499 | "integrity": "sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg==", 1500 | "requires": { 1501 | "@ethersproject/bytes": "^5.5.0", 1502 | "@ethersproject/sha2": "^5.5.0" 1503 | } 1504 | }, 1505 | "@ethersproject/properties": { 1506 | "version": "5.5.0", 1507 | "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", 1508 | "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", 1509 | "requires": { 1510 | "@ethersproject/logger": "^5.5.0" 1511 | } 1512 | }, 1513 | "@ethersproject/providers": { 1514 | "version": "5.5.3", 1515 | "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.5.3.tgz", 1516 | "integrity": "sha512-ZHXxXXXWHuwCQKrgdpIkbzMNJMvs+9YWemanwp1fA7XZEv7QlilseysPvQe0D7Q7DlkJX/w/bGA1MdgK2TbGvA==", 1517 | "requires": { 1518 | "@ethersproject/abstract-provider": "^5.5.0", 1519 | "@ethersproject/abstract-signer": "^5.5.0", 1520 | "@ethersproject/address": "^5.5.0", 1521 | "@ethersproject/basex": "^5.5.0", 1522 | "@ethersproject/bignumber": "^5.5.0", 1523 | "@ethersproject/bytes": "^5.5.0", 1524 | "@ethersproject/constants": "^5.5.0", 1525 | "@ethersproject/hash": "^5.5.0", 1526 | "@ethersproject/logger": "^5.5.0", 1527 | "@ethersproject/networks": "^5.5.0", 1528 | "@ethersproject/properties": "^5.5.0", 1529 | "@ethersproject/random": "^5.5.0", 1530 | "@ethersproject/rlp": "^5.5.0", 1531 | "@ethersproject/sha2": "^5.5.0", 1532 | "@ethersproject/strings": "^5.5.0", 1533 | "@ethersproject/transactions": "^5.5.0", 1534 | "@ethersproject/web": "^5.5.0", 1535 | "bech32": "1.1.4", 1536 | "ws": "7.4.6" 1537 | } 1538 | }, 1539 | "@ethersproject/random": { 1540 | "version": "5.5.1", 1541 | "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.5.1.tgz", 1542 | "integrity": "sha512-YaU2dQ7DuhL5Au7KbcQLHxcRHfgyNgvFV4sQOo0HrtW3Zkrc9ctWNz8wXQ4uCSfSDsqX2vcjhroxU5RQRV0nqA==", 1543 | "requires": { 1544 | "@ethersproject/bytes": "^5.5.0", 1545 | "@ethersproject/logger": "^5.5.0" 1546 | } 1547 | }, 1548 | "@ethersproject/rlp": { 1549 | "version": "5.5.0", 1550 | "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", 1551 | "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", 1552 | "requires": { 1553 | "@ethersproject/bytes": "^5.5.0", 1554 | "@ethersproject/logger": "^5.5.0" 1555 | } 1556 | }, 1557 | "@ethersproject/sha2": { 1558 | "version": "5.5.0", 1559 | "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.5.0.tgz", 1560 | "integrity": "sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA==", 1561 | "requires": { 1562 | "@ethersproject/bytes": "^5.5.0", 1563 | "@ethersproject/logger": "^5.5.0", 1564 | "hash.js": "1.1.7" 1565 | } 1566 | }, 1567 | "@ethersproject/signing-key": { 1568 | "version": "5.5.0", 1569 | "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", 1570 | "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", 1571 | "requires": { 1572 | "@ethersproject/bytes": "^5.5.0", 1573 | "@ethersproject/logger": "^5.5.0", 1574 | "@ethersproject/properties": "^5.5.0", 1575 | "bn.js": "^4.11.9", 1576 | "elliptic": "6.5.4", 1577 | "hash.js": "1.1.7" 1578 | } 1579 | }, 1580 | "@ethersproject/solidity": { 1581 | "version": "5.5.0", 1582 | "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.5.0.tgz", 1583 | "integrity": "sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw==", 1584 | "requires": { 1585 | "@ethersproject/bignumber": "^5.5.0", 1586 | "@ethersproject/bytes": "^5.5.0", 1587 | "@ethersproject/keccak256": "^5.5.0", 1588 | "@ethersproject/logger": "^5.5.0", 1589 | "@ethersproject/sha2": "^5.5.0", 1590 | "@ethersproject/strings": "^5.5.0" 1591 | } 1592 | }, 1593 | "@ethersproject/strings": { 1594 | "version": "5.5.0", 1595 | "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", 1596 | "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", 1597 | "requires": { 1598 | "@ethersproject/bytes": "^5.5.0", 1599 | "@ethersproject/constants": "^5.5.0", 1600 | "@ethersproject/logger": "^5.5.0" 1601 | } 1602 | }, 1603 | "@ethersproject/transactions": { 1604 | "version": "5.5.0", 1605 | "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", 1606 | "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", 1607 | "requires": { 1608 | "@ethersproject/address": "^5.5.0", 1609 | "@ethersproject/bignumber": "^5.5.0", 1610 | "@ethersproject/bytes": "^5.5.0", 1611 | "@ethersproject/constants": "^5.5.0", 1612 | "@ethersproject/keccak256": "^5.5.0", 1613 | "@ethersproject/logger": "^5.5.0", 1614 | "@ethersproject/properties": "^5.5.0", 1615 | "@ethersproject/rlp": "^5.5.0", 1616 | "@ethersproject/signing-key": "^5.5.0" 1617 | } 1618 | }, 1619 | "@ethersproject/units": { 1620 | "version": "5.5.0", 1621 | "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.5.0.tgz", 1622 | "integrity": "sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag==", 1623 | "requires": { 1624 | "@ethersproject/bignumber": "^5.5.0", 1625 | "@ethersproject/constants": "^5.5.0", 1626 | "@ethersproject/logger": "^5.5.0" 1627 | } 1628 | }, 1629 | "@ethersproject/wallet": { 1630 | "version": "5.5.0", 1631 | "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.5.0.tgz", 1632 | "integrity": "sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q==", 1633 | "requires": { 1634 | "@ethersproject/abstract-provider": "^5.5.0", 1635 | "@ethersproject/abstract-signer": "^5.5.0", 1636 | "@ethersproject/address": "^5.5.0", 1637 | "@ethersproject/bignumber": "^5.5.0", 1638 | "@ethersproject/bytes": "^5.5.0", 1639 | "@ethersproject/hash": "^5.5.0", 1640 | "@ethersproject/hdnode": "^5.5.0", 1641 | "@ethersproject/json-wallets": "^5.5.0", 1642 | "@ethersproject/keccak256": "^5.5.0", 1643 | "@ethersproject/logger": "^5.5.0", 1644 | "@ethersproject/properties": "^5.5.0", 1645 | "@ethersproject/random": "^5.5.0", 1646 | "@ethersproject/signing-key": "^5.5.0", 1647 | "@ethersproject/transactions": "^5.5.0", 1648 | "@ethersproject/wordlists": "^5.5.0" 1649 | } 1650 | }, 1651 | "@ethersproject/web": { 1652 | "version": "5.5.1", 1653 | "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", 1654 | "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", 1655 | "requires": { 1656 | "@ethersproject/base64": "^5.5.0", 1657 | "@ethersproject/bytes": "^5.5.0", 1658 | "@ethersproject/logger": "^5.5.0", 1659 | "@ethersproject/properties": "^5.5.0", 1660 | "@ethersproject/strings": "^5.5.0" 1661 | } 1662 | }, 1663 | "@ethersproject/wordlists": { 1664 | "version": "5.5.0", 1665 | "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.5.0.tgz", 1666 | "integrity": "sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q==", 1667 | "requires": { 1668 | "@ethersproject/bytes": "^5.5.0", 1669 | "@ethersproject/hash": "^5.5.0", 1670 | "@ethersproject/logger": "^5.5.0", 1671 | "@ethersproject/properties": "^5.5.0", 1672 | "@ethersproject/strings": "^5.5.0" 1673 | } 1674 | }, 1675 | "@tsconfig/node10": { 1676 | "version": "1.0.8", 1677 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", 1678 | "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", 1679 | "dev": true 1680 | }, 1681 | "@tsconfig/node12": { 1682 | "version": "1.0.9", 1683 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", 1684 | "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", 1685 | "dev": true 1686 | }, 1687 | "@tsconfig/node14": { 1688 | "version": "1.0.1", 1689 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", 1690 | "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", 1691 | "dev": true 1692 | }, 1693 | "@tsconfig/node16": { 1694 | "version": "1.0.2", 1695 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", 1696 | "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", 1697 | "dev": true 1698 | }, 1699 | "@types/node": { 1700 | "version": "17.0.5", 1701 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.5.tgz", 1702 | "integrity": "sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw==" 1703 | }, 1704 | "@types/webidl-conversions": { 1705 | "version": "6.1.1", 1706 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz", 1707 | "integrity": "sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==" 1708 | }, 1709 | "@types/whatwg-url": { 1710 | "version": "8.2.1", 1711 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.1.tgz", 1712 | "integrity": "sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==", 1713 | "requires": { 1714 | "@types/node": "*", 1715 | "@types/webidl-conversions": "*" 1716 | } 1717 | }, 1718 | "acorn": { 1719 | "version": "8.7.0", 1720 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", 1721 | "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", 1722 | "dev": true 1723 | }, 1724 | "acorn-walk": { 1725 | "version": "8.2.0", 1726 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 1727 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 1728 | "dev": true 1729 | }, 1730 | "aes-js": { 1731 | "version": "3.0.0", 1732 | "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", 1733 | "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" 1734 | }, 1735 | "arg": { 1736 | "version": "4.1.3", 1737 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 1738 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 1739 | "dev": true 1740 | }, 1741 | "argparse": { 1742 | "version": "2.0.1", 1743 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1744 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 1745 | }, 1746 | "base64-js": { 1747 | "version": "1.5.1", 1748 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 1749 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 1750 | }, 1751 | "bech32": { 1752 | "version": "1.1.4", 1753 | "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", 1754 | "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" 1755 | }, 1756 | "bn.js": { 1757 | "version": "4.12.0", 1758 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", 1759 | "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" 1760 | }, 1761 | "brorand": { 1762 | "version": "1.1.0", 1763 | "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", 1764 | "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" 1765 | }, 1766 | "bson": { 1767 | "version": "4.6.1", 1768 | "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.1.tgz", 1769 | "integrity": "sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==", 1770 | "requires": { 1771 | "buffer": "^5.6.0" 1772 | } 1773 | }, 1774 | "buffer": { 1775 | "version": "5.7.1", 1776 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 1777 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 1778 | "requires": { 1779 | "base64-js": "^1.3.1", 1780 | "ieee754": "^1.1.13" 1781 | } 1782 | }, 1783 | "create-require": { 1784 | "version": "1.1.1", 1785 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 1786 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 1787 | "dev": true 1788 | }, 1789 | "denque": { 1790 | "version": "2.0.1", 1791 | "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", 1792 | "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==" 1793 | }, 1794 | "diff": { 1795 | "version": "4.0.2", 1796 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 1797 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 1798 | "dev": true 1799 | }, 1800 | "dotenv": { 1801 | "version": "16.0.0", 1802 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", 1803 | "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==" 1804 | }, 1805 | "elliptic": { 1806 | "version": "6.5.4", 1807 | "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", 1808 | "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", 1809 | "requires": { 1810 | "bn.js": "^4.11.9", 1811 | "brorand": "^1.1.0", 1812 | "hash.js": "^1.0.0", 1813 | "hmac-drbg": "^1.0.1", 1814 | "inherits": "^2.0.4", 1815 | "minimalistic-assert": "^1.0.1", 1816 | "minimalistic-crypto-utils": "^1.0.1" 1817 | } 1818 | }, 1819 | "ethers": { 1820 | "version": "5.5.4", 1821 | "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.5.4.tgz", 1822 | "integrity": "sha512-N9IAXsF8iKhgHIC6pquzRgPBJEzc9auw3JoRkaKe+y4Wl/LFBtDDunNe7YmdomontECAcC5APaAgWZBiu1kirw==", 1823 | "requires": { 1824 | "@ethersproject/abi": "5.5.0", 1825 | "@ethersproject/abstract-provider": "5.5.1", 1826 | "@ethersproject/abstract-signer": "5.5.0", 1827 | "@ethersproject/address": "5.5.0", 1828 | "@ethersproject/base64": "5.5.0", 1829 | "@ethersproject/basex": "5.5.0", 1830 | "@ethersproject/bignumber": "5.5.0", 1831 | "@ethersproject/bytes": "5.5.0", 1832 | "@ethersproject/constants": "5.5.0", 1833 | "@ethersproject/contracts": "5.5.0", 1834 | "@ethersproject/hash": "5.5.0", 1835 | "@ethersproject/hdnode": "5.5.0", 1836 | "@ethersproject/json-wallets": "5.5.0", 1837 | "@ethersproject/keccak256": "5.5.0", 1838 | "@ethersproject/logger": "5.5.0", 1839 | "@ethersproject/networks": "5.5.2", 1840 | "@ethersproject/pbkdf2": "5.5.0", 1841 | "@ethersproject/properties": "5.5.0", 1842 | "@ethersproject/providers": "5.5.3", 1843 | "@ethersproject/random": "5.5.1", 1844 | "@ethersproject/rlp": "5.5.0", 1845 | "@ethersproject/sha2": "5.5.0", 1846 | "@ethersproject/signing-key": "5.5.0", 1847 | "@ethersproject/solidity": "5.5.0", 1848 | "@ethersproject/strings": "5.5.0", 1849 | "@ethersproject/transactions": "5.5.0", 1850 | "@ethersproject/units": "5.5.0", 1851 | "@ethersproject/wallet": "5.5.0", 1852 | "@ethersproject/web": "5.5.1", 1853 | "@ethersproject/wordlists": "5.5.0" 1854 | } 1855 | }, 1856 | "hash.js": { 1857 | "version": "1.1.7", 1858 | "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", 1859 | "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", 1860 | "requires": { 1861 | "inherits": "^2.0.3", 1862 | "minimalistic-assert": "^1.0.1" 1863 | } 1864 | }, 1865 | "hmac-drbg": { 1866 | "version": "1.0.1", 1867 | "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", 1868 | "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", 1869 | "requires": { 1870 | "hash.js": "^1.0.3", 1871 | "minimalistic-assert": "^1.0.0", 1872 | "minimalistic-crypto-utils": "^1.0.1" 1873 | } 1874 | }, 1875 | "ieee754": { 1876 | "version": "1.2.1", 1877 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 1878 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 1879 | }, 1880 | "inherits": { 1881 | "version": "2.0.4", 1882 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1883 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1884 | }, 1885 | "ip": { 1886 | "version": "1.1.5", 1887 | "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", 1888 | "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" 1889 | }, 1890 | "js-sha3": { 1891 | "version": "0.8.0", 1892 | "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", 1893 | "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" 1894 | }, 1895 | "js-yaml": { 1896 | "version": "4.1.0", 1897 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1898 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1899 | "requires": { 1900 | "argparse": "^2.0.1" 1901 | } 1902 | }, 1903 | "kafkajs": { 1904 | "version": "1.16.0", 1905 | "resolved": "https://registry.npmjs.org/kafkajs/-/kafkajs-1.16.0.tgz", 1906 | "integrity": "sha512-+Rcfu2hyQ/jv5skqRY8xA7Ra+mmRkDAzCaLDYbkGtgsNKpzxPWiLbk8ub0dgr4EbWrN1Zb4BCXHUkD6+zYfdWg==" 1907 | }, 1908 | "make-error": { 1909 | "version": "1.3.6", 1910 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 1911 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 1912 | "dev": true 1913 | }, 1914 | "memory-pager": { 1915 | "version": "1.5.0", 1916 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 1917 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 1918 | "optional": true 1919 | }, 1920 | "minimalistic-assert": { 1921 | "version": "1.0.1", 1922 | "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", 1923 | "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" 1924 | }, 1925 | "minimalistic-crypto-utils": { 1926 | "version": "1.0.1", 1927 | "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", 1928 | "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" 1929 | }, 1930 | "moment": { 1931 | "version": "2.29.1", 1932 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", 1933 | "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" 1934 | }, 1935 | "moment-timezone": { 1936 | "version": "0.5.34", 1937 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz", 1938 | "integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==", 1939 | "requires": { 1940 | "moment": ">= 2.9.0" 1941 | } 1942 | }, 1943 | "mongodb": { 1944 | "version": "4.3.1", 1945 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.3.1.tgz", 1946 | "integrity": "sha512-sNa8APSIk+r4x31ZwctKjuPSaeKuvUeNb/fu/3B6dRM02HpEgig7hTHM8A/PJQTlxuC/KFWlDlQjhsk/S43tBg==", 1947 | "requires": { 1948 | "bson": "^4.6.1", 1949 | "denque": "^2.0.1", 1950 | "mongodb-connection-string-url": "^2.4.1", 1951 | "saslprep": "^1.0.3", 1952 | "socks": "^2.6.1" 1953 | } 1954 | }, 1955 | "mongodb-connection-string-url": { 1956 | "version": "2.4.1", 1957 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.4.1.tgz", 1958 | "integrity": "sha512-d5Kd2bVsKcSA7YI/yo57fSTtMwRQdFkvc5IZwod1RRxJtECeWPPSo7zqcUGJELifRA//Igs4spVtYAmvFCatug==", 1959 | "requires": { 1960 | "@types/whatwg-url": "^8.2.1", 1961 | "whatwg-url": "^11.0.0" 1962 | } 1963 | }, 1964 | "node-cron": { 1965 | "version": "3.0.0", 1966 | "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.0.tgz", 1967 | "integrity": "sha512-DDwIvvuCwrNiaU7HEivFDULcaQualDv7KoNlB/UU1wPW0n1tDEmBJKhEIE6DlF2FuoOHcNbLJ8ITL2Iv/3AWmA==", 1968 | "requires": { 1969 | "moment-timezone": "^0.5.31" 1970 | } 1971 | }, 1972 | "prettier": { 1973 | "version": "2.7.1", 1974 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", 1975 | "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==" 1976 | }, 1977 | "punycode": { 1978 | "version": "2.1.1", 1979 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1980 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 1981 | }, 1982 | "saslprep": { 1983 | "version": "1.0.3", 1984 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 1985 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 1986 | "optional": true, 1987 | "requires": { 1988 | "sparse-bitfield": "^3.0.3" 1989 | } 1990 | }, 1991 | "scrypt-js": { 1992 | "version": "3.0.1", 1993 | "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", 1994 | "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" 1995 | }, 1996 | "smart-buffer": { 1997 | "version": "4.2.0", 1998 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 1999 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" 2000 | }, 2001 | "socks": { 2002 | "version": "2.6.1", 2003 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", 2004 | "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", 2005 | "requires": { 2006 | "ip": "^1.1.5", 2007 | "smart-buffer": "^4.1.0" 2008 | } 2009 | }, 2010 | "sparse-bitfield": { 2011 | "version": "3.0.3", 2012 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 2013 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", 2014 | "optional": true, 2015 | "requires": { 2016 | "memory-pager": "^1.0.2" 2017 | } 2018 | }, 2019 | "tr46": { 2020 | "version": "3.0.0", 2021 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", 2022 | "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", 2023 | "requires": { 2024 | "punycode": "^2.1.1" 2025 | } 2026 | }, 2027 | "ts-node": { 2028 | "version": "10.4.0", 2029 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", 2030 | "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", 2031 | "dev": true, 2032 | "requires": { 2033 | "@cspotcode/source-map-support": "0.7.0", 2034 | "@tsconfig/node10": "^1.0.7", 2035 | "@tsconfig/node12": "^1.0.7", 2036 | "@tsconfig/node14": "^1.0.0", 2037 | "@tsconfig/node16": "^1.0.2", 2038 | "acorn": "^8.4.1", 2039 | "acorn-walk": "^8.1.1", 2040 | "arg": "^4.1.0", 2041 | "create-require": "^1.1.0", 2042 | "diff": "^4.0.1", 2043 | "make-error": "^1.1.1", 2044 | "yn": "3.1.1" 2045 | } 2046 | }, 2047 | "typescript": { 2048 | "version": "4.5.5", 2049 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", 2050 | "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", 2051 | "dev": true 2052 | }, 2053 | "webidl-conversions": { 2054 | "version": "7.0.0", 2055 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 2056 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" 2057 | }, 2058 | "whatwg-url": { 2059 | "version": "11.0.0", 2060 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", 2061 | "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", 2062 | "requires": { 2063 | "tr46": "^3.0.0", 2064 | "webidl-conversions": "^7.0.0" 2065 | } 2066 | }, 2067 | "ws": { 2068 | "version": "7.4.6", 2069 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", 2070 | "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", 2071 | "requires": {} 2072 | }, 2073 | "yn": { 2074 | "version": "3.1.1", 2075 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 2076 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 2077 | "dev": true 2078 | } 2079 | } 2080 | } 2081 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "intergalatic-indexer", 3 | "version": "1.0.0", 4 | "description": "Indexer for blockchain events", 5 | "main": "indexer.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "start": "node build/indexer.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/Polynomial-Protocol/intergalactic-indexer.git" 13 | }, 14 | "author": "phoenikx", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/Polynomial-Protocol/intergalactic-indexer/issues" 18 | }, 19 | "homepage": "https://github.com/Polynomial-Protocol/intergalactic-indexer#readme", 20 | "dependencies": { 21 | "dotenv": "^16.0.0", 22 | "ethers": "^5.5.4", 23 | "js-yaml": "^4.1.0", 24 | "kafkajs": "^1.16.0", 25 | "mongodb": "^4.3.1", 26 | "node-cron": "^3.0.0", 27 | "prettier": "^2.7.1" 28 | }, 29 | "devDependencies": { 30 | "ts-node": "^10.4.0", 31 | "typescript": "^4.5.5" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Optimistic Indexer 2 | 3 | ![GitHub Stars](https://img.shields.io/github/stars/Polynomial-Protocol/optimistic-indexer?style=social) 4 | ![GitHub Issues](https://img.shields.io/github/issues/Polynomial-Protocol/optimistic-indexer) 5 | ![GitHub License](https://img.shields.io/github/license/Polynomial-Protocol/optimistic-indexer) 6 | ![Version](https://img.shields.io/badge/version-1.0.0-blue) 7 | ![Built With](https://img.shields.io/badge/built%20with-Typescript-blue) 8 | ![Blockchain](https://img.shields.io/badge/blockchain-EVM--based-green) 9 | 10 | **Easy-to-Setup, Low Latency Indexer for L2s** 11 | Optimistic Indexer 12 | 13 | ## What is Optimistic Indexer? 14 | 15 | Optimistic Indexer is a Fast, Low Latency event indexer tailored for EVM-based blockchains. Written in TypeScript, it offers two concurrent modes: 16 | 17 | - **Point-to-Point:** Indexes each block as they are added. 18 | - **Reconciliation:** Catches up on any missed blocks. 19 | 20 | The indexer is non-opinionated, exporting events to a MongoDB NOSQL database. It's designed to be part of a larger service architecture that reads these events to serve to end-users. 21 | 22 | 23 | Optimistic Indexer 24 | 25 | ### 🌟 Features 26 | 27 | 1. Registering new contracts and specifying the events to listen to. 28 | 2. Reading past and live events from the chain and storing them in the database for querying. 29 | 3. Optionally pushing events to a Kafka topic. 30 | 31 | ## 🚀 Quick Start 32 | 33 | ### 🔍 Prerequisites 34 | Before running the indexer, make sure to install the following: 35 | 36 | 1. **Node.js** and npm installed or use the docker container 37 | 2. **MongoDB**: Install and run MongoDB 4.2+ or use a hosted MongoDB solution like MongoDB Atlas. 38 | 3. **Kafka (Optional)**: Install and run Kafka or use a hosted solution like Confluent if you wish to push events to a Kafka topic. 39 | 4. **Alchemy API Key**: Create an account on Alchemy to query the blockchain for events. Copy your API keys for both indexer and reconciliation tasks. 40 | 41 | ### 🛠️ Installation 42 | 43 | 1. Clone the repository 44 | ```bash 45 | git clone https://github.com/Polynomial-Protocol/intergalactic-indexer.git 46 | ``` 47 | 48 | 2. Navigate to the project directory 49 | ```bash 50 | cd intergalactic-indexer 51 | ``` 52 | 53 | 3. Install dependencies 54 | ```bash 55 | npm install 56 | ``` 57 | 58 | 4. Create a `.env` file and populate it with your settings. An example is given below: 59 | ```env 60 | MONGO_URI="mongodb://localhost:27017" # uri of mongodb installation 61 | APP_NAME="indexer" # anything that you want 62 | ALCHEMY_API_KEY_FOR_INDEXER="" # alchemy api key that will be used to listen for events using websockets 63 | ALCHEMY_API_KEY_FOR_RECONCILIATION="" # alchemy api key that will be used to run the reconciliation loop, you can use the same API key as above 64 | INDEXES_RELATIVE_PATH="artifacts/indexes.yaml" # this file is used to store all the mongodb indexes that have to be created, do not change it or else indexer will start running slowly due to slow db queries 65 | RECONCILIATION_CRON="*/2 * * * *" # cron expression for reconciliation, change it as needed 66 | DB_NAME="indexer" # name of the database in mongodb 67 | CONTRACTS_RELATIVE_PATH="artifacts/example-contract.yaml" # file where contracts and their index configuration are present 68 | NETWORK="optimism" # network for which the indexer will run 69 | KAFKA_API_KEY="" # kafka api key, only required if you wish to push events to a kafka topic 70 | KAFKA_API_SECRET="" # kafka api secret, only required if you wish to push events to a kafka topic 71 | KAFKA_BOOTSTRAP_SERVER="" # kafka bootstrap server, only required if you wish to push events to a kafka topic 72 | KAFKA_TOPIC=events # change it to point to correct name of topic name 73 | PUSH_EVENTS=false # set it to true if you wish to push events to a kafka topic 74 | ``` 75 | 5. Run the indexer 76 | ```bash 77 | npx ts-node src/indexer.ts 78 | ``` 79 | 80 | ## 🔧 Configuring Contracts 81 | 82 | Modify the configuration file specified by `CONTRACTS_RELATIVE_PATH` to include the smart contracts and events you wish to index. 83 | 84 | #### `example-contract.yaml` Breakdown 85 | 86 | - `identifier`: A unique name for the contract. 87 | - `abi`: The Application Binary Interface of the contract. It's an array of method and event descriptions. It is taken as string and ABI can be provided in JSON or by formatting it like [this](https://github.com/gnidan/abi-to-sol). Examples for both can be in artifacts folder 88 | - `startIdx`: Starting block number for indexing events. 89 | - `address`: Ethereum address where the contract is deployed. 90 | - `eventsToIndex`: Events your application should listen for. 91 | - `disabled`: Boolean flag indicating if the contract should be ignored. 92 | 93 | ## 💡 Additional Information 94 | 95 | #### 📈 Importance of an Indexer 96 | 97 | An indexer is indispensable for building robust backends for blockchain applications. Events emitted in transaction receipts contain vital information for applications, such as transfers of tokens in an NFT marketplace. 98 | 99 | #### 🚧 Limitations of the Current Indexer 100 | 101 | 1. Only supports the Optimism chain for now. 102 | 2. Event ordering is not guaranteed. The indexer uses websockets to listen for current events and a reconciliation loop to index events that might be missed. 103 | 3. Not optimized for performance; single-threaded. Not scalable for a large number of events 104 | 105 | #### 📗 Examples of Applications 106 | 107 | Almost all applications with a smart contract deployed on a blockchain could benefit from this indexer. Typically, dApps either use a decentralized indexer like The Graph or run an in-house indexer. 108 | 109 | #### 🤝 How an Open-Sourced Model Will Help 110 | 111 | Having an open-source indexer saves developers from reinventing the wheel, significantly cutting down on development time and cost. 112 | 113 | #### 🐳 Non-Kubernetes Deployments 114 | 115 | This project includes a Dockerfile for containerized deployment. Ensure Docker is installed if you're not using Kubernetes. 116 | 117 | 118 | ### 🌐 Community and Contributions 119 | 120 | Open to contributions, please check the CONTRIBUTING.md file for details. 121 | 122 | ### ⚖️ License 123 | 124 | This project is licensed under the MIT License - see the LICENSE.md file for details. 125 | 126 | --- 127 | 128 | Made with 🔴 by Polynomial Protocol. 129 | For any issues or contributions, please refer to our [GitHub repository](https://github.com/Polynomial-Protocol/intergalactic-indexer). 130 | 131 | Credits @phoenikx 132 | -------------------------------------------------------------------------------- /src/artifacts/example-contract-alternative.yaml: -------------------------------------------------------------------------------- 1 | - identifier: AAVE_L2_POOL 2 | abi: 3 | [ 4 | "event Supply( address indexed reserve,address user,address indexed onBehalfOf,uint256 amount,uint16 indexed referralCode )" 5 | ] 6 | startIdx: 109849585 7 | address: "0x794a61358D6845594F94dc1DB02A252b5b4814aD" 8 | eventsToIndex: 9 | - name: Supply 10 | arguments: 11 | - reserve 12 | - user 13 | - onBehalfOf 14 | - amount 15 | - referralCode 16 | -------------------------------------------------------------------------------- /src/artifacts/example-contract.yaml: -------------------------------------------------------------------------------- 1 | - identifier: OP_TOKEN 2 | 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":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","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":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint32","name":"pos","type":"uint32"}],"name":"checkpoints","outputs":[{"components":[{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"uint224","name":"votes","type":"uint224"}],"internalType":"struct ERC20Votes.Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"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":"amount","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":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}] 3 | startIdx: 109801544 4 | address: "0x4200000000000000000000000000000000000042" 5 | disabled: false 6 | eventsToIndex: 7 | - name: Transfer 8 | arguments: 9 | - from 10 | - to 11 | - value -------------------------------------------------------------------------------- /src/artifacts/indexes.yaml: -------------------------------------------------------------------------------- 1 | contracts: 2 | - name: unique_identifier_idx 3 | unique: true 4 | background: false 5 | spec: 6 | - identifier: 1 7 | - name: address_idx 8 | unique: false 9 | background: false 10 | spec: 11 | - address: 1 12 | 13 | events: 14 | - name: name_key_value_idx 15 | unique: false 16 | background: false 17 | spec: 18 | - name: 1 19 | - args.key: 1 20 | - args.value: 1 21 | - name: contract_name_idx 22 | unique: false 23 | background: false 24 | spec: 25 | - contractAddress: 1 26 | - name: 1 27 | - name: transaction_hash_log_index_idx 28 | unique: true 29 | background: false 30 | spec: 31 | - transactionHash: 1 32 | - logIndex: 1 -------------------------------------------------------------------------------- /src/dao/EventDAOImpl.ts: -------------------------------------------------------------------------------- 1 | import { EventDAO } from "./interface/EventDAO"; 2 | import { Collection, Db } from "mongodb"; 3 | import * as events from "events"; 4 | import { OptimisticEventDBO } from "../models/OptimisticEventDBO"; 5 | import { OptimisticContractService } from "../services/interfaces/OptimisticContractService"; 6 | 7 | export class EventDAOImpl implements EventDAO { 8 | private events: Collection; 9 | private sweepData: Collection; 10 | 11 | constructor(db: Db, private contractService: OptimisticContractService) { 12 | this.events = db.collection("events"); 13 | this.sweepData = db.collection("sweepData"); 14 | } 15 | 16 | async getLastBlockProcessedForEvent( 17 | contractIdentifier: string, 18 | eventName: string 19 | ): Promise { 20 | const doc = await this.sweepData.findOne({ 21 | contractIdentifier: contractIdentifier, 22 | eventName: eventName, 23 | }); 24 | if (doc === null) { 25 | const contract = await this.contractService.getContract( 26 | contractIdentifier 27 | ); 28 | return contract.startIdx; 29 | } 30 | 31 | return doc["blockNumber"]; 32 | } 33 | 34 | async save(OptimisticEventDBO: OptimisticEventDBO): Promise { 35 | try { 36 | await this.events.insertOne(OptimisticEventDBO); 37 | return true; 38 | } catch (error) { 39 | console.error("Could not save event due to error: %s", error); 40 | return false; 41 | } 42 | } 43 | 44 | async updateLastProcessedBlockForEvent( 45 | identifier: string, 46 | name: string, 47 | currentBlockNumber: number 48 | ): Promise { 49 | await this.sweepData.updateOne( 50 | { contractIdentifier: identifier, eventName: name }, 51 | { $set: { blockNumber: currentBlockNumber } }, 52 | { upsert: true } 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/dao/OptimisticContractDAOImpl.ts: -------------------------------------------------------------------------------- 1 | import {OptimisticContract} from "../models/OptimisticContract"; 2 | import {OptimisticContractDAO} from "./interface/OptimisticContractDAO"; 3 | import {Collection, Db, Document, Filter} from "mongodb"; 4 | import {WithId} from "mongodb/mongodb.ts34"; 5 | 6 | export class OptimisticContractDAOImpl implements OptimisticContractDAO { 7 | private collection: Collection; 8 | private static COLLECTION_NAME = "contracts"; 9 | 10 | private static getContractFromDBObject(document: WithId): OptimisticContract { 11 | return { 12 | identifier: document['identifier'], 13 | abi: document['abi'], 14 | address: document['address'], 15 | eventsToIndex: document['eventsToIndex'], 16 | startIdx: document['startIdx'], 17 | disabled: document['disabled'] || false 18 | } 19 | } 20 | 21 | public constructor(database: Db) { 22 | this.collection = database.collection(OptimisticContractDAOImpl.COLLECTION_NAME); 23 | } 24 | 25 | async query(criteria: Filter): Promise> { 26 | const result = await this.collection.find({disabled: {"$ne": true}}); 27 | let contracts = await result.toArray(); 28 | return contracts.map((value) => { 29 | return OptimisticContractDAOImpl.getContractFromDBObject(value); 30 | }); 31 | } 32 | 33 | async get(identifier: string): Promise { 34 | const result = await this.collection.findOne({identifier: identifier}); 35 | if (result) { 36 | return { 37 | identifier: result['identifier'], 38 | abi: result['abi'], 39 | address: result['address'], 40 | eventsToIndex: result['eventsToIndex'], 41 | startIdx: result['startIdx'] 42 | }; 43 | } 44 | return Promise.reject('No such contract found with identifier: ' + identifier); 45 | } 46 | 47 | async save(contract: OptimisticContract): Promise { 48 | const result = await this.collection.replaceOne({identifier: contract.identifier}, contract, {upsert: true}) 49 | return result.modifiedCount == 1 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/dao/interface/EventDAO.ts: -------------------------------------------------------------------------------- 1 | import { OptimisticEventDBO } from "../../models/OptimisticEventDBO"; 2 | 3 | export interface EventDAO { 4 | getLastBlockProcessedForEvent( 5 | contractIdentifier: string, 6 | eventName: string 7 | ): Promise; 8 | 9 | save(OptimisticEventDBO: OptimisticEventDBO): Promise; 10 | 11 | updateLastProcessedBlockForEvent( 12 | contractIdentifier: string, 13 | eventName: string, 14 | currentBlockNumber: number 15 | ): Promise; 16 | } 17 | -------------------------------------------------------------------------------- /src/dao/interface/OptimisticContractDAO.ts: -------------------------------------------------------------------------------- 1 | import {OptimisticContract} from "../../models/OptimisticContract"; 2 | import {Filter} from "mongodb/mongodb.ts34"; 3 | 4 | export interface OptimisticContractDAO { 5 | save(contract: OptimisticContract): Promise; 6 | 7 | get(identifier: string): Promise; 8 | 9 | query(criteria: Filter): Promise>; 10 | } -------------------------------------------------------------------------------- /src/helpers/ContractRegistrationHelper.ts: -------------------------------------------------------------------------------- 1 | export interface ContractRegistrationHelper { 2 | registerContracts(): Promise>; 3 | } -------------------------------------------------------------------------------- /src/helpers/ContractRegistrationServiceLocalImpl.ts: -------------------------------------------------------------------------------- 1 | import { ContractRegistrationHelper } from "./ContractRegistrationHelper"; 2 | import { OptimisticContractService } from "../services/interfaces/OptimisticContractService"; 3 | import { OptimisticContract } from "../models/OptimisticContract"; 4 | 5 | const yaml = require("js-yaml"); 6 | const fs = require("fs"); 7 | 8 | export class ContractRegistrationServiceLocalImpl 9 | implements ContractRegistrationHelper 10 | { 11 | public constructor( 12 | private contractsPath: string, 13 | private contractService: OptimisticContractService 14 | ) {} 15 | 16 | async registerContracts(): Promise { 17 | const contracts: OptimisticContract[] = yaml.load( 18 | fs.readFileSync(this.contractsPath, "utf8") 19 | ); 20 | let identifiers: Set = new Set(); 21 | 22 | for (let contract of contracts) { 23 | let contractFromDB: OptimisticContract; 24 | try { 25 | contractFromDB = await this.contractService.getContract( 26 | contract.identifier 27 | ); 28 | contractFromDB.abi = contract.abi; 29 | contractFromDB.address = contract.address; 30 | contractFromDB.eventsToIndex = contract.eventsToIndex; 31 | contractFromDB.disabled = contract.disabled; 32 | } catch (e) { 33 | contractFromDB = { 34 | identifier: contract.identifier, 35 | abi: contract.abi, 36 | startIdx: contract.startIdx, 37 | address: contract.address, 38 | eventsToIndex: contract.eventsToIndex, 39 | disabled: contract.disabled, 40 | }; 41 | } 42 | 43 | await this.contractService.save(contractFromDB); 44 | 45 | identifiers.add(contract.identifier); 46 | } 47 | return identifiers; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/helpers/ContractRegistrationServiceRemoteImpl.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polynomial-Protocol/optimistic-indexer/34ca87c44e5494f8fa26ee4ba97858e29f029015/src/helpers/ContractRegistrationServiceRemoteImpl.ts -------------------------------------------------------------------------------- /src/indexer.ts: -------------------------------------------------------------------------------- 1 | import { OptimisticContractDAOImpl } from "./dao/OptimisticContractDAOImpl"; 2 | import { Db, MongoClient } from "mongodb"; 3 | import { IndexManager } from "./services/interfaces/IndexManager"; 4 | import { IndexManagerImpl } from "./services/IndexManagerImpl"; 5 | import * as path from "path"; 6 | import { ContractRegistrationServiceLocalImpl } from "./helpers/ContractRegistrationServiceLocalImpl"; 7 | import { OptimisticContractServiceImpl } from "./services/OptimisticContractServiceImpl"; 8 | import { Contract, ethers, providers } from "ethers"; 9 | import { EventServiceImpl } from "./services/EventServiceImpl"; 10 | import { EventDAOImpl } from "./dao/EventDAOImpl"; 11 | import { ChainServiceImpl } from "./services/ChainServiceImpl"; 12 | import { PointToPointIndexerServiceImpl } from "./services/PointToPointIndexerServiceImpl"; 13 | import { SweepingIndexerServiceImpl } from "./services/SweepingIndexerServiceImpl"; 14 | import { OptimisticContractService } from "./services/interfaces/OptimisticContractService"; 15 | import { EventService } from "./services/interfaces/EventService"; 16 | import { Kafka } from "kafkajs"; 17 | import { 18 | KafkaQueueServiceImpl, 19 | MockQueueServiceImpl, 20 | } from "./queue/queue.service"; 21 | 22 | let cron = require("node-cron"); 23 | const yaml = require("js-yaml"); 24 | const fs = require("fs"); 25 | require("dotenv").config(); 26 | 27 | const MONGO_URI = process.env.MONGO_URI!; 28 | const CONTRACTS_FILE_PATH = path.resolve( 29 | __dirname, 30 | process.env.CONTRACTS_RELATIVE_PATH! 31 | ); 32 | const NETWORK = process.env.NETWORK!; 33 | const ALCHEMY_API_KEY_FOR_INDEXER = process.env.ALCHEMY_API_KEY_FOR_INDEXER!; 34 | const ALCHEMY_API_KEY_FOR_RECONCILIATION = 35 | process.env.ALCHEMY_API_KEY_FOR_RECONCILIATION!; 36 | const DB_NAME = process.env.DB_NAME!; 37 | const INDEXES_FILE_PATH = path.resolve( 38 | __dirname, 39 | process.env.INDEXES_RELATIVE_PATH! 40 | ); 41 | const RECONCILIATION_CRON = process.env.RECONCILIATION_CRON || "*/5 * * * *"; 42 | const EXPECTED_PONG_BACK = 15000; 43 | const KEEP_ALIVE_CHECK_INTERVAL = 7500; 44 | const KAFKA_CLIENT_ID = process.env.KAFKA_CLIENT_ID!; 45 | const KAFKA_BOOTSTRAP_SERVER = process.env.KAFKA_BOOTSTRAP_SERVER!; 46 | const KAFKA_API_KEY = process.env.KAFKA_API_KEY!; 47 | const KAFKA_API_SECRET = process.env.KAFKA_API_SECRET!; 48 | export const KAFKA_TOPIC = process.env.KAFKA_TOPIC!; 49 | 50 | async function setupKafka() { 51 | const kafka = await new Kafka({ 52 | clientId: KAFKA_CLIENT_ID, 53 | brokers: [KAFKA_BOOTSTRAP_SERVER], 54 | ssl: true, 55 | sasl: { 56 | mechanism: "plain", 57 | username: KAFKA_API_KEY, 58 | password: KAFKA_API_SECRET, 59 | }, 60 | }); 61 | 62 | const kafkaProducer = kafka.producer(); 63 | await kafkaProducer.connect(); 64 | console.log("Kafka Producer connected."); 65 | return { 66 | kafkaProducer, 67 | }; 68 | } 69 | 70 | export const getProvider = ( 71 | network: string, 72 | apiKey: string 73 | ): providers.WebSocketProvider => { 74 | const provider = ethers.providers.AlchemyProvider.getWebSocketProvider( 75 | network, 76 | apiKey 77 | ); 78 | let pingTimeout: any = null; 79 | let keepAliveInterval: any = null; 80 | 81 | provider._websocket.on("open", () => { 82 | keepAliveInterval = setInterval(() => { 83 | provider._websocket.ping(); 84 | 85 | pingTimeout = setTimeout(() => { 86 | provider._websocket.terminate(); 87 | }, EXPECTED_PONG_BACK); 88 | }, KEEP_ALIVE_CHECK_INTERVAL); 89 | }); 90 | 91 | provider._websocket.on("close", () => { 92 | console.error( 93 | "The websocket connection was closed, killing and restarting service..." 94 | ); 95 | clearInterval(keepAliveInterval); 96 | clearTimeout(pingTimeout); 97 | process.exit(1); 98 | }); 99 | 100 | provider._websocket.on("pong", () => { 101 | clearInterval(pingTimeout); 102 | }); 103 | 104 | return provider; 105 | }; 106 | 107 | async function getDatabase(mongoUri: string, dbName: string): Promise { 108 | const client = new MongoClient(mongoUri); 109 | await client.connect(); 110 | return client.db(dbName); 111 | } 112 | 113 | async function createIndexes( 114 | filePath: string, 115 | db: Db, 116 | indexManager: IndexManager 117 | ): Promise> { 118 | const collectionIndexes = yaml.load(fs.readFileSync(filePath, "utf8")); 119 | return await indexManager.ensureIndexes(collectionIndexes); 120 | } 121 | 122 | async function reconcilePastEvents( 123 | contractService: OptimisticContractService, 124 | eventService: EventService 125 | ) { 126 | let chainProvider = getProvider(NETWORK, ALCHEMY_API_KEY_FOR_RECONCILIATION); 127 | let chainService = new ChainServiceImpl(chainProvider); 128 | let contracts = await contractService.getActiveContracts(); 129 | for (let OptimisticContract of contracts) { 130 | const chainContract = new Contract( 131 | OptimisticContract.address, 132 | OptimisticContract.abi, 133 | chainProvider 134 | ); 135 | await new SweepingIndexerServiceImpl( 136 | OptimisticContract, 137 | chainContract, 138 | eventService, 139 | chainService 140 | ).reconcilePastEvents(); 141 | } 142 | } 143 | 144 | async function runIndexer( 145 | db: Db, 146 | contractService: OptimisticContractService, 147 | eventService: EventService 148 | ) { 149 | const chainProvider = getProvider(NETWORK, ALCHEMY_API_KEY_FOR_INDEXER); 150 | const chainService = new ChainServiceImpl(chainProvider); 151 | const pointToPointIndexerService = new PointToPointIndexerServiceImpl( 152 | contractService, 153 | eventService, 154 | chainService, 155 | chainProvider 156 | ); 157 | await pointToPointIndexerService.startListeningForEventsFromActiveContracts(); 158 | } 159 | 160 | async function actrunCronForReconcilingPastEvents( 161 | contractService: OptimisticContractService, 162 | eventService: EventService 163 | ) { 164 | cron.schedule(RECONCILIATION_CRON, () => { 165 | reconcilePastEvents(contractService, eventService); 166 | }); 167 | } 168 | 169 | async function main() { 170 | const db = await getDatabase(MONGO_URI, DB_NAME); 171 | console.log("Connected to database: %s", db.databaseName); 172 | 173 | const indexesCreated = await createIndexes( 174 | INDEXES_FILE_PATH, 175 | db, 176 | new IndexManagerImpl(db) 177 | ); 178 | console.log("Created indexes: [%s]", indexesCreated.toString()); 179 | 180 | const contractService = new OptimisticContractServiceImpl( 181 | new OptimisticContractDAOImpl(db) 182 | ); 183 | 184 | let queueService; 185 | if (process.env.PUSH_TO_KAFKA === "true") { 186 | const { kafkaProducer } = await setupKafka(); 187 | queueService = new KafkaQueueServiceImpl(kafkaProducer); 188 | } else { 189 | queueService = new MockQueueServiceImpl(); 190 | } 191 | 192 | const eventService = new EventServiceImpl( 193 | new EventDAOImpl(db, contractService), 194 | queueService 195 | ); 196 | 197 | const contractRegistrationService = new ContractRegistrationServiceLocalImpl( 198 | CONTRACTS_FILE_PATH, 199 | contractService 200 | ); 201 | await contractRegistrationService.registerContracts(); 202 | 203 | console.log("Reconciling past events during boot up..."); 204 | 205 | await reconcilePastEvents(contractService, eventService); 206 | 207 | await runIndexer(db, contractService, eventService); 208 | 209 | await actrunCronForReconcilingPastEvents(contractService, eventService); 210 | } 211 | 212 | main().catch((error) => { 213 | console.log(error); 214 | process.exit(1); 215 | }); 216 | -------------------------------------------------------------------------------- /src/models/OptimisticContract.ts: -------------------------------------------------------------------------------- 1 | export interface Event { 2 | name: string; 3 | signature: string; 4 | } 5 | 6 | export interface OptimisticContract { 7 | identifier: string; 8 | abi: string[]; 9 | address: string; 10 | eventsToIndex: OptimisticEvent[]; 11 | startIdx: number; 12 | disabled?: boolean; 13 | } 14 | 15 | export interface OptimisticEvent { 16 | name: string 17 | arguments: string[] 18 | } -------------------------------------------------------------------------------- /src/models/OptimisticEventDBO.ts: -------------------------------------------------------------------------------- 1 | import {ReconcilerType} from "./ReconcilerType"; 2 | 3 | export interface OptimisticEventDBO { 4 | id?: string; 5 | contractAddress: string; 6 | transactionHash: string; 7 | name: string; 8 | timestamp?: number; 9 | args?: EventArg[]; 10 | blockNumber: number; 11 | logIndex: number; 12 | signature: string | undefined; 13 | savedBy: ReconcilerType; 14 | } 15 | 16 | export interface EventArg { 17 | key: string 18 | value: any 19 | } -------------------------------------------------------------------------------- /src/models/ReconcilerType.ts: -------------------------------------------------------------------------------- 1 | export enum ReconcilerType { 2 | SWEEP, 3 | POINT 4 | } -------------------------------------------------------------------------------- /src/queue/queue.service.ts: -------------------------------------------------------------------------------- 1 | import { KAFKA_TOPIC } from "../indexer"; 2 | import { Producer } from "kafkajs"; 3 | 4 | export interface QueueServiceInterface { 5 | pushEvent(key: string, value: string): Promise; 6 | } 7 | 8 | export class KafkaQueueServiceImpl implements QueueServiceInterface { 9 | constructor(private kafkaProducer: Producer) {} 10 | 11 | async pushEvent(key: string, value: string): Promise { 12 | return await this.kafkaProducer.send({ 13 | topic: KAFKA_TOPIC, 14 | messages: [ 15 | { 16 | key: key, 17 | value: value, 18 | }, 19 | ], 20 | }); 21 | } 22 | } 23 | 24 | export class MockQueueServiceImpl implements QueueServiceInterface { 25 | async pushEvent(key: string, value: string): Promise { 26 | return "Not implemented"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/services/ChainServiceImpl.ts: -------------------------------------------------------------------------------- 1 | import {ChainService} from "./interfaces/ChainService"; 2 | import {ethers} from "ethers"; 3 | 4 | export class ChainServiceImpl implements ChainService { 5 | delayFunc = (ms: number) => new Promise(res => setTimeout(res, ms)); 6 | 7 | constructor(private provider: ethers.providers.Provider) { 8 | } 9 | 10 | async getCurrentBlockNumber(): Promise { 11 | return await this.provider.getBlockNumber(); 12 | } 13 | 14 | async getTimestamp(blockNumber: number): Promise { 15 | let block: any = await this.provider.getBlock(blockNumber); 16 | if (!block) { 17 | await this.delayFunc(5000); 18 | block = await this.provider.getBlock(blockNumber); 19 | } 20 | 21 | if (block) { 22 | return block.timestamp; 23 | } 24 | return 0; 25 | } 26 | 27 | async getTimestampByHash(transactionHash: string): Promise { 28 | let block: any = await this.provider.getTransaction(transactionHash); 29 | if (!block) { 30 | await this.delayFunc(5000); 31 | block = await this.provider.getTransaction(transactionHash); 32 | } 33 | 34 | if (block) { 35 | return block.timestamp 36 | } 37 | return 0; 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/services/EventReconciliationServiceImpl.ts: -------------------------------------------------------------------------------- 1 | import { EventReconciliationService } from "./interfaces/EventReconciliationService"; 2 | import { ReconcilerType } from "../models/ReconcilerType"; 3 | import { EventService } from "./interfaces/EventService"; 4 | import { ChainService } from "./interfaces/ChainService"; 5 | import { Contract, ethers } from "ethers"; 6 | import { 7 | OptimisticContract, 8 | OptimisticEvent, 9 | } from "../models/OptimisticContract"; 10 | import { EventArg, OptimisticEventDBO } from "../models/OptimisticEventDBO"; 11 | 12 | export class EventReconciliationServiceImpl 13 | implements EventReconciliationService 14 | { 15 | constructor( 16 | private eventService: EventService, 17 | private chainService: ChainService, 18 | private contract: Contract, 19 | private OptimisticContract: OptimisticContract, 20 | private OptimisticEvent: OptimisticEvent 21 | ) {} 22 | 23 | public static getArgs( 24 | argValues: ethers.utils.Result, 25 | args: string[] 26 | ): Array { 27 | let eventArgs = new Array(); 28 | for (const argName of args) { 29 | let val = argValues[argName]; 30 | if (val != undefined) { 31 | eventArgs.push({ key: argName, value: val }); 32 | } 33 | } 34 | return eventArgs; 35 | } 36 | 37 | private async convertEvent( 38 | event: ethers.Event, 39 | contractAddress: string, 40 | reconcilerType: ReconcilerType 41 | ): Promise { 42 | let timestamp = await this.chainService.getTimestamp(event.blockNumber); 43 | return { 44 | name: event.event!, 45 | contractAddress: contractAddress, 46 | transactionHash: event.transactionHash, 47 | blockNumber: event.blockNumber, 48 | logIndex: event.logIndex, 49 | signature: event.eventSignature, 50 | timestamp: timestamp, 51 | args: EventReconciliationServiceImpl.getArgs( 52 | event.args!, 53 | this.OptimisticEvent.arguments 54 | ), 55 | savedBy: reconcilerType, 56 | }; 57 | } 58 | 59 | async reconcileBatch(fromBlock: number, toBlock: number) { 60 | console.log( 61 | `Reconciling ${this.OptimisticEvent.name} from ${fromBlock} to ${toBlock}` 62 | ); 63 | let events = await this.contract.queryFilter( 64 | this.contract.filters[this.OptimisticEvent.name](), 65 | fromBlock, 66 | toBlock 67 | ); 68 | 69 | if (events.length > 0) { 70 | for (let event of events) { 71 | const OptimisticEventDBO = await this.convertEvent( 72 | event, 73 | this.OptimisticContract.address, 74 | ReconcilerType.SWEEP 75 | ); 76 | try { 77 | await this.eventService.save(OptimisticEventDBO); 78 | } catch (error: any) { 79 | console.log("Could not save event: %s", error.toString()); 80 | } 81 | } 82 | } 83 | 84 | console.log( 85 | "[%s] Reconciled %s events of type: %s for contract: %s from block: %s to block: %s", 86 | new Date(Date.now()).toLocaleString(), 87 | events.length, 88 | this.OptimisticEvent.name, 89 | this.OptimisticContract.identifier, 90 | fromBlock, 91 | toBlock 92 | ); 93 | await this.eventService.updateLastBlockProcessedForEvent( 94 | this.OptimisticContract.identifier, 95 | this.OptimisticEvent.name, 96 | toBlock 97 | ); 98 | } 99 | 100 | async reconcile(): Promise { 101 | let lastBlockNumber = await this.eventService.getLastBlockProcessedForEvent( 102 | this.OptimisticContract.identifier, 103 | this.OptimisticEvent.name 104 | ); 105 | const currentBlockNumber = await this.chainService.getCurrentBlockNumber(); 106 | 107 | const batchSize = 86400; 108 | 109 | while (lastBlockNumber < currentBlockNumber) { 110 | let toBlock = lastBlockNumber + batchSize; 111 | if (toBlock > currentBlockNumber) { 112 | toBlock = currentBlockNumber; 113 | } 114 | await this.reconcileBatch(lastBlockNumber, toBlock); 115 | lastBlockNumber = toBlock; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/services/EventServiceImpl.ts: -------------------------------------------------------------------------------- 1 | import { EventService } from "./interfaces/EventService"; 2 | import { EventDAO } from "../dao/interface/EventDAO"; 3 | import { OptimisticEventDBO } from "../models/OptimisticEventDBO"; 4 | import { QueueServiceInterface } from "../queue/queue.service"; 5 | 6 | export class EventServiceImpl implements EventService { 7 | constructor( 8 | private eventDAO: EventDAO, 9 | private queueService: QueueServiceInterface 10 | ) {} 11 | 12 | async getLastBlockProcessedForEvent( 13 | contractIdentifier: string, 14 | eventName: string 15 | ): Promise { 16 | return await this.eventDAO.getLastBlockProcessedForEvent( 17 | contractIdentifier, 18 | eventName 19 | ); 20 | } 21 | 22 | async save(OptimisticEventDBO: OptimisticEventDBO): Promise { 23 | const success = await this.eventDAO.save(OptimisticEventDBO); 24 | const { contractAddress, name } = OptimisticEventDBO; 25 | if (success) { 26 | const response = await this.queueService.pushEvent( 27 | `${contractAddress}-${name}`, 28 | JSON.stringify(OptimisticEventDBO) 29 | ); 30 | console.log(response); 31 | } 32 | return success; 33 | } 34 | 35 | async updateLastBlockProcessedForEvent( 36 | identifier: string, 37 | name: string, 38 | currentBlockNumber: number 39 | ): Promise { 40 | return await this.eventDAO.updateLastProcessedBlockForEvent( 41 | identifier, 42 | name, 43 | currentBlockNumber 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/services/IndexManagerImpl.ts: -------------------------------------------------------------------------------- 1 | import {CollectionIndex, Index, IndexManager} from "./interfaces/IndexManager"; 2 | import {Db} from "mongodb"; 3 | 4 | export class IndexManagerImpl implements IndexManager { 5 | public constructor(private db: Db) { 6 | } 7 | 8 | async ensureIndexes(collectionIndex: CollectionIndex): Promise> { 9 | let indexNames = new Array(); 10 | for (let collectionIndexKey in collectionIndex) { 11 | const indexes: Index[] = collectionIndex[collectionIndexKey] 12 | for (let index of indexes) { 13 | const indexName = await this.db.collection(collectionIndexKey).createIndex( 14 | index.spec, {background: index.background, unique: index.unique}); 15 | indexNames.push(indexName); 16 | } 17 | } 18 | 19 | return Promise.resolve(indexNames); 20 | } 21 | } -------------------------------------------------------------------------------- /src/services/OptimisticContractServiceImpl.ts: -------------------------------------------------------------------------------- 1 | import {OptimisticContractService} from "./interfaces/OptimisticContractService"; 2 | import {OptimisticContract} from "../models/OptimisticContract"; 3 | import {OptimisticContractDAO} from "../dao/interface/OptimisticContractDAO"; 4 | 5 | export class OptimisticContractServiceImpl implements OptimisticContractService { 6 | 7 | public constructor(private dao: OptimisticContractDAO) { 8 | } 9 | 10 | async disableContract(identifier: string): Promise { 11 | let contract = await this.getContract(identifier); 12 | contract.disabled = true; 13 | await this.save(contract); 14 | 15 | return true; 16 | } 17 | 18 | async getContract(identifier: string): Promise { 19 | return await this.dao.get(identifier); 20 | } 21 | 22 | async save(contract: OptimisticContract): Promise { 23 | const id = await this.dao.save(contract); 24 | return Promise.resolve(id); 25 | } 26 | 27 | async getActiveContracts(): Promise> { 28 | return await this.dao.query({disabled: false}); 29 | } 30 | } -------------------------------------------------------------------------------- /src/services/PointToPointIndexerServiceImpl.ts: -------------------------------------------------------------------------------- 1 | import {PointToPointIndexerService} from "./interfaces/PointToPointIndexerService"; 2 | import {Contract, ethers} from "ethers"; 3 | import {OptimisticContractService} from "./interfaces/OptimisticContractService"; 4 | import {EventService} from "./interfaces/EventService"; 5 | import {ReconcilerType} from "../models/ReconcilerType"; 6 | import {EventReconciliationServiceImpl} from "./EventReconciliationServiceImpl"; 7 | import {ChainService} from "./interfaces/ChainService"; 8 | 9 | export class PointToPointIndexerServiceImpl implements PointToPointIndexerService { 10 | private contracts: Map; 11 | 12 | public constructor(private contractService: OptimisticContractService, 13 | private eventService: EventService, 14 | private chainService: ChainService, 15 | private provider: ethers.providers.Provider) { 16 | this.contracts = new Map(); 17 | } 18 | 19 | public async disableContract(identifier: string): Promise { 20 | const contract = this.contracts.get(identifier); 21 | if (contract === undefined) { 22 | return false; 23 | } 24 | 25 | const success = await this.contractService.disableContract(identifier); 26 | if (success) { 27 | await contract.removeAllListeners(); 28 | return true; 29 | } 30 | return false; 31 | } 32 | 33 | 34 | public async startListeningForEventsFromActiveContracts() { 35 | const contracts = await this.contractService.getActiveContracts(); 36 | for (let OptimisticContract of contracts) { 37 | const blockChainContract = new Contract(OptimisticContract.address, OptimisticContract.abi, this.provider); 38 | OptimisticContract.eventsToIndex.forEach(evt => { 39 | console.log("[%s] [Point to Point Indexer] => Running for Event: %s from Contract: %s, address: %s", 40 | new Date(Date.now()).toLocaleString(), evt.name, OptimisticContract.identifier, OptimisticContract.address) 41 | blockChainContract.on(evt.name, async (...args) => { 42 | console.log("[%s] [Point to Point Indexer]: Received event of type: %s from contract: %s", 43 | new Date(Date.now()).toLocaleString(), evt.name, OptimisticContract.identifier); 44 | let event: ethers.Event = args[args.length - 1]; 45 | let timestamp = await this.chainService.getTimestamp(event.blockNumber); 46 | console.log("Transaction hash: %s Timestamp: %s", event.transactionHash, timestamp); 47 | await this.eventService.save({ 48 | name: evt.name, 49 | transactionHash: event.transactionHash, 50 | contractAddress: OptimisticContract.address, 51 | savedBy: ReconcilerType.POINT, 52 | blockNumber: event.blockNumber, 53 | timestamp: timestamp, 54 | logIndex: event.logIndex, 55 | signature: event.eventSignature, 56 | args: EventReconciliationServiceImpl.getArgs(event.args!, evt.arguments) 57 | }) 58 | }); 59 | }); 60 | this.contracts.set(OptimisticContract.identifier, blockChainContract); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/services/SweepingIndexerServiceImpl.ts: -------------------------------------------------------------------------------- 1 | import {OptimisticContract} from "../models/OptimisticContract"; 2 | import {SweepingIndexerService} from "./interfaces/SweepingIndexerService"; 3 | import {EventReconciliationServiceImpl} from "./EventReconciliationServiceImpl"; 4 | import {ChainService} from "./interfaces/ChainService"; 5 | import {EventService} from "./interfaces/EventService"; 6 | import {Contract} from "ethers"; 7 | 8 | export class SweepingIndexerServiceImpl implements SweepingIndexerService { 9 | constructor(private OptimisticContract: OptimisticContract, private contract: Contract, private eventService: EventService, 10 | private chainService: ChainService) { 11 | } 12 | 13 | 14 | async reconcilePastEvents(): Promise { 15 | for (let event of this.OptimisticContract.eventsToIndex) { 16 | const eventReconciliationService = new EventReconciliationServiceImpl(this.eventService, this.chainService, 17 | this.contract, this.OptimisticContract, event); 18 | await eventReconciliationService.reconcile(); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/services/interfaces/ChainService.ts: -------------------------------------------------------------------------------- 1 | export interface ChainService { 2 | getCurrentBlockNumber(): Promise; 3 | 4 | getTimestamp(blockNumber: number): Promise; 5 | 6 | getTimestampByHash(transactionHash: string): Promise; 7 | } -------------------------------------------------------------------------------- /src/services/interfaces/EventReconciliationService.ts: -------------------------------------------------------------------------------- 1 | export interface EventReconciliationService { 2 | reconcile(): Promise; 3 | } -------------------------------------------------------------------------------- /src/services/interfaces/EventService.ts: -------------------------------------------------------------------------------- 1 | import { OptimisticEventDBO } from "../../models/OptimisticEventDBO"; 2 | 3 | export interface EventService { 4 | getLastBlockProcessedForEvent( 5 | contractIdentifier: string, 6 | eventName: string 7 | ): Promise; 8 | 9 | save(OptimisticEventDBO: OptimisticEventDBO): Promise; 10 | 11 | updateLastBlockProcessedForEvent( 12 | identifier: string, 13 | name: string, 14 | currentBlockNumber: number 15 | ): Promise; 16 | } 17 | -------------------------------------------------------------------------------- /src/services/interfaces/IndexManager.ts: -------------------------------------------------------------------------------- 1 | export interface CollectionIndex { 2 | [key: string]: Index[] 3 | } 4 | 5 | export interface Index { 6 | name: string 7 | unique: boolean 8 | background: boolean 9 | spec: [IndexField] 10 | } 11 | 12 | export interface IndexField { 13 | [key: string]: -1 | 1 | 0 14 | } 15 | 16 | export interface IndexManager { 17 | ensureIndexes(collectionIndex: CollectionIndex): Promise>; 18 | } -------------------------------------------------------------------------------- /src/services/interfaces/OptimisticContractService.ts: -------------------------------------------------------------------------------- 1 | import {OptimisticContract} from "../../models/OptimisticContract"; 2 | 3 | export interface OptimisticContractService { 4 | save(contract: OptimisticContract): Promise; 5 | 6 | getContract(identifier: string): Promise; 7 | 8 | disableContract(identifier: string): Promise; 9 | 10 | getActiveContracts(): Promise>; 11 | } -------------------------------------------------------------------------------- /src/services/interfaces/PointToPointIndexerService.ts: -------------------------------------------------------------------------------- 1 | export interface PointToPointIndexerService { 2 | disableContract(identifier: string): Promise; 3 | 4 | startListeningForEventsFromActiveContracts(): Promise; 5 | } -------------------------------------------------------------------------------- /src/services/interfaces/SweepingIndexerService.ts: -------------------------------------------------------------------------------- 1 | export interface SweepingIndexerService { 2 | reconcilePastEvents(): Promise; 3 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2016", 4 | "module": "commonjs", 5 | "outDir": "build", 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "esModuleInterop": true 9 | }, 10 | "exclude": [ 11 | "node_modules" 12 | ], 13 | "include": [ 14 | "src/**/*" 15 | ] 16 | } --------------------------------------------------------------------------------