diff --git a/Unichain/unichain-testnet-uniswap-v3/.github/workflows/pr.yml b/Unichain/unichain-testnet-uniswap-v3/.github/workflows/pr.yml new file mode 100644 index 00000000..a7cf1a66 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/.github/workflows/pr.yml @@ -0,0 +1,20 @@ +name: PR +on: + pull_request: + paths-ignore: + - ".github/workflows/**" +jobs: + pr: + name: pr + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup Node.js environment + uses: actions/setup-node@v2 + with: + node-version: 16 + - run: yarn + - name: Codegen + run: yarn codegen + - name: Build + run: yarn build diff --git a/Unichain/unichain-testnet-uniswap-v3/.gitignore b/Unichain/unichain-testnet-uniswap-v3/.gitignore new file mode 100644 index 00000000..53b19635 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/.gitignore @@ -0,0 +1,60 @@ +# These are some examples of commonly ignored file patterns. +# You should customize this list as applicable to your project. +# Learn more about .gitignore: +# https://www.atlassian.com/git/tutorials/saving-changes/gitignore + +# Node artifact files +node_modules/ +dist/ + +# lock files +yarn.lock +package-lock.json + +# Compiled Java class files +*.class + +# Compiled Python bytecode +*.py[cod] + +# Log files +*.log + +# Package files +*.jar + +# Generated files +target/ +dist/ +src/types +project.yaml + +# JetBrains IDE +.idea/ + +# Unit test reports +TEST*.xml + +# Generated by MacOS +.DS_Store + +# Generated by Windows +Thumbs.db + +# Applications +*.app +*.exe +*.war + +# Large media files +*.mp4 +*.tiff +*.avi +*.flv +*.mov +*.wmv + +.data +.yarn + +.DS_Store diff --git a/Unichain/unichain-testnet-uniswap-v3/LICENSE b/Unichain/unichain-testnet-uniswap-v3/LICENSE new file mode 100644 index 00000000..f168fbe1 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/LICENSE @@ -0,0 +1,21 @@ +MIT LICENSE + +Copyright 2020-2024 SubQuery Pte Ltd authors & contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Unichain/unichain-testnet-uniswap-v3/README.md b/Unichain/unichain-testnet-uniswap-v3/README.md new file mode 100644 index 00000000..589735ea --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/README.md @@ -0,0 +1,60 @@ +# SubQuery - Example Uniswap Exchange-V3 Project for Unichain Testnet + +[SubQuery](https://subquery.network) is a fast, flexible, and reliable open-source data indexer that provides you with custom APIs for your web3 project across all of our supported networks. To learn about how to get started with SubQuery, [visit our docs](https://academy.subquery.network). + +**This SubQuery project indexes the standard Uniswap Exchange-V3 project on Unichain Testnet** + +## Start + +First, install SubQuery CLI globally on your terminal by using NPM `npm install -g @subql/cli` + +You can either clone this GitHub repo, or use the `subql` CLI to bootstrap a clean project in the network of your choosing by running `subql init` and following the prompts. + +Don't forget to install dependencies with `npm install` or `yarn install`! + +## Editing your SubQuery project + +Although this is a working example SubQuery project, you can edit the SubQuery project by changing the following files: + +- The project manifest in `project.ts` defines the key project configuration and mapping handler filters +- The GraphQL Schema (`schema.graphql`) defines the shape of the resulting data that you are using SubQuery to index +- The Mapping functions in `src/mappings/` directory are typescript functions that handle transformation logic + +SubQuery supports various layer-1 blockchain networks and provides [dedicated quick start guides](https://academy.subquery.network/quickstart/quickstart.html) as well as [detailed technical documentation](https://academy.subquery.network/build/introduction.html) for each of them. + +## Run your project + +_If you get stuck, find out how to get help below._ + +The simplest way to run your project is by running `yarn dev` or `npm run-script dev`. This does all of the following: + +1. `yarn codegen` - Generates types from the GraphQL schema definition and contract ABIs and saves them in the `/src/types` directory. This must be done after each change to the `schema.graphql` file or the contract ABIs +2. `yarn build` - Builds and packages the SubQuery project into the `/dist` directory +3. `docker-compose pull && docker-compose up` - Runs a Docker container with an indexer, PostgeSQL DB, and a query service. This requires [Docker to be installed](https://docs.docker.com/engine/install) and running locally. The configuration for this container is set from your `docker-compose.yml` + +You can observe the three services start, and once all are running (it may take a few minutes on your first start), please open your browser and head to [http://localhost:3000](http://localhost:3000) - you should see a GraphQL playground showing with the schemas ready to query. [Read the docs for more information](https://academy.subquery.network/run_publish/run.html) or [explore the possible service configuration for running SubQuery](https://academy.subquery.network/run_publish/references.html). + +## Query your project + +You can explore the different possible queries and entities to help you with GraphQL using the documentation draw on the right. + +## Publish your project + +SubQuery is open-source, meaning you have the freedom to run it in the following three ways: + +- Locally on your own computer (or a cloud provider of your choosing), [view the instructions on how to run SubQuery Locally](https://academy.subquery.network/run_publish/run.html) +- By publishing it to our enterprise-level [Managed Service](https://managedservice.subquery.network), where we'll host your SubQuery project in production ready services for mission critical data with zero-downtime blue/green deployments. We even have a generous free tier. [Find out how](https://academy.subquery.network/run_publish/publish.html) +- By publishing it to the decentralised [SubQuery Network](https://app.subquery.network), the most open, performant, reliable, and scalable data service for dApp developers. The SubQuery Network indexes and services data to the global community in an incentivised and verifiable way + +## What Next? + +Take a look at some of our advanced features to take your project to the next level! + +- [**Multi-chain indexing support**](https://academy.subquery.network/build/multi-chain.html) - SubQuery allows you to index data from across different layer-1 networks into the same database, this allows you to query a single endpoint to get data for all supported networks. +- [**Dynamic Data Sources**](https://academy.subquery.network/build/dynamicdatasources.html) - When you want to index factory contracts, for example on a DEX or generative NFT project. +- [**Project Optimisation Advice**](https://academy.subquery.network/build/optimisation.html) - Some common tips on how to tweak your project to maximise performance. +- [**GraphQL Subscriptions**](https://academy.subquery.network/run_publish/subscription.html) - Build more reactive front end applications that subscribe to changes in your SubQuery project. + +## Need Help? + +The fastest way to get support is by [searching our documentation](https://academy.subquery.network), or by [joining our discord](https://discord.com/invite/subquery) and messaging us in the `#technical-support` channel. diff --git a/Unichain/unichain-testnet-uniswap-v3/abis/ERC20.json b/Unichain/unichain-testnet-uniswap-v3/abis/ERC20.json new file mode 100644 index 00000000..405d6b36 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/abis/ERC20.json @@ -0,0 +1,222 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +] diff --git a/Unichain/unichain-testnet-uniswap-v3/abis/ERC20NameBytes.json b/Unichain/unichain-testnet-uniswap-v3/abis/ERC20NameBytes.json new file mode 100644 index 00000000..2d3c877a --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/abis/ERC20NameBytes.json @@ -0,0 +1,17 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/Unichain/unichain-testnet-uniswap-v3/abis/ERC20SymbolBytes.json b/Unichain/unichain-testnet-uniswap-v3/abis/ERC20SymbolBytes.json new file mode 100644 index 00000000..a76d6163 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/abis/ERC20SymbolBytes.json @@ -0,0 +1,17 @@ +[ + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/Unichain/unichain-testnet-uniswap-v3/abis/NonfungiblePositionManager.json b/Unichain/unichain-testnet-uniswap-v3/abis/NonfungiblePositionManager.json new file mode 100644 index 00000000..0a24c439 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/abis/NonfungiblePositionManager.json @@ -0,0 +1,1239 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_deployer", + "type": "address" + }, + { + "internalType": "address", + "name": "_factory", + "type": "address" + }, + { + "internalType": "address", + "name": "_WETH9", + "type": "address" + }, + { + "internalType": "address", + "name": "_tokenDescriptor_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Collect", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "DecreaseLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "IncreaseLiquidity", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PERMIT_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WETH9", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "baseURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint128", + "name": "amount0Max", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Max", + "type": "uint128" + } + ], + "internalType": "struct INonfungiblePositionManager.CollectParams", + "name": "params", + "type": "tuple" + } + ], + "name": "collect", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + } + ], + "name": "createAndInitializePoolIfNecessary", + "outputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "amount0Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct INonfungiblePositionManager.DecreaseLiquidityParams", + "name": "params", + "type": "tuple" + } + ], + "name": "decreaseLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "deployer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount0Desired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Desired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount0Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct INonfungiblePositionManager.IncreaseLiquidityParams", + "name": "params", + "type": "tuple" + } + ], + "name": "increaseLiquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint256", + "name": "amount0Desired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Desired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount0Min", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Min", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "internalType": "struct INonfungiblePositionManager.MintParams", + "name": "params", + "type": "tuple" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "multicall", + "outputs": [ + { + "internalType": "bytes[]", + "name": "results", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount0Owed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1Owed", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "pancakeV3MintCallback", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "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": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "tokensOwed0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "tokensOwed1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "refundETH", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "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": "selfPermit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "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": "selfPermitAllowed", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "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": "selfPermitAllowedIfNecessary", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "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": "selfPermitIfNecessary", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "sweepToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "tokenURI", + "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": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountMinimum", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "unwrapWETH9", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/Unichain/unichain-testnet-uniswap-v3/abis/factory.json b/Unichain/unichain-testnet-uniswap-v3/abis/factory.json new file mode 100644 index 00000000..29f77e6b --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/abis/factory.json @@ -0,0 +1,450 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_poolDeployer", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "FeeAmountEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "bool", + "name": "whitelistRequested", + "type": "bool" + }, + { + "indexed": false, + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "FeeAmountExtraInfoUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "indexed": false, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "lmPoolDeployer", + "type": "address" + } + ], + "name": "SetLmPoolDeployer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "verified", + "type": "bool" + } + ], + "name": "WhiteListAdded", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint128", + "name": "amount0Requested", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Requested", + "type": "uint128" + } + ], + "name": "collectProtocol", + "outputs": [ + { + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + } + ], + "name": "createPool", + "outputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + } + ], + "name": "enableFeeAmount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "name": "feeAmountTickSpacing", + "outputs": [ + { + "internalType": "int24", + "name": "", + "type": "int24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "name": "feeAmountTickSpacingExtraInfo", + "outputs": [ + { + "internalType": "bool", + "name": "whitelistRequested", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "name": "getPool", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lmPoolDeployer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "poolDeployer", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "internalType": "bool", + "name": "whitelistRequested", + "type": "bool" + }, + { + "internalType": "bool", + "name": "enabled", + "type": "bool" + } + ], + "name": "setFeeAmountExtraInfo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "uint32", + "name": "feeProtocol0", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "feeProtocol1", + "type": "uint32" + } + ], + "name": "setFeeProtocol", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pool", + "type": "address" + }, + { + "internalType": "address", + "name": "lmPool", + "type": "address" + } + ], + "name": "setLmPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_lmPoolDeployer", + "type": "address" + } + ], + "name": "setLmPoolDeployer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "bool", + "name": "verified", + "type": "bool" + } + ], + "name": "setWhiteListAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/Unichain/unichain-testnet-uniswap-v3/abis/pool.json b/Unichain/unichain-testnet-uniswap-v3/abis/pool.json new file mode 100644 index 00000000..c1f9e08d --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/abis/pool.json @@ -0,0 +1,1039 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "name": "Collect", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "name": "CollectProtocol", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "paid0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "paid1", + "type": "uint256" + } + ], + "name": "Flash", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint16", + "name": "observationCardinalityNextOld", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "observationCardinalityNextNew", + "type": "uint16" + } + ], + "name": "IncreaseObservationCardinalityNext", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Initialize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "feeProtocol0Old", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "feeProtocol1Old", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "feeProtocol0New", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "feeProtocol1New", + "type": "uint32" + } + ], + "name": "SetFeeProtocol", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "name": "SetLmPoolEvent", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount0", + "type": "int256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount1", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "protocolFeesToken0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "protocolFeesToken1", + "type": "uint128" + } + ], + "name": "Swap", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "name": "burn", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount0Requested", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Requested", + "type": "uint128" + } + ], + "name": "collect", + "outputs": [ + { + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint128", + "name": "amount0Requested", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1Requested", + "type": "uint128" + } + ], + "name": "collectProtocol", + "outputs": [ + { + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "fee", + "outputs": [ + { + "internalType": "uint24", + "name": "", + "type": "uint24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeGrowthGlobal0X128", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeGrowthGlobal1X128", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "flash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + } + ], + "name": "increaseObservationCardinalityNext", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "liquidity", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lmPool", + "outputs": [ + { + "internalType": "contract IPancakeV3LmPool", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxLiquidityPerTick", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "observations", + "outputs": [ + { + "internalType": "uint32", + "name": "blockTimestamp", + "type": "uint32" + }, + { + "internalType": "int56", + "name": "tickCumulative", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityCumulativeX128", + "type": "uint160" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32[]", + "name": "secondsAgos", + "type": "uint32[]" + } + ], + "name": "observe", + "outputs": [ + { + "internalType": "int56[]", + "name": "tickCumulatives", + "type": "int56[]" + }, + { + "internalType": "uint160[]", + "name": "secondsPerLiquidityCumulativeX128s", + "type": "uint160[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "positions", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside0LastX128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthInside1LastX128", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "tokensOwed0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "tokensOwed1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "protocolFees", + "outputs": [ + { + "internalType": "uint128", + "name": "token0", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "token1", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "feeProtocol0", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "feeProtocol1", + "type": "uint32" + } + ], + "name": "setFeeProtocol", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_lmPool", + "type": "address" + } + ], + "name": "setLmPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "slot0", + "outputs": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint16", + "name": "observationIndex", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinality", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + }, + { + "internalType": "uint32", + "name": "feeProtocol", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "unlocked", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + } + ], + "name": "snapshotCumulativesInside", + "outputs": [ + { + "internalType": "int56", + "name": "tickCumulativeInside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityInsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsInside", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "bool", + "name": "zeroForOne", + "type": "bool" + }, + { + "internalType": "int256", + "name": "amountSpecified", + "type": "int256" + }, + { + "internalType": "uint160", + "name": "sqrtPriceLimitX96", + "type": "uint160" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "swap", + "outputs": [ + { + "internalType": "int256", + "name": "amount0", + "type": "int256" + }, + { + "internalType": "int256", + "name": "amount1", + "type": "int256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int16", + "name": "", + "type": "int16" + } + ], + "name": "tickBitmap", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tickSpacing", + "outputs": [ + { + "internalType": "int24", + "name": "", + "type": "int24" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "int24", + "name": "", + "type": "int24" + } + ], + "name": "ticks", + "outputs": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside0X128", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "feeGrowthOutside1X128", + "type": "uint256" + }, + { + "internalType": "int56", + "name": "tickCumulativeOutside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityOutsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsOutside", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token0", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "token1", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/Unichain/unichain-testnet-uniswap-v3/docker-compose.yml b/Unichain/unichain-testnet-uniswap-v3/docker-compose.yml new file mode 100644 index 00000000..29c01abc --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/docker-compose.yml @@ -0,0 +1,63 @@ +version: "3" + +services: + postgres: + build: + context: . + dockerfile: ./docker/pg-Dockerfile + ports: + - 5432:5432 + volumes: + - .data/postgres:/var/lib/postgresql/data + environment: + POSTGRES_PASSWORD: postgres + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + + subquery-node: + image: subquerynetwork/subql-node-ethereum:latest + depends_on: + "postgres": + condition: service_healthy + restart: unless-stopped + environment: + DB_USER: postgres + DB_PASS: postgres + DB_DATABASE: postgres + DB_HOST: postgres + DB_PORT: 5432 + volumes: + - ./:/app + command: + - -f=/app + - --db-schema=app + - --batch-size=1 + healthcheck: + test: ["CMD", "curl", "-f", "http://subquery-node:3000/ready"] + interval: 3s + timeout: 5s + retries: 10 + + graphql-engine: + image: subquerynetwork/subql-query:latest + ports: + - 3000:3000 + depends_on: + "postgres": + condition: service_healthy + "subquery-node": + condition: service_healthy + restart: always + environment: + DB_USER: postgres + DB_PASS: postgres + DB_DATABASE: postgres + DB_HOST: postgres + DB_PORT: 5432 + command: + - --name=app + - --playground + - --indexer=http://subquery-node:3000 diff --git a/Unichain/unichain-testnet-uniswap-v3/docker/load-extensions.sh b/Unichain/unichain-testnet-uniswap-v3/docker/load-extensions.sh new file mode 100644 index 00000000..7f5d0206 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/docker/load-extensions.sh @@ -0,0 +1,6 @@ + +#!/bin/sh + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <=3.0.0", + }, + query: { + name: "@subql/query", + version: "*", + }, + }, + schema: { + file: "./schema.graphql", + }, + network: { + /** + * chainId is the EVM Chain ID, for Ethereum this is 1 + * https://chainlist.org/chain/1 + */ + chainId: "1301", + /** + * These endpoint(s) should be non-pruned archive node + * We recommend providing more than one endpoint for improved reliability, performance, and uptime + * Public nodes may be rate limited, which can affect indexing speed + * When developing your project we suggest getting a private API key + * If you use a rate limited endpoint, adjust the --batch-size and --workers parameters + * These settings can be found in your docker-compose.yaml, they will slow indexing but prevent your project being rate limited + */ + endpoint: ["https://sepolia.unichain.org"], + }, + dataSources: [ + { + kind: EthereumDatasourceKind.Runtime, + startBlock: 1, + options: { + // Must be a key of assets + abi: "Factory", + address: "0x1F98431c8aD98523631AE4a59f267346ea31F984", + }, + assets: new Map([ + ["Factory", { file: "./abis/factory.json" }], + ["ERC20", { file: "./abis/ERC20.json" }], + + ["ERC20SymbolBytes", { file: "./abis/ERC20SymbolBytes.json" }], + ["ERC20NameBytes", { file: "./abis/ERC20NameBytes.json" }], + ["Pool", { file: "./abis/pool.json" }], + ]), + mapping: { + file: "./dist/index.js", + handlers: [ + { + kind: EthereumHandlerKind.Event, + handler: "handlePoolCreated", + filter: { + topics: [ + "PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool)", + ], + }, + }, + ], + }, + }, + // ethereum/contract + { + kind: EthereumDatasourceKind.Runtime, + startBlock: 1, + options: { + // Must be a key of assets + abi: "NonfungiblePositionManager", + address: "0xB7F724d6dDDFd008eFf5cc2834edDE5F9eF0d075", + }, + assets: new Map([ + [ + "NonfungiblePositionManager", + { file: "./abis/NonfungiblePositionManager.json" }, + ], + ["Pool", { file: "./abis/pool.json" }], + ["ERC20", { file: "./abis/ERC20.json" }], + + ["Factory", { file: "./abis/factory.json" }], + ]), + mapping: { + file: "./dist/index.js", + handlers: [ + { + kind: EthereumHandlerKind.Event, + handler: "handleIncreaseLiquidity", + filter: { + topics: [ + "IncreaseLiquidity (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)", + ], + }, + }, + { + kind: EthereumHandlerKind.Event, + handler: "handleDecreaseLiquidity", + filter: { + topics: [ + "DecreaseLiquidity (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)", + ], + }, + }, + { + kind: EthereumHandlerKind.Event, + handler: "handleCollect", + filter: { + topics: [ + "Collect (uint256 tokenId, address recipient, uint256 amount0, uint256 amount1)", + ], + }, + }, + { + kind: EthereumHandlerKind.Event, + handler: "handleTransfer", + filter: { + topics: ["Transfer (address from, address to, uint256 tokenId)"], + }, + }, + ], + }, + }, + ], + templates: [ + { + kind: EthereumDatasourceKind.Runtime, + name: "Pool", + options: { + abi: "Pool", + }, + assets: new Map([ + ["Pool", { file: "./abis/pool.json" }], + ["ERC20", { file: "./abis/ERC20.json" }], + ["Factory", { file: "./abis/factory.json" }], + ]), + mapping: { + file: "./dist/index.js", + handlers: [ + { + kind: EthereumHandlerKind.Event, + handler: "handleInitialize", + filter: { + topics: ["Initialize (uint160,int24)"], + }, + }, + { + kind: EthereumHandlerKind.Event, + handler: "handleSwap", + filter: { + topics: [ + "Swap(address,address,int256,int256,uint160,uint128,int24,uint128,uint128)", + ], + }, + }, + { + kind: EthereumHandlerKind.Event, + handler: "handleMint", + filter: { + topics: [ + "Mint(address sender, address owner, int24 tickLower, int24 tickUpper, uint128 amount, uint256 amount0, uint256 amount1)", + ], + }, + }, + { + kind: EthereumHandlerKind.Event, + handler: "handleBurn", + filter: { + topics: ["Burn(address,int24,int24,uint128,uint256,uint256)"], + }, + }, + { + kind: EthereumHandlerKind.Event, + handler: "handleFlash", + filter: { + topics: [ + "Flash(address,address,uint256,uint256,uint256,uint256)", + ], + }, + }, + ], + }, + }, + ], + repository: "https://github.com/subquery/ethereum-subql-starter", +}; + +// Must set default to the project instance +export default project; diff --git a/Unichain/unichain-testnet-uniswap-v3/schema.graphql b/Unichain/unichain-testnet-uniswap-v3/schema.graphql new file mode 100644 index 00000000..29c1eddf --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/schema.graphql @@ -0,0 +1,661 @@ +type Factory @entity { + # factory address + id: ID! + # amount of pools created + poolCount: BigInt! + # amoutn of transactions all time + txCount: BigInt! + # total volume all time in derived USD + totalVolumeUSD: Float! + # total volume all time in derived ETH + totalVolumeETH: Float! + # total swap fees all time in USD + totalFeesUSD: Float! + # total swap fees all time in USD + totalFeesETH: Float! + # all volume even through less reliable USD values + untrackedVolumeUSD: Float! + # TVL derived in USD + totalValueLockedUSD: Float! + # TVL derived in ETH + totalValueLockedETH: Float! + # TVL derived in USD untracked + totalValueLockedUSDUntracked: Float! + # TVL derived in ETH untracked + totalValueLockedETHUntracked: Float! + + # current owner of the factory + owner: ID! +} + +# stores for USD calculations +type Bundle @entity { + id: ID! + # price of ETH in usd + ethPriceUSD: Float! +} + +type Token @entity { + # token address + id: ID! + # token symbol + symbol: String! + # token name + name: String! + # token decimals + decimals: BigInt! + # token total supply + totalSupply: BigInt! + # volume in token units + volume: Float! + # volume in derived USD + volumeUSD: Float! + # volume in USD even on pools with less reliable USD values + untrackedVolumeUSD: Float! + # fees in USD + feesUSD: Float! + # transactions across all pools that include this token + txCount: BigInt! + # number of pools containing this token + poolCount: BigInt! + # liquidity across all pools in token units + totalValueLocked: Float! + # liquidity across all pools in derived USD + totalValueLockedUSD: Float! + # TVL derived in USD untracked + totalValueLockedUSDUntracked: Float! + # derived price in ETH + derivedETH: Float! + # pools token is in that are white listed for USD pricing + # Should be Pool + # whitelistPools: [Pool!]! + # derived fields + tokenDayData: [TokenDayData!]! @derivedFrom(field: "token") +} + +type WhiteListPools @entity { + id: ID! + token: Token! @index + pool: Pool! +} + +type Pool @entity { + # pool address + id: ID! + # creation + createdAtTimestamp: BigInt! + # block pool was created at + createdAtBlockNumber: BigInt! + # token0 + token0: Token + # token0: [Token!] @derivedFrom(field: "id") + token1: Token + # token1: [Token!] @derivedFrom(field: "id") + # fee amount + feeTier: BigInt! + # in range liquidity + liquidity: BigInt! + # current price tracker + sqrtPrice: BigInt! + # tracker for global fee growth + feeGrowthGlobal0X128: BigInt! + # tracker for global fee growth + feeGrowthGlobal1X128: BigInt! + # token0 per token1 + token0Price: Float! + # token1 per token0 + token1Price: Float! + # current tick + tick: BigInt + # current observation index + observationIndex: BigInt! + # all time token0 swapped + volumeToken0: Float! + # all time token1 swapped + volumeToken1: Float! + # all time USD swapped + volumeUSD: Float! + # all time USD swapped, unfiltered for unreliable USD pools + untrackedVolumeUSD: Float! + # fees in USD + feesUSD: Float! + # all time number of transactions + txCount: BigInt! + # all time fees collected token0 + collectedFeesToken0: Float! + # all time fees collected token1 + collectedFeesToken1: Float! + # all time fees collected derived USD + collectedFeesUSD: Float! + # total token 0 across all ticks + totalValueLockedToken0: Float! + # total token 1 across all ticks + totalValueLockedToken1: Float! + # tvl derived ETH + totalValueLockedETH: Float! + # tvl USD + totalValueLockedUSD: Float! + # TVL derived in USD untracked + totalValueLockedUSDUntracked: Float! + # Fields used to help derived relationship + liquidityProviderCount: BigInt! # used to detect new exchanges + # hourly snapshots of pool data + poolHourData: [PoolHourData!]! @derivedFrom(field: "pool") + # daily snapshots of pool data + poolDayData: [PoolDayData!]! @derivedFrom(field: "pool") + # derived fields + mints: [Mint!]! @derivedFrom(field: "pool") + burns: [Burn!]! @derivedFrom(field: "pool") + swaps: [Swap!]! @derivedFrom(field: "pool") + collects: [Collect!]! @derivedFrom(field: "pool") + ticks: [Tick!]! @derivedFrom(field: "pool") +} + +type Tick @entity { + # format: # + id: ID! + # pool address + poolAddress: String + # tick index + tickIdx: BigInt! + # pointer to pool + pool: Pool! + # total liquidity pool has as tick lower or upper + liquidityGross: BigInt! + # how much liquidity changes when tick crossed + liquidityNet: BigInt! + # calculated price of token0 of tick within this pool - constant + price0: Float! + # calculated price of token1 of tick within this pool - constant + price1: Float! + # lifetime volume of token0 with this tick in range + volumeToken0: Float! + # lifetime volume of token1 with this tick in range + volumeToken1: Float! + # lifetime volume in derived USD with this tick in range + volumeUSD: Float! + # lifetime volume in untracked USD with this tick in range + untrackedVolumeUSD: Float! + # fees in USD + feesUSD: Float! + # all time collected fees in token0 + collectedFeesToken0: Float! + # all time collected fees in token1 + collectedFeesToken1: Float! + # all time collected fees in USD + collectedFeesUSD: Float! + # created time + createdAtTimestamp: BigInt! + # created block + createdAtBlockNumber: BigInt! + # Fields used to help derived relationship + liquidityProviderCount: BigInt! # used to detect new exchanges + # derived fields + # swaps: [Swap!]! @derivedFrom(field: "tick") + # vars needed for fee computation + feeGrowthOutside0X128: BigInt! + feeGrowthOutside1X128: BigInt! +} + +type Position @entity { + # Positions created through NonfungiblePositionManager + # NFT token id + id: ID! + # owner of the NFT + owner: String! + # pool position is within + pool: Pool! + # allow indexing by tokens + token0: Token! + # allow indexing by tokens + token1: Token! + # lower tick of the position + tickLower: Tick! + # upper tick of the position + tickUpper: Tick! + # total position liquidity + liquidity: BigInt! + # amount of token 0 ever deposited to position + depositedToken0: Float! + # amount of token 1 ever deposited to position + depositedToken1: Float! + # amount of token 0 ever withdrawn from position (without fees) + withdrawnToken0: Float! + # amount of token 1 ever withdrawn from position (without fees) + withdrawnToken1: Float! + # all time collected fees in token0 + collectedFeesToken0: Float! + # all time collected fees in token1 + collectedFeesToken1: Float! + # tx in which the position was initialized + transaction: Transaction! + # vars needed for fee computation + feeGrowthInside0LastX128: BigInt! + feeGrowthInside1LastX128: BigInt! +} + +type PositionSnapshot @entity { + # # + id: ID! + # owner of the NFT + owner: String! + # pool the position is within + pool: Pool! + # position of which the snap was taken of + position: Position! + # block in which the snap was created + blockNumber: BigInt! + # timestamp of block in which the snap was created + timestamp: BigInt! + # total position liquidity + liquidity: BigInt! + # amount of token 0 ever deposited to position + depositedToken0: Float! + # amount of token 1 ever deposited to position + depositedToken1: Float! + # amount of token 0 ever withdrawn from position (without fees) + withdrawnToken0: Float! + # amount of token 1 ever withdrawn from position (without fees) + withdrawnToken1: Float! + # all time collected fees in token0 + collectedFeesToken0: Float! + # all time collected fees in token1 + collectedFeesToken1: Float! + # tx in which the snapshot was initialized + transaction: Transaction! + # internal vars needed for fee computation + feeGrowthInside0LastX128: BigInt! + feeGrowthInside1LastX128: BigInt! +} + +type Transaction @entity { + # txn hash + id: ID! + # block txn was included in + blockNumber: BigInt! + # timestamp txn was confirmed + timestamp: BigInt! + # gas used during txn execution + gasUsed: BigInt! + gasPrice: BigInt! + # derived values + mints: [Mint]! @derivedFrom(field: "transaction") + burns: [Burn]! @derivedFrom(field: "transaction") + swaps: [Swap]! @derivedFrom(field: "transaction") + flashed: [Flash]! @derivedFrom(field: "transaction") + collects: [Collect]! @derivedFrom(field: "transaction") +} + +type Mint @entity { + # transaction hash + "#" + index in mints Transaction array + id: ID! + # which txn the mint was included in + transaction: Transaction! + # time of txn + timestamp: BigInt! + # pool position is within + pool: Pool! + # allow indexing by tokens + token0: Token! + # allow indexing by tokens + token1: Token! + # owner of position where liquidity minted to + owner: String! + # the address that minted the liquidity + sender: String + # txn origin + origin: String! # the EOA that initiated the txn + # amount of liquidity minted + amount: BigInt! + # amount of token 0 minted + amount0: Float! + # amount of token 1 minted + amount1: Float! + # derived amount based on available prices of tokens + amountUSD: Float + # lower tick of the position + tickLower: BigInt! + # upper tick of the position + tickUpper: BigInt! + # order within the txn + logIndex: BigInt +} + +type Burn @entity { + # transaction hash + "#" + index in mints Transaction array + id: ID! + # txn burn was included in + transaction: Transaction! + # pool position is within + pool: Pool! + # allow indexing by tokens + token0: Token! + # allow indexing by tokens + token1: Token! + # need this to pull recent txns for specific token or pool + timestamp: BigInt! + # owner of position where liquidity was burned + owner: String + # txn origin + origin: String! # the EOA that initiated the txn + # amouny of liquidity burned + amount: BigInt! + # amount of token 0 burned + amount0: Float! + # amount of token 1 burned + amount1: Float! + # derived amount based on available prices of tokens + amountUSD: Float + # lower tick of position + tickLower: BigInt! + # upper tick of position + tickUpper: BigInt! + # position within the transactions + logIndex: BigInt +} + +type Swap @entity { + # transaction hash + "#" + index in swaps Transaction array + id: ID! + # pointer to transaction + transaction: Transaction! + # timestamp of transaction + timestamp: BigInt! + # pool swap occured within + pool: Pool! + # allow indexing by tokens + token0: Token! + # allow indexing by tokens + token1: Token! + # sender of the swap + sender: String! + # recipient of the swap + recipient: String! + # txn origin + origin: String! # the EOA that initiated the txn + # delta of token0 swapped + amount0: Float! + # delta of token1 swapped + amount1: Float! + # derived info + amountUSD: Float! + # The sqrt(price) of the pool after the swap, as a Q64.96 + sqrtPriceX96: BigInt! + # the tick after the swap + tick: BigInt! + # index within the txn + logIndex: BigInt +} + +type Collect @entity { + # transaction hash + "#" + index in collect Transaction array + id: ID! + # pointer to txn + transaction: Transaction! + # timestamp of event + timestamp: BigInt! + # pool collect occured within + pool: Pool! + # owner of position collect was performed on + owner: String + # amount of token0 collected + amount0: Float! + # amount of token1 collected + amount1: Float! + # derived amount based on available prices of tokens + amountUSD: Float + # lower tick of position + tickLower: BigInt! + # uppper tick of position + tickUpper: BigInt! + # index within the txn + logIndex: BigInt +} + +type Flash @entity { + # transaction hash + "-" + index in collect Transaction array + id: ID! + # pointer to txn + transaction: Transaction! + # timestamp of event + timestamp: BigInt! + # pool collect occured within + pool: Pool! + # sender of the flash + sender: String! + # recipient of the flash + recipient: String! + # amount of token0 flashed + amount0: Float! + # amount of token1 flashed + amount1: Float! + # derived amount based on available prices of tokens + amountUSD: Float! + # amount token0 paid for flash + amount0Paid: Float! + # amount token1 paid for flash + amount1Paid: Float! + # index within the txn + logIndex: BigInt +} + +# Data accumulated and condensed into day stats for all of PancakeSwap +type PancakeSwapDayData @entity { + # timestamp rounded to current day by dividing by 86400 + id: ID! + # timestamp rounded to current day by dividing by 86400 + date: Int! + # total daily volume in PancakeSwap derived in terms of ETH + volumeETH: Float! + # total daily volume in PancakeSwap derived in terms of USD + volumeUSD: Float! + # total daily volume in PancakeSwap derived in terms of USD untracked + volumeUSDUntracked: Float! + # fees in USD + feesUSD: Float! + # number of daily transactions + txCount: BigInt! + # tvl in terms of USD + tvlUSD: Float! +} + +# Data accumulated and condensed into day stats for each pool +type PoolDayData @entity { + # timestamp rounded to current day by dividing by 86400 + id: ID! + # timestamp rounded to current day by dividing by 86400 + date: Int! + # pointer to pool + pool: Pool! + # in range liquidity at end of period + liquidity: BigInt! + # current price tracker at end of period + sqrtPrice: BigInt! + # price of token0 - derived from sqrtPrice + token0Price: Float! + # price of token1 - derived from sqrtPrice + token1Price: Float! + # current tick at end of period + tick: BigInt + # tracker for global fee growth + feeGrowthGlobal0X128: BigInt! + # tracker for global fee growth + feeGrowthGlobal1X128: BigInt! + # tvl derived in USD at end of period + tvlUSD: Float! + # volume in token0 + volumeToken0: Float! + # volume in token1 + volumeToken1: Float! + # volume in USD + volumeUSD: Float! + # fees in USD + feesUSD: Float! + # numebr of transactions during period + txCount: BigInt! + # opening price of token0 + open: Float! + # high price of token0 + high: Float! + # low price of token0 + low: Float! + # close price of token0 + close: Float! +} + +# hourly stats tracker for pool +type PoolHourData @entity { + # format: - + id: ID! + # unix timestamp for start of hour + periodStartUnix: Int! + # pointer to pool + pool: Pool! + # in range liquidity at end of period + liquidity: BigInt! + # current price tracker at end of period + sqrtPrice: BigInt! + # price of token0 - derived from sqrtPrice + token0Price: Float! + # price of token1 - derived from sqrtPrice + token1Price: Float! + # current tick at end of period + tick: BigInt + # tracker for global fee growth + feeGrowthGlobal0X128: BigInt! + # tracker for global fee growth + feeGrowthGlobal1X128: BigInt! + # tvl derived in USD at end of period + tvlUSD: Float! + # volume in token0 + volumeToken0: Float! + # volume in token1 + volumeToken1: Float! + # volume in USD + volumeUSD: Float! + # fees in USD + feesUSD: Float! + # numebr of transactions during period + txCount: BigInt! + # opening price of token0 + open: Float! + # high price of token0 + high: Float! + # low price of token0 + low: Float! + # close price of token0 + close: Float! +} + +type TickHourData @entity { + # format: -- + id: ID! + # unix timestamp for start of hour + periodStartUnix: Int! + # pointer to pool + pool: Pool! + # pointer to tick + tick: Tick! + # total liquidity pool has as tick lower or upper at end of period + liquidityGross: BigInt! + # how much liquidity changes when tick crossed at end of period + liquidityNet: BigInt! + # hourly volume of token0 with this tick in range + volumeToken0: Float! + # hourly volume of token1 with this tick in range + volumeToken1: Float! + # hourly volume in derived USD with this tick in range + volumeUSD: Float! + # fees in USD + feesUSD: Float! +} + +# Data accumulated and condensed into day stats for each exchange +# Note: this entity gets saved only if there is a change during the day +type TickDayData @entity { + # format: -- + id: ID! + # timestamp rounded to current day by dividing by 86400 + date: Int! + # pointer to pool + pool: Pool! + # pointer to tick + tick: Tick! + # total liquidity pool has as tick lower or upper at end of period + liquidityGross: BigInt! + # how much liquidity changes when tick crossed at end of period + liquidityNet: BigInt! + # hourly volume of token0 with this tick in range + volumeToken0: Float! + # hourly volume of token1 with this tick in range + volumeToken1: Float! + # hourly volume in derived USD with this tick in range + volumeUSD: Float! + # fees in USD + feesUSD: Float! + # vars needed for fee computation + feeGrowthOutside0X128: BigInt! + feeGrowthOutside1X128: BigInt! +} + +type TokenDayData @entity { + # token address concatendated with date + id: ID! + # timestamp rounded to current day by dividing by 86400 + date: Int! + # pointer to token + token: Token! + # volume in token units + volume: Float! + # volume in derived USD + volumeUSD: Float! + # volume in USD even on pools with less reliable USD values + untrackedVolumeUSD: Float! + # liquidity across all pools in token units + totalValueLocked: Float! + # liquidity across all pools in derived USD + totalValueLockedUSD: Float! + # price at end of period in USD + priceUSD: Float! + # fees in USD + feesUSD: Float! + # opening price USD + open: Float! + # high price USD + high: Float! + # low price USD + low: Float! + # close price USD + close: Float! +} + +type TokenHourData @entity { + # token address concatendated with date + id: ID! + # unix timestamp for start of hour + periodStartUnix: Int! + # pointer to token + token: Token! + # volume in token units + volume: Float! + # volume in derived USD + volumeUSD: Float! + # volume in USD even on pools with less reliable USD values + untrackedVolumeUSD: Float! + # liquidity across all pools in token units + totalValueLocked: Float! + # liquidity across all pools in derived USD + totalValueLockedUSD: Float! + # price at end of period in USD + priceUSD: Float! + # fees in USD + feesUSD: Float! + # opening price USD + open: Float! + # high price USD + high: Float! + # low price USD + low: Float! + # close price USD + close: Float! +} diff --git a/Unichain/unichain-testnet-uniswap-v3/src/index.ts b/Unichain/unichain-testnet-uniswap-v3/src/index.ts new file mode 100644 index 00000000..4d9f357c --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/index.ts @@ -0,0 +1,7 @@ +import { atob } from "abab"; + +if (!global.atob) { + global.atob = atob as any; +} + +export * from "./mappings/mappingHandlers"; diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/core.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/core.ts new file mode 100644 index 00000000..9a1ad548 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/core.ts @@ -0,0 +1,702 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { Bundle, Burn, Factory, Mint, Pool, Swap, Tick, Token } from "../types"; +import { + FACTORY_ADDRESS, + ONE_BI, + ZERO_BD, + ZERO_BI, + convertTokenToDecimal, + loadTransaction, + safeDiv, + findEthPerToken, + getEthPriceInUSD, + getTrackedAmountUSD, + sqrtPriceX96ToTokenPrices, + updatePoolDayData, + updatePoolHourData, + updateTickDayData, + updateTokenDayData, + updateTokenHourData, + updatePancakeSwapDayData, + createTick, + feeTierToTickSpacing, +} from "./utils"; +import { EthereumLog } from "@subql/types-ethereum"; +import { BigNumber } from "@ethersproject/bignumber"; +import { + InitializeEvent, + MintEvent, + BurnEvent, + SwapEvent, + FlashEvent, +} from "../types/contracts/Pool"; +import { Pool__factory } from "../types/contracts/factories/Pool__factory"; +import assert from "assert"; + +export async function handleInitialize( + event: EthereumLog, +): Promise { + const [pool, ethPrice] = await Promise.all([ + Pool.get(event.address), + getEthPriceInUSD(), + ]); + assert(pool); + assert(event.args); + pool.sqrtPrice = event.args.sqrtPriceX96.toBigInt(); + pool.tick = BigNumber.from(event.args.tick).toBigInt(); + assert(pool.token0Id); + assert(pool.token1Id); + + // update token prices + const [token0, token1] = await Promise.all([ + Token.get(pool.token0Id), + Token.get(pool.token1Id), + ]); + const bundle = await Bundle.get("1"); + assert(bundle); + bundle.ethPriceUSD = ethPrice.toNumber(); + + await Promise.all([updatePoolDayData(event), updatePoolHourData(event)]); + + assert(token0); + assert(token1); + const [derivedETH0, derivedETH1] = await Promise.all([ + findEthPerToken(token0), + findEthPerToken(token1), + ]); + + // update token prices + token0.derivedETH = derivedETH0.toNumber(); + token1.derivedETH = derivedETH1.toNumber(); + + await Promise.all([pool.save(), bundle.save(), token0.save(), token1.save()]); + // update ETH price now that prices could have changed +} + +export async function handleMint( + event: EthereumLog, +): Promise { + const poolAddress = event.address; + const pool = await Pool.get(poolAddress); + + if (pool === undefined || pool === null) { + logger.warn( + `Could not get pool address ${poolAddress} for mint at transaction ${event.transactionHash}, log id ${event.logIndex}`, + ); + return; + } + + assert(pool.token0Id); + assert(pool.token1Id); + const [bundle, factory, token0, token1, transaction] = await Promise.all([ + Bundle.get("1"), + Factory.get(FACTORY_ADDRESS), + Token.get(pool.token0Id), + Token.get(pool.token1Id), + loadTransaction(event), + ]); + + assert(event.args); + assert(token0); + assert(token1); + assert(bundle); + const amount0 = convertTokenToDecimal(event.args.amount0, token0.decimals); + const amount1 = convertTokenToDecimal(event.args.amount1, token1.decimals); + + const amountUSD = + amount0.toNumber() * (token0.derivedETH * bundle.ethPriceUSD) + + amount1.toNumber() * (token1.derivedETH * bundle.ethPriceUSD); + + assert(factory); + // reset tvl aggregates until new amounts calculated + factory.totalValueLockedETH = + factory.totalValueLockedETH - pool.totalValueLockedETH; + + // update globals + factory.txCount = factory.txCount + ONE_BI; + + // update token0 data + token0.txCount = token0.txCount + ONE_BI; + token0.totalValueLocked = token0.totalValueLocked + amount0.toNumber(); + token0.totalValueLockedUSD = + token0.totalValueLocked * token0.derivedETH * bundle.ethPriceUSD; + + // update token1 data + token1.txCount = token1.txCount + ONE_BI; + token1.totalValueLocked = token1.totalValueLocked + amount1.toNumber(); + token1.totalValueLockedUSD = + token1.totalValueLocked * token1.derivedETH * bundle.ethPriceUSD; + + // pool data + pool.txCount = pool.txCount + ONE_BI; + + // Pools liquidity tracks the currently active liquidity given pools current tick. + // We only want to update it on mint if the new position includes the current tick. + if ( + pool.tick !== undefined && + BigNumber.from(event.args.tickLower).lte(pool.tick) && + BigNumber.from(event.args.tickUpper).gt(pool.tick) + ) { + pool.liquidity = pool.liquidity + event.args.amount.toBigInt(); + } + + pool.totalValueLockedToken0 = + pool.totalValueLockedToken0 + amount0.toNumber(); + pool.totalValueLockedToken1 = + pool.totalValueLockedToken1 + amount1.toNumber(); + pool.totalValueLockedETH = + pool.totalValueLockedToken0 * token0.derivedETH + + pool.totalValueLockedToken1 * token1.derivedETH; + pool.totalValueLockedUSD = pool.totalValueLockedETH + bundle.ethPriceUSD; + + // reset aggregates with new amounts + factory.totalValueLockedETH = + factory.totalValueLockedETH + pool.totalValueLockedETH; + factory.totalValueLockedUSD = + factory.totalValueLockedETH + bundle.ethPriceUSD; + + const mint = Mint.create({ + id: transaction.id.toString() + "#" + pool.txCount.toString(), + transactionId: transaction.id, + timestamp: transaction.timestamp, + poolId: pool.id, + token0Id: pool.token0Id, + token1Id: pool.token1Id, + owner: event.args.owner, + sender: event.args.sender, + origin: event.transaction.from, + amount: event.args.amount.toBigInt(), + amount0: amount0.toNumber(), + amount1: amount1.toNumber(), + amountUSD: amountUSD, + tickLower: BigInt(event.args.tickLower), + tickUpper: BigInt(event.args.tickUpper), + logIndex: BigInt(event.logIndex), + }); + + // tick entities + const lowerTickIdx = event.args.tickLower; + const upperTickIdx = event.args.tickUpper; + const lowerTickId = poolAddress + "#" + event.args.tickLower; + const upperTickId = poolAddress + "#" + event.args.tickUpper; + + let [lowerTick, upperTick] = await Promise.all([ + Tick.get(lowerTickId), + Tick.get(upperTickId), + ]); + + if (lowerTick === null || lowerTick === undefined) { + lowerTick = createTick(lowerTickId, lowerTickIdx, pool.id, event); + } + + if (upperTick === null || upperTick === undefined) { + upperTick = createTick(upperTickId, upperTickIdx, pool.id, event); + } + + const amount = event.args.amount; + lowerTick.liquidityGross = lowerTick.liquidityGross + amount.toBigInt(); + lowerTick.liquidityNet = lowerTick.liquidityNet + amount.toBigInt(); + upperTick.liquidityGross = upperTick.liquidityGross + amount.toBigInt(); + upperTick.liquidityNet = upperTick.liquidityNet - amount.toBigInt(); + + // TODO: Update Tick's volume, fees, and liquidity provider count. Computing these on the tick + // level requires reimplementing some of the swapping code from v3-core. + + await Promise.all([ + updatePancakeSwapDayData(event), + updatePoolDayData(event), + updatePoolHourData(event), + updateTokenDayData(token0, event), + updateTokenDayData(token1, event), + updateTokenHourData(token0, event), + updateTokenHourData(token1, event), + + token0.save(), + token1.save(), + pool.save(), + factory.save(), + mint.save(), + + // Update inner tick vars and save the ticks + updateTickFeeVarsAndSave(lowerTick, event), + updateTickFeeVarsAndSave(upperTick, event), + ]); +} + +export async function handleBurn( + event: EthereumLog, +): Promise { + const poolAddress = event.address; + const pool = await Pool.get(poolAddress); + assert(pool?.token0Id); + assert(pool?.token1Id); + + const [bundle, factory, token0, token1, transaction] = await Promise.all([ + Bundle.get("1"), + Factory.get(FACTORY_ADDRESS), + Token.get(pool.token0Id), + Token.get(pool.token1Id), + loadTransaction(event), + ]); + assert(event.args); + assert(token0); + assert(token1); + + const amount0 = convertTokenToDecimal(event.args.amount0, token0.decimals); + const amount1 = convertTokenToDecimal(event.args.amount1, token1.decimals); + assert(bundle); + + const amountUSD = + amount0.toNumber() * token0.derivedETH * bundle.ethPriceUSD + + amount1.toNumber() * token1.derivedETH * bundle.ethPriceUSD; + + assert(factory); + // reset tvl aggregates until new amounts calculated + factory.totalValueLockedETH = + factory.totalValueLockedETH - pool.totalValueLockedETH; + + // update globals + factory.txCount = factory.txCount + ONE_BI; + + // update token0 data + token0.txCount = token0.txCount + ONE_BI; + token0.totalValueLocked = token0.totalValueLocked - amount0.toNumber(); + token0.totalValueLockedUSD = + token0.totalValueLocked * token0.derivedETH * bundle.ethPriceUSD; + + // update token1 data + token1.txCount = token1.txCount + ONE_BI; + token1.totalValueLocked = token1.totalValueLocked - amount1.toNumber(); + token1.totalValueLockedUSD = + token1.totalValueLocked * token1.derivedETH * bundle.ethPriceUSD; + + // pool data + pool.txCount = pool.txCount + ONE_BI; + // Pools liquidity tracks the currently active liquidity given pools current tick. + // We only want to update it on burn if the position being burnt includes the current tick. + if ( + pool.tick !== undefined && + BigNumber.from(event.args.tickLower).lte(pool.tick) && + BigNumber.from(event.args.tickUpper).gt(pool.tick) + ) { + pool.liquidity = pool.liquidity - event.args.amount.toBigInt(); + } + + pool.totalValueLockedToken0 = + pool.totalValueLockedToken0 - amount0.toNumber(); + pool.totalValueLockedToken1 = + pool.totalValueLockedToken1 - amount1.toNumber(); + pool.totalValueLockedETH = + pool.totalValueLockedToken0 * token0.derivedETH + + pool.totalValueLockedToken1 * token1.derivedETH; + pool.totalValueLockedUSD = pool.totalValueLockedETH * bundle.ethPriceUSD; + + // reset aggregates with new amounts + factory.totalValueLockedETH = + factory.totalValueLockedETH * pool.totalValueLockedETH; + factory.totalValueLockedUSD = + factory.totalValueLockedETH * bundle.ethPriceUSD; + + // burn entity + // const transaction = await loadTransaction(event) + const burn = Burn.create({ + id: transaction.id + "#" + pool.txCount.toString(), + transactionId: transaction.id, + timestamp: transaction.timestamp, + poolId: pool.id, + token0Id: pool.token0Id, + token1Id: pool.token1Id, + owner: event.args.owner, + origin: event.transaction.from, + amount: event.args.amount.toBigInt(), + amount0: amount0.toNumber(), + amount1: amount1.toNumber(), + amountUSD: amountUSD, + tickLower: BigInt(event.args.tickLower), + tickUpper: BigInt(event.args.tickUpper), + logIndex: BigInt(event.logIndex), + }); + + // tick entities + const lowerTickId = poolAddress + "#" + event.args.tickLower; + const upperTickId = poolAddress + "#" + event.args.tickUpper; + const [lowerTick, upperTick] = await Promise.all([ + Tick.get(lowerTickId), + Tick.get(upperTickId), + ]); + const amount = event.args.amount; + assert(lowerTick); + assert(upperTick); + lowerTick.liquidityGross = lowerTick.liquidityGross - amount.toBigInt(); + lowerTick.liquidityNet = lowerTick.liquidityNet - amount.toBigInt(); + upperTick.liquidityGross = upperTick.liquidityGross - amount.toBigInt(); + upperTick.liquidityNet = upperTick.liquidityNet + amount.toBigInt(); + + await Promise.all([ + updatePancakeSwapDayData(event), + updatePoolDayData(event), + updatePoolHourData(event), + updateTokenDayData(token0, event), + updateTokenDayData(token1, event), + updateTokenHourData(token0, event), + updateTokenHourData(token1, event), + updateTickFeeVarsAndSave(lowerTick, event), + updateTickFeeVarsAndSave(upperTick, event), + token0.save(), + token1.save(), + pool.save(), + factory.save(), + burn.save(), + ]); +} + +export async function handleSwap( + event: EthereumLog, +): Promise { + const poolContract = Pool__factory.connect(event.address, api); + const [ + bundle, + factory, + pool, + transaction, + ethPrice, + feeGrowthGlobal0X128, + feeGrowthGlobal1X128, + ] = await Promise.all([ + Bundle.get("1"), + Factory.get(FACTORY_ADDRESS), + Pool.get(event.address), + loadTransaction(event), + getEthPriceInUSD(), + poolContract.feeGrowthGlobal0X128(), + poolContract.feeGrowthGlobal1X128(), + ]); + assert(pool); + + // hot fix for bad pricing + if (pool.id == "0x9663f2ca0454accad3e094448ea6f77443880454") { + return; + } + assert(pool.token0Id); + assert(pool.token1Id); + + const [token0, token1] = await Promise.all([ + Token.get(pool.token0Id), + Token.get(pool.token1Id), + ]); + const oldTick = pool.tick; + assert(event.args); + assert(token0); + assert(token1); + + // amounts - 0/1 are token deltas: can be positive or negative + const amount0 = convertTokenToDecimal(event.args.amount0, token0.decimals); + const amount1 = convertTokenToDecimal(event.args.amount1, token1.decimals); + + // need absolute amounts for volume + let amount0Abs = amount0; + if (amount0.lt(ZERO_BD)) { + amount0Abs = amount0.mul(BigNumber.from("-1")); + } + + let amount1Abs = amount1; + if (amount1.lt(ZERO_BD)) { + amount1Abs = amount1.mul(BigNumber.from("-1")); + } + assert(bundle); + + const amount0ETH = amount0Abs.mul(token0.derivedETH); + const amount1ETH = amount1Abs.mul(token1.derivedETH); + const amount0USD = amount0ETH.mul(bundle.ethPriceUSD); + const amount1USD = amount1ETH.mul(bundle.ethPriceUSD); + + // get amount that should be tracked only - div 2 because cant count both input and output as volume + const amountTotalUSDTracked = ( + await getTrackedAmountUSD(amount0Abs, token0, amount1Abs, token1) + ).div(BigNumber.from("2")); + const amountTotalETHTracked = safeDiv( + amountTotalUSDTracked, + BigNumber.from(bundle.ethPriceUSD), + ); + const amountTotalUSDUntracked = amount0USD + .add(amount1USD) + .div(BigNumber.from("2")); + + const feesETH = amountTotalETHTracked + .mul(pool.feeTier) + .div(BigNumber.from("1000000")); + const feesUSD = amountTotalUSDTracked + .mul(pool.feeTier) + .div(BigNumber.from("1000000")); + + assert(factory); + // global updates + factory.txCount = factory.txCount + ONE_BI; //BigNumber.from(factory.txCount).add(ONE_BI).toBigInt() + factory.totalVolumeETH = + factory.totalVolumeETH + amountTotalETHTracked.toNumber(); //BigNumber.from(factory.totalVolumeETH).add(amountTotalETHTracked).toNumber() + factory.totalVolumeUSD = + factory.totalVolumeUSD + amountTotalUSDTracked.toNumber(); // BigNumber.from(factory.totalVolumeUSD).add(amountTotalUSDTracked).toNumber() + factory.untrackedVolumeUSD = + factory.untrackedVolumeUSD + amountTotalUSDUntracked.toNumber(); // BigNumber.from(factory.untrackedVolumeUSD).add(amountTotalUSDUntracked).toNumber() + factory.totalFeesETH = factory.totalFeesETH + feesETH.toNumber(); // BigNumber.from(factory.totalFeesETH).add(feesETH).toNumber() + factory.totalFeesUSD = factory.totalFeesUSD + feesUSD.toNumber(); // BigNumber.from(factory.totalFeesUSD).add(feesUSD).toNumber() + + // reset aggregate tvl before individual pool tvl updates + // const currentPoolTvlETH = pool.totalValueLockedETH + factory.totalValueLockedETH = + factory.totalValueLockedETH - pool.totalValueLockedETH; //BigNumber.from(factory.totalValueLockedETH).sub(currentPoolTvlETH).toNumber() + + // pool volume + pool.volumeToken0 = pool.volumeToken0 + amount0Abs.toNumber(); //BigNumber.from(pool.volumeToken0).add(amount0Abs).toNumber() + pool.volumeToken1 = pool.volumeToken1 + amount1Abs.toNumber(); //BigNumber.from(pool.volumeToken1).add(amount1Abs).toNumber() + pool.volumeUSD = pool.volumeUSD + amountTotalUSDTracked.toNumber(); + pool.untrackedVolumeUSD = + pool.untrackedVolumeUSD + amountTotalUSDUntracked.toNumber(); + pool.feesUSD = pool.feesUSD + feesUSD.toNumber(); //BigNumber.from(pool.feesUSD).add(feesUSD).toNumber() + pool.txCount = pool.txCount + ONE_BI; //BigNumber.from(pool.txCount).add(ONE_BI).toBigInt() + + // Update the pool with the new active liquidity, price, and tick. + pool.liquidity = event.args.liquidity.toBigInt(); + pool.tick = BigInt(event.args.tick); + pool.sqrtPrice = event.args.sqrtPriceX96.toBigInt(); + pool.totalValueLockedToken0 = + pool.totalValueLockedToken0 + amount0.toNumber(); // BigNumber.from(pool.totalValueLockedToken0).add(amount0).toNumber() + pool.totalValueLockedToken1 = + pool.totalValueLockedToken1 + amount1.toNumber(); // BigNumber.from(pool.totalValueLockedToken1).add(amount1).toNumber() + + // update token0 data + token0.volume = token0.volume + amount0Abs.toNumber(); + token0.totalValueLocked = token0.totalValueLocked + amount0.toNumber(); + token0.volumeUSD = token0.volumeUSD + amountTotalUSDTracked.toNumber(); + token0.untrackedVolumeUSD = + token0.untrackedVolumeUSD + amountTotalUSDUntracked.toNumber(); + token0.feesUSD = token0.feesUSD + feesUSD.toNumber(); + token0.txCount = token0.txCount + ONE_BI; + + // update token1 data + token1.volume = token1.volume + amount1Abs.toNumber(); + token1.totalValueLocked = token1.totalValueLocked + amount1.toNumber(); + token1.volumeUSD = token1.volumeUSD + amountTotalUSDTracked.toNumber(); + token1.untrackedVolumeUSD = + token1.untrackedVolumeUSD + amountTotalUSDUntracked.toNumber(); + token1.feesUSD = token1.feesUSD + feesUSD.toNumber(); + token1.txCount = token1.txCount + ONE_BI; + + // updated pool ratess + const prices = sqrtPriceX96ToTokenPrices(pool.sqrtPrice, token0, token1); + + pool.token0Price = prices[0]; + pool.token1Price = prices[1]; + + // update USD pricing + bundle.ethPriceUSD = ethPrice.toNumber(); + + const [derivedETH0, derivedETH1] = await Promise.all([ + findEthPerToken(token0), + findEthPerToken(token1), + ]); + token0.derivedETH = derivedETH0.toNumber(); + token1.derivedETH = derivedETH1.toNumber(); + + /** + * Things afffected by new USD rates + */ + + pool.totalValueLockedETH = + pool.totalValueLockedToken0 * token0.derivedETH + + pool.totalValueLockedToken1 * token1.derivedETH; + pool.totalValueLockedUSD = pool.totalValueLockedETH * bundle.ethPriceUSD; + + factory.totalValueLockedETH = + factory.totalValueLockedETH + pool.totalValueLockedETH; + factory.totalValueLockedUSD = + factory.totalValueLockedETH * bundle.ethPriceUSD; + + token0.totalValueLockedUSD = + token0.totalValueLocked * token0.derivedETH * bundle.ethPriceUSD; + token1.totalValueLockedUSD = + token1.totalValueLocked * token1.derivedETH * bundle.ethPriceUSD; + + // create Swap event + // const transaction = await loadTransaction(event) + const swap = Swap.create({ + id: transaction.id + "#" + pool.txCount.toString(), + transactionId: transaction.id, + timestamp: transaction.timestamp, + poolId: pool.id, + token0Id: pool.token0Id, + token1Id: pool.token1Id, + sender: event.args.sender, + origin: event.transaction.from, + recipient: event.args.recipient, + amount0: amount0.toNumber(), + amount1: amount1.toNumber(), + amountUSD: amountTotalUSDTracked.toNumber(), + tick: BigInt(event.args.tick), + sqrtPriceX96: event.args.sqrtPriceX96.toBigInt(), + logIndex: BigInt(event.logIndex), + }); + + // update fee growth + pool.feeGrowthGlobal0X128 = feeGrowthGlobal0X128.toBigInt(); + pool.feeGrowthGlobal1X128 = feeGrowthGlobal1X128.toBigInt(); + + // interval data + const [ + pancakeSwapDayData, + poolDayData, + poolHourData, + token0DayData, + token1DayData, + token0HourData, + token1HourData, + ] = await Promise.all([ + updatePancakeSwapDayData(event), + updatePoolDayData(event), + updatePoolHourData(event), + updateTokenDayData(token0, event), + updateTokenDayData(token1, event), + updateTokenHourData(token0, event), + updateTokenHourData(token1, event), + ]); + + // update volume metrics + pancakeSwapDayData.volumeETH = + pancakeSwapDayData.volumeETH + amountTotalETHTracked.toNumber(); + pancakeSwapDayData.volumeUSD = + pancakeSwapDayData.volumeUSD + amountTotalUSDTracked.toNumber(); + pancakeSwapDayData.feesUSD = pancakeSwapDayData.feesUSD + feesUSD.toNumber(); + + poolDayData.volumeUSD = + poolDayData.volumeUSD + amountTotalUSDTracked.toNumber(); + poolDayData.volumeToken0 = poolDayData.volumeToken0 + amount0Abs.toNumber(); + poolDayData.volumeToken1 = poolDayData.volumeToken1 + amount1Abs.toNumber(); + poolDayData.feesUSD = poolDayData.feesUSD + feesUSD.toNumber(); + + poolHourData.volumeUSD = + poolHourData.volumeUSD + amountTotalUSDTracked.toNumber(); + poolHourData.volumeToken0 = poolHourData.volumeToken0 + amount0Abs.toNumber(); + poolHourData.volumeToken1 = poolHourData.volumeToken1 + amount1Abs.toNumber(); + poolHourData.feesUSD = poolHourData.feesUSD + feesUSD.toNumber(); + + token0DayData.volume = token0DayData.volume + amount0Abs.toNumber(); + token0DayData.volumeUSD = + token0DayData.volumeUSD + amountTotalUSDTracked.toNumber(); + token0DayData.untrackedVolumeUSD = + token0DayData.untrackedVolumeUSD + amountTotalUSDTracked.toNumber(); + token0DayData.feesUSD = token0DayData.feesUSD + feesUSD.toNumber(); + + token0HourData.volume = token0HourData.volume + amount0Abs.toNumber(); + token0HourData.volumeUSD = + token0HourData.volumeUSD + amountTotalUSDTracked.toNumber(); + token0HourData.untrackedVolumeUSD = + token0HourData.untrackedVolumeUSD + amountTotalUSDTracked.toNumber(); + token0HourData.feesUSD = token0HourData.feesUSD + feesUSD.toNumber(); + + token1DayData.volume = token1DayData.volume + amount1Abs.toNumber(); + token1DayData.volumeUSD = + token1DayData.volumeUSD + amountTotalUSDTracked.toNumber(); + token1DayData.untrackedVolumeUSD = + token1DayData.untrackedVolumeUSD + amountTotalUSDTracked.toNumber(); + token1DayData.feesUSD = token1DayData.feesUSD + feesUSD.toNumber(); + + token1HourData.volume = token1HourData.volume + amount1Abs.toNumber(); + token1HourData.volumeUSD = + token1HourData.volumeUSD + amountTotalUSDTracked.toNumber(); + token1HourData.untrackedVolumeUSD = + token1HourData.untrackedVolumeUSD + amountTotalUSDTracked.toNumber(); + token1HourData.feesUSD = token1HourData.feesUSD + feesUSD.toNumber(); + + await Promise.all([ + bundle.save(), + swap.save(), + token0DayData.save(), + token1DayData.save(), + pancakeSwapDayData.save(), + poolDayData.save(), + token0HourData.save(), + token1HourData.save(), + poolHourData.save(), + factory.save(), + pool.save(), + token0.save(), + token1.save(), + ]); + + // Update inner vars of current or crossed ticks + const newTick = BigNumber.from(pool.tick); + const tickSpacing = feeTierToTickSpacing(BigNumber.from(pool.feeTier)); + const modulo = newTick.mod(tickSpacing); + if (modulo.eq(ZERO_BI)) { + // Current tick is initialized and needs to be updated + await loadTickUpdateFeeVarsAndSave(newTick.toString(), event); + } + + const numIters = BigNumber.from(oldTick).sub(newTick).abs().div(tickSpacing); + assert(oldTick); + + if (numIters.gt(BigNumber.from(100))) { + // In case more than 100 ticks need to be updated ignore the update in + // order to avoid timeouts. From testing this behavior occurs only upon + // pool initialization. This should not be a big issue as the ticks get + // updated later. For early users this error also disappears when calling + // collect + } else if (newTick.gt(oldTick)) { + const firstInitialized = BigNumber.from(oldTick).add( + BigNumber.from(tickSpacing).add(modulo), + ); + for (let i = firstInitialized; i.lte(newTick); i = i.add(tickSpacing)) { + await loadTickUpdateFeeVarsAndSave(i.toString(), event); + } + } else if (newTick.lt(oldTick)) { + const firstInitialized = BigNumber.from(oldTick).sub(modulo); + for (let i = firstInitialized; i.gte(newTick); i = i.sub(tickSpacing)) { + await loadTickUpdateFeeVarsAndSave(i.toString(), event); + } + } +} + +export async function handleFlash( + event: EthereumLog, +): Promise { + // update fee growth + const pool = await Pool.get(event.address); + const poolContract = Pool__factory.connect(event.address, api); + + const [feeGrowthGlobal0X128, feeGrowthGlobal1X128] = await Promise.all([ + poolContract.feeGrowthGlobal0X128(), + poolContract.feeGrowthGlobal1X128(), + ]); + assert(pool); + pool.feeGrowthGlobal0X128 = feeGrowthGlobal0X128.toBigInt(); + pool.feeGrowthGlobal1X128 = feeGrowthGlobal1X128.toBigInt(); + await pool.save(); +} + +async function updateTickFeeVarsAndSave( + tick: Tick, + event: EthereumLog, +): Promise { + const poolAddress = event.address; + // not all ticks are initialized so obtaining null is expected behavior + const poolContract = Pool__factory.connect(poolAddress, api); + const tickResult = await poolContract.ticks(tick.tickIdx); + tick.feeGrowthOutside0X128 = tickResult[2].toBigInt(); + tick.feeGrowthOutside1X128 = tickResult[3].toBigInt(); + await tick.save(); + + await updateTickDayData(tick, event); +} + +async function loadTickUpdateFeeVarsAndSave( + tickId: string, + event: EthereumLog, +): Promise { + const poolAddress = event.address; + const tick = await Tick.get(`${poolAddress}#${tickId.toString()}`); + if (tick !== undefined) { + await updateTickFeeVarsAndSave(tick, event); + } +} diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/factory.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/factory.ts new file mode 100644 index 00000000..3885c256 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/factory.ts @@ -0,0 +1,191 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { + WHITELIST_TOKENS, + fetchTokenSymbol, + fetchTokenName, + fetchTokenTotalSupply, + fetchTokenDecimals, + ADDRESS_ZERO, + FACTORY_ADDRESS, + ONE_BI, + ZERO_BI, +} from "./utils"; +import { + Pool, + Token, + Bundle, + Factory, + createPoolDatasource, + WhiteListPools, +} from "../types"; +import { EthereumLog } from "@subql/types-ethereum"; +import { PoolCreatedEvent } from "../types/contracts/Factory"; +import assert from "assert"; + +export async function handlePoolCreated( + event: EthereumLog, +): Promise { + // temp fix + if (event.address === "0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248") { + return; + } + assert(event.args); + + await createPoolDatasource({ + address: event.args.pool, + }); + + // load factory + let factory = await Factory.get(FACTORY_ADDRESS); + if (factory === undefined || factory === undefined) { + factory = Factory.create({ + id: FACTORY_ADDRESS, + poolCount: ZERO_BI, + totalVolumeETH: 0, + totalVolumeUSD: 0, + untrackedVolumeUSD: 0, + totalFeesUSD: 0, + totalFeesETH: 0, + totalValueLockedETH: 0, + totalValueLockedUSD: 0, + totalValueLockedUSDUntracked: 0, + totalValueLockedETHUntracked: 0, + txCount: ZERO_BI, + owner: ADDRESS_ZERO, + }); + // create new bundle for tracking eth price + const bundle = Bundle.create({ + id: "1", + ethPriceUSD: 0, + }); + + await bundle.save(); + } + + let [token0, token1] = await Promise.all([ + Token.get(event.args.token0), + Token.get(event.args.token1), + ]); + // fetch info if nul + if (token0 === undefined || token0 == null) { + const [symbol, name, totalSupply, decimals] = await Promise.all([ + fetchTokenSymbol(event.args.token0), + fetchTokenName(event.args.token0), + fetchTokenTotalSupply(event.args.token0).then((r) => r.toBigInt()), + fetchTokenDecimals(event.args.token0), + ]); + // bail if we couldn't figure out the decimals + if (!decimals) { + return; + } + + token0 = Token.create({ + id: event.args.token0, + symbol, + name, + totalSupply, + decimals: decimals.toBigInt(), + derivedETH: 0, + volume: 0, + volumeUSD: 0, + feesUSD: 0, + untrackedVolumeUSD: 0, + totalValueLocked: 0, + totalValueLockedUSD: 0, + totalValueLockedUSDUntracked: 0, + txCount: ZERO_BI, + poolCount: ZERO_BI, + }); + } + + if (token1 === undefined || token1 == null) { + const [symbol, name, totalSupply, decimals] = await Promise.all([ + fetchTokenSymbol(event.args.token1), + fetchTokenName(event.args.token1), + fetchTokenTotalSupply(event.args.token1).then((r) => r.toBigInt()), + fetchTokenDecimals(event.args.token1), + ]); + // bail if we couldn't figure out the decimals + if (!decimals) { + return; + } + + token1 = Token.create({ + id: event.args.token1, + symbol, + name, + totalSupply, + decimals: decimals.toBigInt(), + derivedETH: 0, + volume: 0, + volumeUSD: 0, + feesUSD: 0, + untrackedVolumeUSD: 0, + totalValueLocked: 0, + totalValueLockedUSD: 0, + totalValueLockedUSDUntracked: 0, + txCount: ZERO_BI, + poolCount: ZERO_BI, + }); + } + + factory.poolCount = factory.poolCount + ONE_BI; + + const pool = Pool.create({ + id: event.args.pool, + token0Id: token0.id, + token1Id: token1.id, + feeTier: BigInt(event.args.fee), + createdAtTimestamp: event.block.timestamp, + createdAtBlockNumber: BigInt(event.blockNumber), + liquidityProviderCount: ZERO_BI, + txCount: ZERO_BI, + liquidity: ZERO_BI, + sqrtPrice: ZERO_BI, + feeGrowthGlobal0X128: ZERO_BI, + feeGrowthGlobal1X128: ZERO_BI, + token0Price: 0, + token1Price: 0, + observationIndex: ZERO_BI, + totalValueLockedToken0: 0, + totalValueLockedToken1: 0, + totalValueLockedUSD: 0, + totalValueLockedETH: 0, + totalValueLockedUSDUntracked: 0, + volumeToken0: 0, + volumeToken1: 0, + volumeUSD: 0, + feesUSD: 0, + untrackedVolumeUSD: 0, + collectedFeesToken0: 0, + collectedFeesToken1: 0, + collectedFeesUSD: 0, + }); + + // update white listed pools + if (WHITELIST_TOKENS.includes(token0.id)) { + const newPool = WhiteListPools.create({ + id: `${pool.id + token1.id}`, + tokenId: token1.id, + poolId: pool.id, + }); + await newPool.save(); + } + if (WHITELIST_TOKENS.includes(token1.id)) { + const newPool = WhiteListPools.create({ + id: `${pool.id + token0.id}`, + tokenId: token0.id, + poolId: pool.id, + }); + await newPool.save(); + } + + await Promise.all([ + token0.save(), + token1.save(), // create the tracked contract based on the template + pool.save(), + factory.save(), + ]); +} diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/mappingHandlers.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/mappingHandlers.ts new file mode 100644 index 00000000..8e3c91ab --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/mappingHandlers.ts @@ -0,0 +1,7 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export * from "./core"; +export * from "./factory"; +export * from "./position-manager"; +export * from "./utils"; diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/position-manager.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/position-manager.ts new file mode 100644 index 00000000..0fda1c93 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/position-manager.ts @@ -0,0 +1,257 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { Position, PositionSnapshot, Token } from "../types"; +import { NonfungiblePositionManager__factory } from "../types/contracts"; +import { + IncreaseLiquidityEvent, + DecreaseLiquidityEvent, + CollectEvent, + TransferEvent, +} from "../types/contracts/NonfungiblePositionManager"; +import { + ADDRESS_ZERO, + factoryContract, + ZERO_BD, + ZERO_BI, + convertTokenToDecimal, + loadTransaction, +} from "./utils"; +import { EthereumLog } from "@subql/types-ethereum"; +import { BigNumber } from "ethers"; +import assert = require("assert"); + +async function getPosition( + event: EthereumLog, + tokenId: BigNumber, +): Promise { + let position = await Position.get(tokenId.toString()); + + if (position === undefined) { + const contract = NonfungiblePositionManager__factory.connect( + event.address, + api, + ); + let positionResult; + try { + positionResult = await contract.positions(tokenId); + } catch (e) { + logger.warn( + `Contract ${event.address}, could not get position with tokenId ${tokenId}`, + ); + return null; + } + logger.warn(positionResult[2]); + logger.warn(positionResult[3]); + logger.warn(positionResult[4]); + logger.warn(factoryContract.address); + + const [poolAddress, transaction] = await Promise.all([ + factoryContract.getPool( + positionResult[2], + positionResult[3], + positionResult[4], + ), + loadTransaction(event), + ]); + // the following call reverts in situations where the position is minted + // and deleted in the same block - from my investigation this happens + // in calls from BancorSwap + // (e.g. 0xf7867fa19aa65298fadb8d4f72d0daed5e836f3ba01f0b9b9631cdc6c36bed40) + + // The owner gets correctly updated in the Transfer handler + position = Position.create({ + id: tokenId.toString(), + owner: ADDRESS_ZERO, + poolId: poolAddress, + token0Id: positionResult[2], + token1Id: positionResult[3], + tickLowerId: `${poolAddress}#${positionResult[5].toString()}`, + tickUpperId: `${poolAddress}#${positionResult[6].toString()}`, + liquidity: ZERO_BI, + depositedToken0: 0, //ZERO_BD.toNumber(), + depositedToken1: 0, //ZERO_BD.toNumber(), + withdrawnToken0: 0, //ZERO_BD.toNumber(), + withdrawnToken1: 0, //ZERO_BD.toNumber(), + collectedFeesToken0: 0, //ZERO_BD.toNumber(), + collectedFeesToken1: 0, //ZERO_BD.toNumber(), + transactionId: transaction.id, + feeGrowthInside0LastX128: positionResult[8].toBigInt(), + feeGrowthInside1LastX128: positionResult[9].toBigInt(), + }); + } + return position; +} + +async function updateFeeVars( + position: Position, + event: EthereumLog, + tokenId: BigNumber, +): Promise { + const positionManagerContract = NonfungiblePositionManager__factory.connect( + event.address, + api, + ); + const positionResult = await positionManagerContract.positions(tokenId); + position.feeGrowthInside0LastX128 = positionResult[8].toBigInt(); + position.feeGrowthInside1LastX128 = positionResult[9].toBigInt(); + return position; +} + +async function savePositionSnapshot( + position: Position, + event: EthereumLog, +): Promise { + const positionSnapshot = PositionSnapshot.create({ + id: `${position.id}#${event.blockNumber.toString()}`, + owner: position.owner, + poolId: position.poolId, + positionId: position.id, + blockNumber: BigInt(event.blockNumber), + timestamp: event.block.timestamp, + liquidity: position.liquidity, + depositedToken0: position.depositedToken0, + depositedToken1: position.depositedToken1, + withdrawnToken0: position.withdrawnToken0, + withdrawnToken1: position.withdrawnToken1, + collectedFeesToken0: position.collectedFeesToken0, + collectedFeesToken1: position.collectedFeesToken1, + transactionId: (await loadTransaction(event)).id, + feeGrowthInside0LastX128: position.feeGrowthInside0LastX128, + feeGrowthInside1LastX128: position.feeGrowthInside1LastX128, + }); + await positionSnapshot.save(); +} + +export async function handleIncreaseLiquidity( + event: EthereumLog, +): Promise { + // temp fix + if (BigNumber.from(event.blockNumber).eq(14317993)) { + return; + } + + assert(event.args); + const position = await getPosition(event, event.args.tokenId); + + // position was not able to be fetched + if (position == undefined || position === null) { + return; + } + + // temp fix + if (position.poolId === "0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248") { + return; + } + assert(event.args); + + const [token0, token1] = await Promise.all([ + Token.get(position.token0Id), + Token.get(position.token1Id), + ]); + assert(token0); + assert(token1); + + const amount0 = convertTokenToDecimal(event.args.amount0, token0.decimals); + const amount1 = convertTokenToDecimal(event.args.amount1, token1.decimals); + + position.liquidity = BigNumber.from(position.liquidity) + .add(event.args.liquidity) + .toBigInt(); + + position.depositedToken0 = BigNumber.from(position.depositedToken0) + .add(amount0) + .toNumber(); + position.depositedToken1 = BigNumber.from(position.depositedToken1) + .add(amount1) + .toNumber(); + + await updateFeeVars(position, event, event.args.tokenId); + + await position.save(); + + await savePositionSnapshot(position, event); +} + +export async function handleDecreaseLiquidity( + event: EthereumLog, +): Promise { + // temp fix + if (event.blockNumber == 14317993) { + return; + } + assert(event.args); + + let position = await getPosition(event, event.args.tokenId); + + // position was not able to be fetched + if (position === undefined || position === null) { + return; + } + + // temp fix + if (position.poolId === "0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248") { + return; + } + + const [token0, token1] = await Promise.all([ + Token.get(position.token0Id), + Token.get(position.token1Id), + ]); + assert(token0); + assert(token1); + + const amount0 = convertTokenToDecimal(event.args.amount0, token0.decimals); + const amount1 = convertTokenToDecimal(event.args.amount1, token1.decimals); + + position.liquidity = position.liquidity - event.args.liquidity.toBigInt(); + position.withdrawnToken0 = amount0.add(position.withdrawnToken0).toNumber(); + position.withdrawnToken1 = amount1.add(position.withdrawnToken1).toNumber(); + + position = await updateFeeVars(position, event, event.args.tokenId); + await position.save(); + await savePositionSnapshot(position, event); +} + +export async function handleCollect( + event: EthereumLog, +): Promise { + assert(event.args); + let position = await getPosition(event, event.args.tokenId); + // position was not able to be fetched + if (position === undefined || position === null) { + return; + } + if (position.poolId === "0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248") { + return; + } + + const token0 = await Token.get(position.token0Id); + assert(token0); + const amount0 = convertTokenToDecimal(event.args.amount0, token0.decimals); + position.collectedFeesToken0 = amount0 + .add(position.collectedFeesToken0) + .toNumber(); + position.collectedFeesToken1 = amount0 + .add(position.collectedFeesToken1) + .toNumber(); + + position = await updateFeeVars(position, event, event.args.tokenId); + + await Promise.all([position.save(), savePositionSnapshot(position, event)]); +} + +export async function handleTransfer( + event: EthereumLog, +): Promise { + assert(event.args); + const position = await getPosition(event, event.args.tokenId); + + // position was not able to be fetched + if (position === undefined || position === null) { + return; + } + + position.owner = event.args.to; + await Promise.all([position.save(), savePositionSnapshot(position, event)]); +} diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/constants.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/constants.ts new file mode 100644 index 00000000..41945313 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/constants.ts @@ -0,0 +1,29 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { BigNumber } from "@ethersproject/bignumber"; +import { Factory__factory } from "../../types/contracts/factories/Factory__factory"; + +export const ADDRESS_ZERO = "0x0000000000000000000000000000000000000000"; +export const FACTORY_ADDRESS = "0x1F98431c8aD98523631AE4a59f267346ea31F984"; + +export const ZERO_BI = BigInt(0); +export const ONE_BI = BigInt(1); +// export let ZERO_BD = 0 +// export let ONE_BD = 1 +// export let BI_18 = 18 + +// export let ZERO_BI = BigNumber.from('0') +// export let ONE_BI = BigNumber.from('1') +export const ZERO_BD = BigNumber.from("0"); +export const ONE_BD = BigNumber.from("1"); +export const BI_18 = BigNumber.from(18); + +// export let ZERO_BI = BigInt.fromI32(0) +// export let ONE_BI = BigInt.fromI32(1) +// export let ZERO_BD = BigDecimal.fromString('0') +// export let ONE_BD = BigDecimal.fromString('1') +// export let BI_18 = BigInt.fromI32(18) + +// export let factoryContract = FactoryContract.bind(Address.fromString(FACTORY_ADDRESS)) +export const factoryContract = Factory__factory.connect(FACTORY_ADDRESS, api); diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/index.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/index.ts new file mode 100644 index 00000000..a5609183 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/index.ts @@ -0,0 +1,10 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export * from "./constants"; +export * from "./intervalUpdates"; +export * from "./pricing"; +export * from "./staticTokenDefinition"; +export * from "./tick"; +export * from "./token"; +export * from "./utils"; diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/intervalUpdates.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/intervalUpdates.ts new file mode 100644 index 00000000..f7590993 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/intervalUpdates.ts @@ -0,0 +1,305 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { ZERO_BD, ZERO_BI, ONE_BI, FACTORY_ADDRESS } from "./constants"; +import { + PancakeSwapDayData, + Factory, + Pool, + PoolDayData, + Token, + TokenDayData, + TokenHourData, + Bundle, + PoolHourData, + TickDayData, + Tick, +} from "../../types"; +import { EthereumLog } from "@subql/types-ethereum"; +import { BigNumber } from "@ethersproject/bignumber"; +import assert from "assert"; + +/** + * Tracks global aggregate data over daily windows + * @param event + */ +// TODO : event type ? +export async function updatePancakeSwapDayData( + event: EthereumLog, +): Promise { + const timestamp = parseInt(event.block.timestamp.toString()); + const dayID = timestamp / 86400; // rounded + const dayStartTimestamp = Number(event.block.timestamp); + + let [pancakeSwap, pancakeSwapDayData] = await Promise.all([ + Factory.get(FACTORY_ADDRESS), + PancakeSwapDayData.get(dayID.toString()), + ]); + assert(pancakeSwap); + if (pancakeSwapDayData === undefined) { + pancakeSwapDayData = PancakeSwapDayData.create({ + id: dayID.toString(), + date: dayStartTimestamp, + volumeETH: 0, + volumeUSD: 0, + volumeUSDUntracked: 0, + feesUSD: 0, + tvlUSD: pancakeSwap.totalValueLockedUSD, + txCount: pancakeSwap.txCount, + }); + } + await pancakeSwapDayData.save(); + return pancakeSwapDayData; +} + +export async function updatePoolDayData( + event: EthereumLog, +): Promise { + const timestamp = BigNumber.from(event.block.timestamp); + const dayID = timestamp.div(86400); + const dayStartTimestamp = dayID.mul(86400); + const dayPoolID = `${event.address}-${dayID.toString()}`; + let [pool, poolDayData] = await Promise.all([ + Pool.get(event.address), + PoolDayData.get(dayPoolID), + ]); + assert(pool); + if (poolDayData === undefined) { + poolDayData = PoolDayData.create({ + id: dayPoolID, + date: dayStartTimestamp.toNumber(), + poolId: pool.id, + // things that dont get initialized always + volumeToken0: 0, + volumeToken1: 0, + volumeUSD: 0, + feesUSD: 0, + txCount: ZERO_BI, + feeGrowthGlobal0X128: ZERO_BI, + feeGrowthGlobal1X128: ZERO_BI, + open: pool.token0Price, + high: pool.token0Price, + low: pool.token0Price, + close: pool.token0Price, + liquidity: pool.liquidity, + sqrtPrice: pool.sqrtPrice, + token0Price: pool.token0Price, + token1Price: pool.token1Price, + tick: pool.tick, + tvlUSD: pool.totalValueLockedUSD, + }); + } + + if (pool.token0Price > poolDayData.high) { + poolDayData.high = pool.token0Price; + } + if (pool.token0Price < poolDayData.low) { + poolDayData.low = pool.token0Price; + } + + poolDayData.txCount = poolDayData.txCount + ONE_BI; + + await poolDayData.save(); + + return poolDayData; +} + +export async function updatePoolHourData( + event: EthereumLog, +): Promise { + const timestamp = BigNumber.from(event.block.timestamp); + const hourIndex = timestamp.div(3600); // get unique hour within unix history + const hourStartUnix = hourIndex.mul(3600); // want the rounded effect + const hourPoolID = event.address + // .toHexString() + .concat("-") + .concat(hourIndex.toString()); + let [pool, poolHourData] = await Promise.all([ + await Pool.get(event.address), + await PoolHourData.get(hourPoolID), + ]); + assert(pool); + if (poolHourData === undefined) { + poolHourData = PoolHourData.create({ + id: hourPoolID, + periodStartUnix: hourStartUnix.toNumber(), + poolId: pool.id, + // things that dont get initialized always + volumeToken0: 0, + volumeToken1: 0, + volumeUSD: 0, + feesUSD: 0, + txCount: ZERO_BI, + feeGrowthGlobal0X128: ZERO_BI, + feeGrowthGlobal1X128: ZERO_BI, + open: pool.token0Price, + high: pool.token0Price, + low: pool.token0Price, + close: pool.token0Price, + liquidity: pool.liquidity, + sqrtPrice: pool.sqrtPrice, + token0Price: pool.token0Price, + token1Price: pool.token1Price, + tick: pool.tick, + tvlUSD: pool.totalValueLockedUSD, + }); + } + + if (pool.token0Price > poolHourData.high) { + poolHourData.high = pool.token0Price; + } + if (pool.token0Price < poolHourData.low) { + poolHourData.low = pool.token0Price; + } + poolHourData.liquidity = pool.liquidity; + poolHourData.sqrtPrice = pool.sqrtPrice; + poolHourData.token0Price = pool.token0Price; + poolHourData.token1Price = pool.token1Price; + poolHourData.feeGrowthGlobal0X128 = pool.feeGrowthGlobal0X128; + poolHourData.feeGrowthGlobal1X128 = pool.feeGrowthGlobal1X128; + poolHourData.close = pool.token0Price; + poolHourData.tick = pool.tick; + poolHourData.tvlUSD = pool.totalValueLockedUSD; + poolHourData.txCount = poolHourData.txCount + ONE_BI; + + await poolHourData.save(); + + // test + return poolHourData; +} + +export async function updateTokenDayData( + token: Token, + event: EthereumLog, +): Promise { + const timestamp = BigNumber.from(event.block.timestamp); + const dayID = timestamp.div(86400); + const dayStartTimestamp = dayID.mul(86400); + const tokenDayID = token.id.toString().concat("-").concat(dayID.toString()); + + let [tokenDayData, bundle] = await Promise.all([ + TokenDayData.get(tokenDayID), + Bundle.get("1"), + ]); + assert(bundle); + const tokenPrice = token.derivedETH * bundle.ethPriceUSD; + + if (tokenDayData === undefined) { + tokenDayData = TokenDayData.create({ + id: tokenDayID, + date: dayStartTimestamp.toNumber(), + tokenId: token.id, + volume: 0, + volumeUSD: 0, + feesUSD: 0, + untrackedVolumeUSD: 0, + open: tokenPrice, + high: tokenPrice, + low: tokenPrice, + close: tokenPrice, + priceUSD: token.derivedETH * bundle.ethPriceUSD, + totalValueLocked: token.totalValueLocked, + totalValueLockedUSD: token.totalValueLockedUSD, + }); + } + + if (tokenPrice > tokenDayData.high) { + tokenDayData.high = tokenPrice; + } + + if (tokenPrice < tokenDayData.low) { + tokenDayData.low = tokenPrice; + } + + await tokenDayData.save(); + + return tokenDayData; +} + +export async function updateTokenHourData( + token: Token, + event: EthereumLog, +): Promise { + const timestamp = BigNumber.from(event.block.timestamp); + const hourIndex = timestamp.div(3600); // get unique hour within unix history + const hourStartUnix = hourIndex.mul(3600); // want the rounded effect + const tokenHourID = token.id + .toString() + .concat("-") + .concat(hourIndex.toString()); + let [tokenHourData, bundle] = await Promise.all([ + TokenHourData.get(tokenHourID), + Bundle.get("1"), + ]); + assert(bundle); + const tokenPrice = BigNumber.from(token.derivedETH).mul(bundle.ethPriceUSD); + if (tokenHourData === undefined) { + tokenHourData = TokenHourData.create({ + id: tokenHourID, + periodStartUnix: hourStartUnix.toNumber(), + tokenId: token.id, + volume: 0, + volumeUSD: 0, + untrackedVolumeUSD: 0, + feesUSD: 0, + open: tokenPrice.toNumber(), + high: tokenPrice.toNumber(), + low: tokenPrice.toNumber(), + close: tokenPrice.toNumber(), + totalValueLocked: token.totalValueLocked, + totalValueLockedUSD: token.totalValueLockedUSD, + priceUSD: tokenPrice.toNumber(), + }); + } + + if (tokenPrice.gt(tokenHourData.high)) { + tokenHourData.high = tokenPrice.toNumber(); + } + + if (tokenPrice.lt(tokenHourData.low)) { + tokenHourData.low = tokenPrice.toNumber(); + } + + await tokenHourData.save(); + + return tokenHourData; +} + +export async function updateTickDayData( + tick: Tick, + event: EthereumLog, +): Promise { + const timestamp = BigNumber.from(event.block.timestamp); + const dayID = timestamp.div(86400); + const dayStartTimestamp = dayID.mul(86400); + const tickDayDataID = tick.id.concat("-").concat(dayID.toString()); + let tickDayData = await TickDayData.get(tickDayDataID); + if (tickDayData === undefined) { + tickDayData = TickDayData.create({ + id: tickDayDataID, + date: dayStartTimestamp.toNumber(), + poolId: tick.poolId, + tickId: tick.id, + liquidityGross: tick.liquidityGross, + liquidityNet: tick.liquidityNet, + volumeToken0: tick.volumeToken0, + volumeToken1: tick.volumeToken0, + volumeUSD: tick.volumeUSD, + feesUSD: tick.feesUSD, + feeGrowthOutside0X128: tick.feeGrowthOutside0X128, + feeGrowthOutside1X128: tick.feeGrowthOutside1X128, + }); + } + tickDayData.liquidityGross = tick.liquidityGross; + tickDayData.liquidityNet = tick.liquidityNet; + tickDayData.volumeToken0 = tick.volumeToken0; + tickDayData.volumeToken1 = tick.volumeToken0; + tickDayData.volumeUSD = tick.volumeUSD; + tickDayData.feesUSD = tick.feesUSD; + tickDayData.feeGrowthOutside0X128 = tick.feeGrowthOutside0X128; + tickDayData.feeGrowthOutside1X128 = tick.feeGrowthOutside1X128; + + await tickDayData.save(); + + return tickDayData; +} diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/pricing.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/pricing.ts new file mode 100644 index 00000000..93bd94d0 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/pricing.ts @@ -0,0 +1,184 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { ONE_BD, ZERO_BD, ZERO_BI } from "./constants"; +import { Bundle, Pool, Token, WhiteListPools } from "../../types"; +import { BigNumber } from "@ethersproject/bignumber"; +import { exponentToBigDecimal, safeDiv, safeDivNumToNum } from "./index"; +import { formatUnits, parseUnits } from "@ethersproject/units"; +import assert from "assert"; +import {GetOptions } from "@subql/types-core"; + +const WETH_ADDRESS = "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c"; +const USDC_WETH_03_POOL = "0x36696169c63e42cd08ce11f5deebbcebae652050"; + +// token where amounts should contribute to tracked volume and liquidity +// usually tokens that many tokens are paired with s +export const WHITELIST_TOKENS: string[] = + "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c,0x55d398326f99059ff775485246999027b3197955,0xe9e7cea3dedca5984780bafc599bd69add087d56,0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d,0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c,0x2170ed0880ac9a755fd29b2688956bd959f933f8,0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82".split( + ",", + ); + +const STABLE_COINS: string[] = + "0x55d398326f99059ff775485246999027b3197955,0xe9e7cea3dedca5984780bafc599bd69add087d56,0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d".split( + ",", + ); + +const MINIMUM_ETH_LOCKED = BigNumber.from("60"); + +const Q192 = 2 ** 192; +export function sqrtPriceX96ToTokenPrices( + sqrtPriceX96: bigint, + token0: Token, + token1: Token, +): number[] { + const num = sqrtPriceX96 * sqrtPriceX96; + const denom = BigInt(Q192); + const divide = num / denom; + const decimals = BigInt( + Math.abs(Number(token1.decimals) - Number(token0.decimals)), + ); + const price1 = Number(formatUnits(divide, decimals)); + + const price0 = safeDivNumToNum(1, price1); + return [price0, price1]; +} + +export async function getEthPriceInUSD(): Promise { + // fetch eth prices for each stablecoin + const usdcPool = await Pool.get(USDC_WETH_03_POOL); // dai is token0 + if (usdcPool !== undefined) { + return BigNumber.from(usdcPool.token0Price); + } else { + return ZERO_BD; + } +} + +/** + * Search through graph to find derived Eth per token. + * @todo update to be derived ETH (add stablecoin estimates) + **/ +export async function findEthPerToken(token: Token): Promise { + if (token.id == WETH_ADDRESS) { + return ONE_BD; + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + // const whiteList: string[] = token.whitelistPoolsId + + // for now just take USD from pool with greatest TVL + // need to update this to actually detect best rate based on liquidity distribution + let largestLiquidityETH = ZERO_BD; + let priceSoFar = ZERO_BD; + const bundle = await Bundle.get("1"); + + const options: GetOptions = { + limit: 100, // Specify the limit you want + offset: 0, // Optional: Specify the offset if needed + orderDirection: 'ASC', // Optional: Specify 'ASC' or 'DESC' +}; + + const tokenWhitelist = await WhiteListPools.getByTokenId(token.id, options); + // hardcoded fix for incorrect rates + // if whitelist includes token - get the safe price + assert(bundle); + assert(tokenWhitelist); + if (STABLE_COINS.includes(token.id)) { + priceSoFar = safeDiv(ONE_BD, BigNumber.from(bundle.ethPriceUSD)); + } else { + // get a list of whitelist For the matching token + // + for (let i = 0; i < tokenWhitelist.length; ++i) { + const poolAddress = tokenWhitelist[i].poolId; + const pool = await Pool.get(poolAddress); + assert(pool); + assert(pool.token0Id); + assert(pool.token1Id); + + if (BigNumber.from(pool.liquidity).gt(ZERO_BI)) { + if (pool.token0Id == token.id) { + // whitelist token is token1 + const token1 = await Token.get(pool.token1Id); + // get the derived ETH in pool + assert(token1); + const ethLocked = BigNumber.from(pool.totalValueLockedToken1).mul( + token1.derivedETH, + ); + if ( + ethLocked.gt(largestLiquidityETH) && + ethLocked.gt(MINIMUM_ETH_LOCKED) + ) { + largestLiquidityETH = ethLocked; + // token1 per our token * Eth per token1 + priceSoFar = BigNumber.from(pool.token1Price).mul( + token1.derivedETH, + ); + } + } + if (pool.token1Id == token.id) { + const token0 = await Token.get(pool.token0Id); + assert(token0); + // get the derived ETH in pool + const ethLocked = BigNumber.from(pool.totalValueLockedToken0).mul( + token0.derivedETH, + ); + if ( + ethLocked.gt(largestLiquidityETH) && + ethLocked.gt(MINIMUM_ETH_LOCKED) + ) { + largestLiquidityETH = ethLocked; + // token0 per our token * ETH per token0 + priceSoFar = BigNumber.from(pool.token0Price).mul( + token0.derivedETH, + ); + } + } + } + } + } + return priceSoFar; // nothing was found return 0 +} + +/** + * Accepts tokens and amounts, return tracked amount based on token whitelist + * If one token on whitelist, return amount in that token converted to USD * 2. + * If both are, return sum of two amounts + * If neither is, return 0 + */ +export async function getTrackedAmountUSD( + tokenAmount0: BigNumber, + token0: Token, + tokenAmount1: BigNumber, + token1: Token, +): Promise { + const bundle = await Bundle.get("1"); + assert(bundle); + const price0USD = BigNumber.from(token0.derivedETH).mul(bundle.ethPriceUSD); + const price1USD = BigNumber.from(token1.derivedETH).mul(bundle.ethPriceUSD); + + // both are whitelist tokens, return sum of both amounts + if ( + WHITELIST_TOKENS.includes(token0.id) && + WHITELIST_TOKENS.includes(token1.id) + ) { + return tokenAmount0.mul(price0USD).add(tokenAmount1.mul(price1USD)); + } + + // take double value of the whitelisted token amount + if ( + WHITELIST_TOKENS.includes(token0.id) && + !WHITELIST_TOKENS.includes(token1.id) + ) { + return tokenAmount0.mul(price0USD).mul(BigNumber.from("2")); + } + + // take double value of the whitelisted token amount + if ( + !WHITELIST_TOKENS.includes(token0.id) && + WHITELIST_TOKENS.includes(token1.id) + ) { + return tokenAmount1.mul(price1USD).mul(BigNumber.from("2")); + } + + // neither token is on white list, tracked amount is 0 + return ZERO_BD; +} diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/staticTokenDefinition.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/staticTokenDefinition.ts new file mode 100644 index 00000000..ed6280e1 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/staticTokenDefinition.ts @@ -0,0 +1,97 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { BigNumber } from "@ethersproject/bignumber"; + +export class StaticTokenDefinition { + address: string; + symbol: string; + name: string; + decimals: BigNumber; + + // Initialize a Token Definition with its attributes + constructor( + address: string, + symbol: string, + name: string, + decimals: BigNumber, + ) { + this.address = address; + this.symbol = symbol; + this.name = name; + this.decimals = decimals; + } + + // Get all tokens with a static defintion + static getStaticDefinitions(): StaticTokenDefinition[] { + const staticDefinitions = []; + // Add DGD + const tokenDGD = new StaticTokenDefinition( + "0xe0b7927c4af23765cb51314a0e0521a9645f0e2a", + "DGD", + "DGD", + BigNumber.from(9), + ); + staticDefinitions.push(tokenDGD); + + // Add AAVE + const tokenAAVE = new StaticTokenDefinition( + "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9", + "AAVE", + "Aave Token", + BigNumber.from(18), + ); + staticDefinitions.push(tokenAAVE); + + // Add LIF + const tokenLIF = new StaticTokenDefinition( + "0xeb9951021698b42e4399f9cbb6267aa35f82d59d", + "LIF", + "Lif", + BigNumber.from(18), + ); + staticDefinitions.push(tokenLIF); + + // Add SVD + const tokenSVD = new StaticTokenDefinition( + "0xbdeb4b83251fb146687fa19d1c660f99411eefe3", + "SVD", + "savedroid", + BigNumber.from(18), + ); + staticDefinitions.push(tokenSVD); + + // Add TheDAO + const tokenTheDAO = new StaticTokenDefinition( + "0xbb9bc244d798123fde783fcc1c72d3bb8c189413", + "TheDAO", + "TheDAO", + BigNumber.from(16), + ); + staticDefinitions.push(tokenTheDAO); + + // Add HPB + const tokenHPB = new StaticTokenDefinition( + "0x38c6a68304cdefb9bec48bbfaaba5c5b47818bb2", + "HPB", + "HPBCoin", + BigNumber.from(18), + ); + staticDefinitions.push(tokenHPB); + + return staticDefinitions; + } + + // Helper for hardcoded tokens + static fromAddress(tokenAddress: string): StaticTokenDefinition | null { + const staticDefinitions = this.getStaticDefinitions(); + // Search the definition using the address + for (const staticDefinition of staticDefinitions) { + if (staticDefinition.address === tokenAddress) { + return staticDefinition; + } + } + // If not found, return null + return null; + } +} diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/tick.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/tick.ts new file mode 100644 index 00000000..112ea51b --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/tick.ts @@ -0,0 +1,65 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { safeDivNumToNum } from "./utils"; +import { Tick } from "../../types"; +import { ONE_BD, ZERO_BD, ZERO_BI } from "./constants"; +import { EthereumLog } from "@subql/types-ethereum"; +import { BigNumber } from "@ethersproject/bignumber"; +import { MintEvent } from "../../types/contracts/Pool"; + +export function createTick( + tickId: string, + tickIdx: number, + poolId: string, + event: EthereumLog, +): Tick { + // 1.0001^tick is token1/token0. + // return bigNumber.js bignumber and convert to number + const price0 = Math.pow(1.0001, tickIdx); + + const tick = Tick.create({ + id: tickId, + tickIdx: BigInt(tickIdx), + poolId: poolId, + poolAddress: poolId, + + createdAtTimestamp: event.block.timestamp, + createdAtBlockNumber: BigInt(event.block.number), + liquidityGross: ZERO_BI, + liquidityNet: ZERO_BI, + liquidityProviderCount: ZERO_BI, + + price0: ONE_BD.toNumber(), + price1: safeDivNumToNum(1, price0), + volumeToken0: ZERO_BD.toNumber(), + volumeToken1: ZERO_BD.toNumber(), + volumeUSD: ZERO_BD.toNumber(), + feesUSD: ZERO_BD.toNumber(), + untrackedVolumeUSD: ZERO_BD.toNumber(), + collectedFeesToken0: ZERO_BD.toNumber(), + collectedFeesToken1: ZERO_BD.toNumber(), + collectedFeesUSD: ZERO_BD.toNumber(), + feeGrowthOutside0X128: ZERO_BI, + feeGrowthOutside1X128: ZERO_BI, + }); + + return tick; +} + +export function feeTierToTickSpacing(feeTier: BigNumber): BigNumber { + if (feeTier.eq(BigNumber.from(10000))) { + return BigNumber.from(200); + } + if (feeTier.eq(BigNumber.from(3000))) { + return BigNumber.from(60); + } + if (feeTier.eq(BigNumber.from(500))) { + return BigNumber.from(10); + } + if (feeTier.eq(BigNumber.from(100))) { + return BigNumber.from(1); + } + + throw Error("Unexpected fee tier"); +} diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/token.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/token.ts new file mode 100644 index 00000000..159d8384 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/token.ts @@ -0,0 +1,99 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { StaticTokenDefinition } from "./staticTokenDefinition"; +import { isNullEthValue } from "."; +import { BigNumber } from "@ethersproject/bignumber"; +import { ERC20SymbolBytes__factory } from "../../types/contracts/factories/ERC20SymbolBytes__factory"; +import { ERC20__factory } from "../../types/contracts/factories/ERC20__factory"; +import { ERC20NameBytes__factory } from "../../types/contracts/factories/ERC20NameBytes__factory"; +import assert from "assert"; + +export async function fetchTokenSymbol(tokenAddress: string): Promise { + const contract = ERC20__factory.connect(tokenAddress, api); + const contractSymbolBytes = ERC20SymbolBytes__factory.connect( + tokenAddress, + api, + ); + // try types string and bytes32 for symbol + let symbolValue = "unknown"; + try { + symbolValue = await contract.symbol(); + } catch (e) { + // try { + const symbolResultBytes = await contractSymbolBytes.callStatic.symbol(); + if (!isNullEthValue(symbolResultBytes)) { + symbolValue = symbolResultBytes.toString(); + // TODO: hexString -> utf8 string + // throw new Error('Not implemented') + } else { + // try with the static definition + const staticTokenDefinition = + StaticTokenDefinition.fromAddress(tokenAddress); + if (staticTokenDefinition) { + symbolValue = staticTokenDefinition.symbol; + logger.error("try with static definition"); + } + } + } + return symbolValue; +} + +export async function fetchTokenName(tokenAddress: string): Promise { + const contract = ERC20__factory.connect(tokenAddress, api); + const contractNameBytes = ERC20NameBytes__factory.connect(tokenAddress, api); + + // try types string and bytes32 for name + let nameValue = "unknown"; + try { + nameValue = await contract.name(); + } catch (e) { + const nameResultBytes = await contractNameBytes.name(); + if (!isNullEthValue(nameResultBytes)) { + nameValue = nameResultBytes; + } else { + // try with the static definition + const staticTokenDefinition = + StaticTokenDefinition.fromAddress(tokenAddress); + assert(staticTokenDefinition); + if (staticTokenDefinition !== undefined) { + nameValue = staticTokenDefinition.name; + } + } + } + return nameValue; +} + +export async function fetchTokenTotalSupply( + tokenAddress: string, +): Promise { + const contract = ERC20__factory.connect(tokenAddress, api); + let totalSupplyValue = null; + try { + totalSupplyValue = await contract.totalSupply(); + } catch (e) { + return BigNumber.from(null); + } + return BigNumber.from(totalSupplyValue); +} + +export async function fetchTokenDecimals( + tokenAddress: string, +): Promise { + // try types uint8 for decimals + let decimalValue = null; + try { + const contract = ERC20__factory.connect(tokenAddress, api); + decimalValue = await contract.decimals(); + } catch (e) { + // try with the static definition + const staticTokenDefinition = + StaticTokenDefinition.fromAddress(tokenAddress); + if (staticTokenDefinition != null) { + return staticTokenDefinition.decimals; + } else { + logger.warn(`Could not get token ${tokenAddress} decimals`); + } + } + return BigNumber.from(decimalValue); +} diff --git a/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/utils.ts b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/utils.ts new file mode 100644 index 00000000..2c3bb261 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/src/mappings/utils/utils.ts @@ -0,0 +1,75 @@ +// Copyright 2020-2022 SubQuery Pte Ltd authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { Transaction } from "../../types"; +import { ZERO_BD } from "./constants"; +import { BigNumber } from "@ethersproject/bignumber"; +import { EthereumLog } from "@subql/types-ethereum"; +import assert from "assert"; + +export function exponentToBigDecimal(decimals: bigint): BigNumber { + if (Number(decimals) === 0) { + return BigNumber.from("1"); + } + const base = BigInt(10); + const pwr = base ** decimals; + return BigNumber.from(pwr); +} + +export function safeDivNumToNum(amount0: number, amount1: number): number { + return amount1 === 0 ? 0 : amount0 / amount1; +} + +export function safeDiv(amount0: BigNumber, amount1: BigNumber): BigNumber { + // I assume eq means equal + if (amount1.eq(ZERO_BD)) { + // return BigNumber.from(ZERO_BD) + return ZERO_BD; + } else { + return amount0.div(amount1); + } +} + +export function isNullEthValue(value: string): boolean { + return ( + value == + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); +} + +export function convertTokenToDecimal( + tokenAmount: BigNumber, + exchangeDecimals: bigint, +): BigNumber { + if (Number(exchangeDecimals) == 0) { + return tokenAmount; + } + return tokenAmount.div(exponentToBigDecimal(exchangeDecimals)); +} + +export async function loadTransaction( + event: EthereumLog, +): Promise { + let transaction = await Transaction.get(event.transactionHash); + if (transaction === undefined) { + transaction = Transaction.create({ + id: event.transactionHash, + blockNumber: BigInt(event.blockNumber), + timestamp: event.block.timestamp, + gasPrice: BigInt(0), + gasUsed: BigInt(0), + }); + } + + // transaction.gasPrice = event.block.gasPrice + const eventTransaction = event.block.transactions.find( + (transaction) => + transaction.transactionIndex == + BigNumber.from(event.transactionIndex).toBigInt(), + ); + assert(eventTransaction); + // transaction.gasUsed = (await eventTransaction.receipt()).gasUsed; + transaction.gasPrice = eventTransaction.gasPrice; + await transaction.save(); + return transaction; +} diff --git a/Unichain/unichain-testnet-uniswap-v3/tsconfig.json b/Unichain/unichain-testnet-uniswap-v3/tsconfig.json new file mode 100644 index 00000000..e482aae2 --- /dev/null +++ b/Unichain/unichain-testnet-uniswap-v3/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "esModuleInterop": true, + "declaration": true, + "importHelpers": true, + "resolveJsonModule": true, + "module": "commonjs", + "outDir": "dist", + "rootDir": "src", + "target": "es2018", + "strict": true, + }, + "include": [ + "src/**/*", + "node_modules/@subql/types-core/dist/global.d.ts", + "node_modules/@subql/types-ethereum/dist/global.d.ts", + ], +}