From 30d2266a2b764bd0f165ce14171c8851279172e9 Mon Sep 17 00:00:00 2001 From: Chef Huan Date: Wed, 31 May 2023 12:12:28 +0700 Subject: [PATCH] chore: Add user position calculation --- .../template/mappings/pool.ts | 48 +++++----- .../template/mappings/position-manager.ts | 17 +++- .../user-position-v3/template/package.json | 4 +- .../user-position-v3/template/schema.graphql | 44 +++------ .../template/subgraph.template.yaml | 12 --- .../user-position-v3/template/subgraph.yaml | 94 +++++++++++++++++++ .../template/utils/constants.template.ts | 17 ---- .../template/utils/constants.ts | 9 ++ .../user-position-v3/template/utils/index.ts | 43 +-------- .../user-position-v3/template/utils/schema.ts | 48 ++++++++++ .../user-position-v3/template/utils/token.ts | 24 +---- 11 files changed, 207 insertions(+), 153 deletions(-) create mode 100644 subgraphs/user-position-v3/template/subgraph.yaml delete mode 100644 subgraphs/user-position-v3/template/utils/constants.template.ts create mode 100644 subgraphs/user-position-v3/template/utils/constants.ts create mode 100644 subgraphs/user-position-v3/template/utils/schema.ts diff --git a/subgraphs/user-position-v3/template/mappings/pool.ts b/subgraphs/user-position-v3/template/mappings/pool.ts index 369365f8..7145b9e7 100644 --- a/subgraphs/user-position-v3/template/mappings/pool.ts +++ b/subgraphs/user-position-v3/template/mappings/pool.ts @@ -1,8 +1,10 @@ /* eslint-disable prefer-const */ import { BigInt } from "@graphprotocol/graph-ts"; import { Burn as BurnEvent, Mint as MintEvent, Swap as SwapEvent } from "../generated/templates/Pool/Pool"; -import { Burn, Mint, Pool, Swap, Token } from "../generated/schema"; -import { convertTokenToDecimal, loadTransaction } from "../utils"; +import { Burn, Mint, Pool, Token } from "../generated/schema"; +import { convertTokenToDecimal } from "../utils"; +import { loadTransaction, updateUserPosition } from "../utils/schema"; +import { ONE_BI } from "../utils/constants"; export function handleMint(event: MintEvent): void { let transaction = loadTransaction(event); @@ -29,6 +31,17 @@ export function handleMint(event: MintEvent): void { mint.tickLower = BigInt.fromI32(event.params.tickLower); mint.tickUpper = BigInt.fromI32(event.params.tickUpper); mint.logIndex = event.logIndex; + mint.save(); + + pool.txCount = pool.txCount.plus(ONE_BI); + pool.save(); + + transaction.tickLower = BigInt.fromI32(event.params.tickLower); + transaction.tickUpper = BigInt.fromI32(event.params.tickUpper); + transaction.increaseLiquidityAmount = event.params.amount; + transaction.save(); + + updateUserPosition(transaction, pool.id); } export function handleBurn(event: BurnEvent): void { @@ -55,30 +68,15 @@ export function handleBurn(event: BurnEvent): void { burn.tickLower = BigInt.fromI32(event.params.tickLower); burn.tickUpper = BigInt.fromI32(event.params.tickUpper); burn.logIndex = event.logIndex; -} + burn.save(); -export function handleSwap(event: SwapEvent): void { - let transaction = loadTransaction(event); - let poolAddress = event.address.toHexString(); - let pool = Pool.load(poolAddress); - let token0 = Token.load(pool.token0); - let token1 = Token.load(pool.token1); + pool.txCount = pool.txCount.plus(ONE_BI); + pool.save(); - let amount0 = convertTokenToDecimal(event.params.amount0, token0.decimals); - let amount1 = convertTokenToDecimal(event.params.amount1, token1.decimals); + transaction.tickLower = BigInt.fromI32(event.params.tickLower); + transaction.tickUpper = BigInt.fromI32(event.params.tickUpper); + transaction.decreaseLiquidityAmount = event.params.amount; + transaction.save(); - let swap = new Swap(transaction.id + "#" + pool.txCount.toString()); - swap.transaction = transaction.id; - swap.timestamp = transaction.timestamp; - swap.pool = pool.id; - swap.token0 = pool.token0; - swap.token1 = pool.token1; - swap.sender = event.params.sender; - swap.origin = event.transaction.from; - swap.recipient = event.params.recipient; - swap.amount0 = amount0; - swap.amount1 = amount1; - swap.tick = BigInt.fromI32(event.params.tick as i32); - swap.sqrtPriceX96 = event.params.sqrtPriceX96; - swap.logIndex = event.logIndex; + updateUserPosition(transaction, pool.id); } diff --git a/subgraphs/user-position-v3/template/mappings/position-manager.ts b/subgraphs/user-position-v3/template/mappings/position-manager.ts index 74d87766..a53cc130 100644 --- a/subgraphs/user-position-v3/template/mappings/position-manager.ts +++ b/subgraphs/user-position-v3/template/mappings/position-manager.ts @@ -1,19 +1,34 @@ /* eslint-disable prefer-const */ +import { Address } from "@graphprotocol/graph-ts"; import { DecreaseLiquidity, IncreaseLiquidity, Transfer, } from "../generated/NonfungiblePositionManager/NonfungiblePositionManager"; -import { loadTransaction } from "../utils"; +import { loadTransaction } from "../utils/schema"; +import { ADDRESS_ZERO } from "../utils/constants"; +import { UserPosition } from "../generated/schema"; export function handleIncreaseLiquidity(event: IncreaseLiquidity): void { let transaction = loadTransaction(event); + transaction.tokenId = event.params.tokenId; + transaction.save(); } export function handleDecreaseLiquidity(event: DecreaseLiquidity): void { let transaction = loadTransaction(event); + transaction.tokenId = event.params.tokenId; + transaction.save(); } export function handleTransfer(event: Transfer): void { let transaction = loadTransaction(event); + if (event.params.from.equals(Address.fromString(ADDRESS_ZERO))) { + transaction.positionOwner = event.params.to; + } else { + let userPosition = UserPosition.load(event.params.tokenId.toString()); + userPosition.owner = event.params.to; + userPosition.save(); + } + transaction.save(); } diff --git a/subgraphs/user-position-v3/template/package.json b/subgraphs/user-position-v3/template/package.json index eb7480d0..dd98a6e8 100644 --- a/subgraphs/user-position-v3/template/package.json +++ b/subgraphs/user-position-v3/template/package.json @@ -6,8 +6,8 @@ "author": "PancakeSwap", "license": "GPL-3.0-or-later", "scripts": { - "template": "mustache ../../../config/bsc.js subgraph.template.yaml > subgraph.yaml && mustache ../../../config/bsc.js utils/constants.template.ts > utils/constants.ts", - "auth": "graph auth --product hosted-service SUBGRAPH_KEY", + "template": "mustache ../../../config/bsc.js subgraph.template.yaml > subgraph.yaml", + "auth": "graph auth --product hosted-service f4b727b41dd3465f461ee000115b1102", "codegen": "graph codegen subgraph.yaml", "build": "graph build subgraph.yaml", "deploy:goerli": "graph deploy --product hosted-service --node https://api.thegraph.com/deploy/ --ipfs https://api.thegraph.com/ipfs/ pancakeswap/user-position-v3-goerli subgraph.yaml", diff --git a/subgraphs/user-position-v3/template/schema.graphql b/subgraphs/user-position-v3/template/schema.graphql index 27a0aad8..dcb3fa15 100644 --- a/subgraphs/user-position-v3/template/schema.graphql +++ b/subgraphs/user-position-v3/template/schema.graphql @@ -21,14 +21,10 @@ type Pool @entity { token0: Token! # token1 token1: Token! - # fee amount - # current tick - tick: BigInt! # derived fields mints: [Mint!]! @derivedFrom(field: "pool") burns: [Burn!]! @derivedFrom(field: "pool") - swaps: [Swap!]! @derivedFrom(field: "pool") } type Transaction @entity { @@ -47,11 +43,14 @@ type Transaction @entity { tokenId: BigInt increaseLiquidityAmount: BigInt decreaseLiquidityAmount: BigInt + tickLower: BigInt + tickUpper: BigInt + + isPositionUpdated: Boolean! # derived values mints: [Mint]! @derivedFrom(field: "transaction") burns: [Burn]! @derivedFrom(field: "transaction") - swaps: [Swap]! @derivedFrom(field: "transaction") } type Mint @entity { @@ -118,33 +117,14 @@ type Burn @entity { logIndex: BigInt } -type Swap @entity { - # transaction hash + "#" + index in swaps Transaction array +type UserPosition @entity { + # tokenId id: ID! - # pointer to transaction - transaction: Transaction! - # timestamp of transaction - timestamp: BigInt! - # pool swap occured within + owner: Bytes! + # pool position is within pool: Pool! - # allow indexing by tokens - token0: Token! - # allow indexing by tokens - token1: Token! - # sender of the swap - sender: Bytes! - # recipient of the swap - recipient: Bytes! - # txn origin - origin: Bytes! # the EOA that initiated the txn - # delta of token0 swapped - amount0: BigDecimal! - # delta of token1 swapped - amount1: BigDecimal! - # 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 + + liquidity: BigInt! + tickLower: BigInt! + tickUpper: BigInt! } diff --git a/subgraphs/user-position-v3/template/subgraph.template.yaml b/subgraphs/user-position-v3/template/subgraph.template.yaml index a4782a31..925bc75c 100644 --- a/subgraphs/user-position-v3/template/subgraph.template.yaml +++ b/subgraphs/user-position-v3/template/subgraph.template.yaml @@ -64,8 +64,6 @@ dataSources: handler: handleIncreaseLiquidity - event: DecreaseLiquidity(indexed uint256,uint128,uint256,uint256) handler: handleDecreaseLiquidity - - event: Collect(indexed uint256,address,uint256,uint256) - handler: handleCollect - event: Transfer(indexed address,indexed address,indexed uint256) handler: handleTransfer templates: @@ -90,17 +88,7 @@ templates: - name: ERC20 file: ./abis/ERC20.json eventHandlers: - - event: Initialize(uint160,int24) - handler: handleInitialize - - event: Swap(indexed address,indexed address,int256,int256,uint160,uint128,int24,uint128,uint128) - handler: handleSwap - event: Mint(address,indexed address,indexed int24,indexed int24,uint128,uint256,uint256) handler: handleMint - event: Burn(indexed address,indexed int24,indexed int24,uint128,uint256,uint256) handler: handleBurn - - event: Flash(indexed address,indexed address,uint256,uint256,uint256,uint256) - handler: handleFlash - - event: Collect(indexed address,address,indexed int24,indexed int24,uint128,uint128) - handler: handleCollect - - event: CollectProtocol(indexed address,indexed address,uint128,uint128) - handler: handleCollectProtocol diff --git a/subgraphs/user-position-v3/template/subgraph.yaml b/subgraphs/user-position-v3/template/subgraph.yaml new file mode 100644 index 00000000..3c24e33d --- /dev/null +++ b/subgraphs/user-position-v3/template/subgraph.yaml @@ -0,0 +1,94 @@ +specVersion: 0.0.2 +description: PancakeSwap is a multi-chain decentralized exchange and automated market maker protocol. +repository: https://github.com/pancakeswap/pancake-subgraph +schema: + file: ./schema.graphql +features: + - nonFatalErrors +dataSources: + - kind: ethereum/contract + name: Factory + network: bsc + source: + address: "0x0bfbcf9fa4f9c56b0f40a671ad40e0805a091865" + abi: Factory + startBlock: 26956207 + mapping: + kind: ethereum/events + apiVersion: 0.0.4 + language: wasm/assemblyscript + file: ./mappings/factory.ts + entities: + - Pool + - Token + abis: + - name: Factory + file: ./abis/factory.json + - name: ERC20 + file: ./abis/ERC20.json + - name: ERC20SymbolBytes + file: ./abis/ERC20SymbolBytes.json + - name: ERC20NameBytes + file: ./abis/ERC20NameBytes.json + - name: Pool + file: ./abis/pool.json + eventHandlers: + - event: PoolCreated(indexed address,indexed address,indexed uint24,int24,address) + handler: handlePoolCreated + - kind: ethereum/contract + name: NonfungiblePositionManager + network: bsc + source: + address: "0x46a15b0b27311cedf172ab29e4f4766fbe7f4364" + abi: NonfungiblePositionManager + startBlock: 26931961 + mapping: + kind: ethereum/events + apiVersion: 0.0.4 + language: wasm/assemblyscript + file: ./mappings/position-manager.ts + entities: + - Pool + - Token + abis: + - name: NonfungiblePositionManager + file: ./abis/NonfungiblePositionManager.json + - name: Pool + file: ./abis/pool.json + - name: Factory + file: ./abis/factory.json + - name: ERC20 + file: ./abis/ERC20.json + eventHandlers: + - event: IncreaseLiquidity(indexed uint256,uint128,uint256,uint256) + handler: handleIncreaseLiquidity + - event: DecreaseLiquidity(indexed uint256,uint128,uint256,uint256) + handler: handleDecreaseLiquidity + - event: Transfer(indexed address,indexed address,indexed uint256) + handler: handleTransfer +templates: + - kind: ethereum/contract + name: Pool + network: bsc + source: + abi: Pool + mapping: + kind: ethereum/events + apiVersion: 0.0.4 + language: wasm/assemblyscript + file: ./mappings/pool.ts + entities: + - Pool + - Token + abis: + - name: Pool + file: ./abis/pool.json + - name: Factory + file: ./abis/factory.json + - name: ERC20 + file: ./abis/ERC20.json + eventHandlers: + - event: Mint(address,indexed address,indexed int24,indexed int24,uint128,uint256,uint256) + handler: handleMint + - event: Burn(indexed address,indexed int24,indexed int24,uint128,uint256,uint256) + handler: handleBurn diff --git a/subgraphs/user-position-v3/template/utils/constants.template.ts b/subgraphs/user-position-v3/template/utils/constants.template.ts deleted file mode 100644 index e89a270a..00000000 --- a/subgraphs/user-position-v3/template/utils/constants.template.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint-disable prefer-const */ -import { BigInt, BigDecimal, Address } from "@graphprotocol/graph-ts"; -import { Factory as FactoryContract } from "../generated/templates/Pool/Factory"; - -export const ADDRESS_ZERO = "0x0000000000000000000000000000000000000000"; -// prettier-ignore -export const FACTORY_ADDRESS = "{{ v3.factoryAddress }}"; - -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 TWO_BD = BigDecimal.fromString("2"); - -export let factoryContract = FactoryContract.bind(Address.fromString(FACTORY_ADDRESS)); diff --git a/subgraphs/user-position-v3/template/utils/constants.ts b/subgraphs/user-position-v3/template/utils/constants.ts new file mode 100644 index 00000000..9bd14bc8 --- /dev/null +++ b/subgraphs/user-position-v3/template/utils/constants.ts @@ -0,0 +1,9 @@ +/* eslint-disable prefer-const */ +import { BigInt, BigDecimal } from "@graphprotocol/graph-ts"; + +export const ADDRESS_ZERO = "0x0000000000000000000000000000000000000000"; + +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"); diff --git a/subgraphs/user-position-v3/template/utils/index.ts b/subgraphs/user-position-v3/template/utils/index.ts index 9d3838bc..dacb7c7a 100644 --- a/subgraphs/user-position-v3/template/utils/index.ts +++ b/subgraphs/user-position-v3/template/utils/index.ts @@ -1,7 +1,6 @@ /* eslint-disable prefer-const */ -import { BigInt, BigDecimal, ethereum } from "@graphprotocol/graph-ts"; -import { Transaction } from "../generated/schema"; -import { ONE_BI, ZERO_BI, ZERO_BD, ONE_BD } from "./constants"; +import { BigInt, BigDecimal } from "@graphprotocol/graph-ts"; +import { ONE_BI, ZERO_BI } from "./constants"; export function exponentToBigDecimal(decimals: BigInt): BigDecimal { let bd = BigDecimal.fromString("1"); @@ -11,31 +10,6 @@ export function exponentToBigDecimal(decimals: BigInt): BigDecimal { return bd; } -// return 0 if denominator is 0 in division -export function safeDiv(amount0: BigDecimal, amount1: BigDecimal): BigDecimal { - if (amount1.equals(ZERO_BD)) { - return ZERO_BD; - } else { - return amount0.div(amount1); - } -} - -export function tokenAmountToDecimal(tokenAmount: BigInt, exchangeDecimals: BigInt): BigDecimal { - if (exchangeDecimals == ZERO_BI) { - return tokenAmount.toBigDecimal(); - } - return tokenAmount.toBigDecimal().div(exponentToBigDecimal(exchangeDecimals)); -} - -export function equalToZero(value: BigDecimal): boolean { - const formattedVal = parseFloat(value.toString()); - const zero = parseFloat(ZERO_BD.toString()); - if (zero == formattedVal) { - return true; - } - return false; -} - export function isNullEthValue(value: string): boolean { return value == "0x0000000000000000000000000000000000000000000000000000000000000001"; } @@ -46,16 +20,3 @@ export function convertTokenToDecimal(tokenAmount: BigInt, exchangeDecimals: Big } return tokenAmount.toBigDecimal().div(exponentToBigDecimal(exchangeDecimals)); } - -export function loadTransaction(event: ethereum.Event): Transaction { - let transaction = Transaction.load(event.transaction.hash.toHexString()); - if (transaction === null) { - transaction = new Transaction(event.transaction.hash.toHexString()); - } - transaction.blockNumber = event.block.number; - transaction.timestamp = event.block.timestamp; - transaction.gasUsed = event.transaction.gasUsed; - transaction.gasPrice = event.transaction.gasPrice; - transaction.save(); - return transaction as Transaction; -} diff --git a/subgraphs/user-position-v3/template/utils/schema.ts b/subgraphs/user-position-v3/template/utils/schema.ts new file mode 100644 index 00000000..61950918 --- /dev/null +++ b/subgraphs/user-position-v3/template/utils/schema.ts @@ -0,0 +1,48 @@ +import { BigInt, Bytes, ethereum } from "@graphprotocol/graph-ts"; +import { Transaction, UserPosition } from "../generated/schema"; +import { ZERO_BI } from "./constants"; + +export function loadTransaction(event: ethereum.Event): Transaction { + let transaction = Transaction.load(event.transaction.hash.toHexString()); + if (transaction === null) { + transaction = new Transaction(event.transaction.hash.toHexString()); + } + transaction.blockNumber = event.block.number; + transaction.timestamp = event.block.timestamp; + transaction.gasUsed = event.transaction.gasUsed; + transaction.gasPrice = event.transaction.gasPrice; + transaction.isPositionUpdated = false; + transaction.save(); + return transaction as Transaction; +} + +export function updateUserPosition(tx: Transaction, poolId: string): void { + if ( + tx.isPositionUpdated === false && + tx.tickLower !== null && + tx.tickUpper !== null && + tx.tokenId !== null && + tx.positionOwner !== null && + (tx.increaseLiquidityAmount !== null || tx.decreaseLiquidityAmount !== null) + ) { + let userPosition = UserPosition.load(tx.tokenId.toString()); + if (userPosition === null) { + userPosition = new UserPosition(tx.tokenId.toString()); + userPosition.liquidity = ZERO_BI; + userPosition.pool = poolId; + } + userPosition.tickLower = tx.tickLower as BigInt; + userPosition.tickUpper = tx.tickUpper as BigInt; + userPosition.owner = tx.positionOwner as Bytes; + if (tx.increaseLiquidityAmount !== null) { + userPosition.liquidity = userPosition.liquidity.plus(tx.increaseLiquidityAmount as BigInt); + } + if (tx.decreaseLiquidityAmount !== null) { + userPosition.liquidity = userPosition.liquidity.minus(tx.decreaseLiquidityAmount as BigInt); + } + + tx.isPositionUpdated = true; + tx.save(); + userPosition.save(); + } +} diff --git a/subgraphs/user-position-v3/template/utils/token.ts b/subgraphs/user-position-v3/template/utils/token.ts index 684cacd7..cd22f559 100644 --- a/subgraphs/user-position-v3/template/utils/token.ts +++ b/subgraphs/user-position-v3/template/utils/token.ts @@ -1,8 +1,8 @@ /* eslint-disable prefer-const */ +import { BigInt, Address } from "@graphprotocol/graph-ts"; import { ERC20 } from "../generated/Factory/ERC20"; import { ERC20SymbolBytes } from "../generated/Factory/ERC20SymbolBytes"; import { ERC20NameBytes } from "../generated/Factory/ERC20NameBytes"; -import { BigInt, Address } from "@graphprotocol/graph-ts"; import { isNullEthValue } from "."; export function fetchTokenSymbol(tokenAddress: Address): string { @@ -18,12 +18,6 @@ export function fetchTokenSymbol(tokenAddress: Address): string { // for broken pairs that have no symbol function exposed if (!isNullEthValue(symbolResultBytes.value.toHexString())) { symbolValue = symbolResultBytes.value.toString(); - } else { - // try with the static definition - let staticTokenDefinition = StaticTokenDefinition.fromAddress(tokenAddress); - if (staticTokenDefinition != null) { - symbolValue = staticTokenDefinition.symbol; - } } } } else { @@ -46,12 +40,6 @@ export function fetchTokenName(tokenAddress: Address): string { // for broken exchanges that have no name function exposed if (!isNullEthValue(nameResultBytes.value.toHexString())) { nameValue = nameResultBytes.value.toString(); - } else { - // try with the static definition - let staticTokenDefinition = StaticTokenDefinition.fromAddress(tokenAddress); - if (staticTokenDefinition != null) { - nameValue = staticTokenDefinition.name; - } } } } else { @@ -61,16 +49,6 @@ export function fetchTokenName(tokenAddress: Address): string { return nameValue; } -export function fetchTokenTotalSupply(tokenAddress: Address): BigInt { - let contract = ERC20.bind(tokenAddress); - let totalSupplyValue = null; - let totalSupplyResult = contract.try_totalSupply(); - if (!totalSupplyResult.reverted) { - totalSupplyValue = totalSupplyResult as i32; - } - return BigInt.fromI32(totalSupplyValue as i32); -} - export function fetchTokenDecimals(tokenAddress: Address): BigInt { let contract = ERC20.bind(tokenAddress); // try types uint8 for decimals