├── .gitignore ├── uniswap-squid ├── src │ ├── model │ │ ├── index.ts │ │ └── generated │ │ │ ├── bundle.model.ts │ │ │ ├── index.ts │ │ │ ├── uniswapDayData.model.ts │ │ │ ├── transaction.model.ts │ │ │ ├── tickHourData.model.ts │ │ │ ├── collect.model.ts │ │ │ ├── tickDayData.model.ts │ │ │ ├── factory.model.ts │ │ │ ├── flash.model.ts │ │ │ ├── tokenDayData.model.ts │ │ │ ├── tokenHourData.model.ts │ │ │ ├── token.model.ts │ │ │ ├── swap.model.ts │ │ │ ├── position.model.ts │ │ │ ├── burn.model.ts │ │ │ ├── mint.model.ts │ │ │ ├── positionSnapshot.model.ts │ │ │ ├── poolDayData.model.ts │ │ │ ├── poolHourData.model.ts │ │ │ ├── tick.model.ts │ │ │ ├── pool.model.ts │ │ │ └── marshal.ts │ ├── .DS_Store │ ├── utils │ │ ├── constants.ts │ │ ├── blockMap.ts │ │ ├── index.ts │ │ ├── tools.ts │ │ ├── tick.ts │ │ ├── staticTokenDefinition.ts │ │ ├── entityManager.ts │ │ ├── pricing.ts │ │ ├── token.ts │ │ └── intervalUpdates.ts │ ├── abi │ │ ├── ERC20NameBytes.abi.ts │ │ ├── ERC20SymbolBytes.abi.ts │ │ ├── ERC20NameBytes.ts │ │ ├── ERC20SymbolBytes.ts │ │ ├── factory.ts │ │ ├── ERC20.ts │ │ ├── abi.support.ts │ │ ├── factory.abi.ts │ │ ├── ERC20.abi.ts │ │ ├── multicall.ts │ │ ├── NonfungiblePositionManager.ts │ │ └── pool.ts │ ├── processor.ts │ └── mappings │ │ ├── factory.ts │ │ └── positionManager.ts ├── .DS_Store ├── .gitignore ├── typegen.sh ├── docker-compose.yml ├── abis │ ├── ERC20NameBytes.json │ ├── ERC20SymbolBytes.json │ ├── factory.json │ ├── ERC20.json │ └── pool.json ├── tsconfig.json ├── Makefile ├── Dockerfile ├── package.json ├── README.md └── schema.graphql └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./generated" 2 | -------------------------------------------------------------------------------- /uniswap-squid/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsquid/subsquid-resources/HEAD/uniswap-squid/.DS_Store -------------------------------------------------------------------------------- /uniswap-squid/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsquid/subsquid-resources/HEAD/uniswap-squid/src/.DS_Store -------------------------------------------------------------------------------- /uniswap-squid/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /lib 3 | 4 | /**Versions.json 5 | /**Versions.jsonl 6 | 7 | # IDE files 8 | /.idea 9 | /.vscode 10 | /.envrc 11 | -------------------------------------------------------------------------------- /uniswap-squid/typegen.sh: -------------------------------------------------------------------------------- 1 | rm -rf ./src/abi 2 | 3 | for abi in abis/*.json; do 4 | name=${abi%.*} 5 | name=${name#*/} 6 | npx squid-evm-typegen --abi "$abi" --output "./src/abi/$name.ts" || exit 1 7 | done -------------------------------------------------------------------------------- /uniswap-squid/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | db: 5 | image: postgres:12 6 | environment: 7 | POSTGRES_DB: postgres 8 | POSTGRES_PASSWORD: postgres 9 | ports: 10 | - "${DB_PORT}:5432" 11 | # command: ["postgres", "-c", "log_statement=all"] 12 | -------------------------------------------------------------------------------- /uniswap-squid/abis/ERC20NameBytes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "name", 6 | "outputs": [ 7 | { 8 | "internalType": "bytes32", 9 | "name": "", 10 | "type": "bytes32" 11 | } 12 | ], 13 | "payable": false, 14 | "stateMutability": "view", 15 | "type": "function" 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /uniswap-squid/abis/ERC20SymbolBytes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "symbol", 6 | "outputs": [ 7 | { 8 | "internalType": "bytes32", 9 | "name": "", 10 | "type": "bytes32" 11 | } 12 | ], 13 | "payable": false, 14 | "stateMutability": "view", 15 | "type": "function" 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /uniswap-squid/src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const CHAIN_NODE = 'ws://node' 2 | 3 | export const ADDRESS_ZERO = '0x0000000000000000000000000000000000000000' 4 | export const MULTICALL_ADDRESS = '0x5ba1e12693dc8f9c48aad8770482f4739beed696' 5 | export const FACTORY_ADDRESS = '0x1f98431c8ad98523631ae4a59f267346ea31f984' 6 | export const POSITIONS_ADDRESS = '0xc36442b4a4522e871399cd717abdd847ab11fe88' 7 | -------------------------------------------------------------------------------- /uniswap-squid/src/abi/ERC20NameBytes.abi.ts: -------------------------------------------------------------------------------- 1 | export const ABI_JSON = [ 2 | { 3 | "type": "function", 4 | "name": "name", 5 | "constant": true, 6 | "stateMutability": "view", 7 | "payable": false, 8 | "inputs": [], 9 | "outputs": [ 10 | { 11 | "type": "bytes32" 12 | } 13 | ] 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /uniswap-squid/src/abi/ERC20SymbolBytes.abi.ts: -------------------------------------------------------------------------------- 1 | export const ABI_JSON = [ 2 | { 3 | "type": "function", 4 | "name": "symbol", 5 | "constant": true, 6 | "stateMutability": "view", 7 | "payable": false, 8 | "inputs": [], 9 | "outputs": [ 10 | { 11 | "type": "bytes32" 12 | } 13 | ] 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/bundle.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | 4 | @Entity_() 5 | export class Bundle { 6 | constructor(props?: Partial) { 7 | Object.assign(this, props) 8 | } 9 | 10 | @PrimaryColumn_() 11 | id!: string 12 | 13 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 14 | ethPriceUSD!: number 15 | } 16 | -------------------------------------------------------------------------------- /uniswap-squid/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2021", 5 | "outDir": "lib", 6 | "rootDir": "src", 7 | "strict": true, 8 | "declaration": false, 9 | "sourceMap": true, 10 | "esModuleInterop": true, 11 | "experimentalDecorators": true, 12 | "emitDecoratorMetadata": true, 13 | "skipLibCheck": true 14 | }, 15 | "include": [ 16 | "src" 17 | ], 18 | "exclude": [ 19 | "node_modules", 20 | ] 21 | } -------------------------------------------------------------------------------- /uniswap-squid/src/abi/ERC20NameBytes.ts: -------------------------------------------------------------------------------- 1 | import * as ethers from 'ethers' 2 | import {LogEvent, Func, ContractBase} from './abi.support' 3 | import {ABI_JSON} from './ERC20NameBytes.abi' 4 | 5 | export const abi = new ethers.utils.Interface(ABI_JSON); 6 | 7 | export const functions = { 8 | name: new Func<[], {}, string>( 9 | abi, '0x06fdde03' 10 | ), 11 | } 12 | 13 | export class Contract extends ContractBase { 14 | 15 | name(): Promise { 16 | return this.eth_call(functions.name, []) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /uniswap-squid/src/abi/ERC20SymbolBytes.ts: -------------------------------------------------------------------------------- 1 | import * as ethers from 'ethers' 2 | import {LogEvent, Func, ContractBase} from './abi.support' 3 | import {ABI_JSON} from './ERC20SymbolBytes.abi' 4 | 5 | export const abi = new ethers.utils.Interface(ABI_JSON); 6 | 7 | export const functions = { 8 | symbol: new Func<[], {}, string>( 9 | abi, '0x95d89b41' 10 | ), 11 | } 12 | 13 | export class Contract extends ContractBase { 14 | 15 | symbol(): Promise { 16 | return this.eth_call(functions.symbol, []) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /uniswap-squid/Makefile: -------------------------------------------------------------------------------- 1 | process: migrate 2 | @node -r dotenv/config lib/processor.js 3 | 4 | pairs: 5 | @node -r dotenv/config lib/pairsProcessor.js 6 | 7 | serve: 8 | @npx squid-graphql-server 9 | 10 | 11 | migrate: 12 | @npx squid-typeorm-migration apply 13 | 14 | 15 | migration: 16 | @npx squid-typeorm-migration generate 17 | 18 | 19 | typegen: 20 | @bash ./typegen.sh 21 | 22 | 23 | codegen: 24 | @npx squid-typeorm-codegen 25 | 26 | 27 | up: 28 | @docker-compose up -d 29 | 30 | 31 | down: 32 | @docker-compose down -v 33 | 34 | 35 | build: 36 | @npm run build 37 | 38 | 39 | .PHONY: process serve migrate migration codegen typegen up down build pairs 40 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./factory.model" 2 | export * from "./bundle.model" 3 | export * from "./token.model" 4 | export * from "./pool.model" 5 | export * from "./tick.model" 6 | export * from "./position.model" 7 | export * from "./positionSnapshot.model" 8 | export * from "./transaction.model" 9 | export * from "./mint.model" 10 | export * from "./burn.model" 11 | export * from "./swap.model" 12 | export * from "./collect.model" 13 | export * from "./flash.model" 14 | export * from "./uniswapDayData.model" 15 | export * from "./poolDayData.model" 16 | export * from "./poolHourData.model" 17 | export * from "./tickHourData.model" 18 | export * from "./tickDayData.model" 19 | export * from "./tokenDayData.model" 20 | export * from "./tokenHourData.model" 21 | -------------------------------------------------------------------------------- /uniswap-squid/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16-alpine AS node 2 | 3 | FROM node AS node-with-gyp 4 | RUN apk add g++ make python3 5 | 6 | FROM node-with-gyp AS builder 7 | WORKDIR /squid 8 | ADD package.json . 9 | ADD package-lock.json . 10 | RUN npm ci 11 | ADD tsconfig.json . 12 | ADD src src 13 | RUN npm run build 14 | 15 | FROM node-with-gyp AS deps 16 | WORKDIR /squid 17 | ADD package.json . 18 | ADD package-lock.json . 19 | RUN npm ci --production 20 | 21 | FROM node AS squid 22 | WORKDIR /squid 23 | COPY --from=deps /squid/package.json . 24 | COPY --from=deps /squid/package-lock.json . 25 | COPY --from=deps /squid/node_modules node_modules 26 | COPY --from=builder /squid/lib lib 27 | RUN echo -e "loglevel=silent\nupdate-notifier=false" > /squid/.npmrc 28 | ADD db db 29 | ADD schema.graphql . 30 | # TODO: use shorter PROMETHEUS_PORT 31 | ENV PROCESSOR_PROMETHEUS_PORT 3000 32 | EXPOSE 3000 33 | EXPOSE 4000 34 | 35 | 36 | FROM squid AS processor 37 | CMD ["npm", "run", "processor:start"] 38 | 39 | 40 | FROM squid AS query-node 41 | CMD ["npm", "run", "query-node:start"] -------------------------------------------------------------------------------- /uniswap-squid/src/utils/blockMap.ts: -------------------------------------------------------------------------------- 1 | import {EvmBlock} from '@subsquid/evm-processor' 2 | import {last} from './tools' 3 | 4 | export class BlockMap extends Map { 5 | push(block: EvmBlock, ...items: T[]) { 6 | let blockItems = this.get(block) 7 | if (!blockItems) { 8 | this.set(block, items) 9 | } else { 10 | blockItems.push(...items) 11 | } 12 | return this 13 | } 14 | 15 | some(block: EvmBlock, fn: (item: T) => boolean) { 16 | let blockItems = this.get(block) 17 | if (blockItems) { 18 | return blockItems.some(fn) 19 | } 20 | return false 21 | } 22 | 23 | map(fn: (block: EvmBlock, items: T[]) => R[]) { 24 | return new BlockMap(this.entriesArray().map(([block, items]) => [block, fn(block, items)])) 25 | } 26 | 27 | keysArray() { 28 | return [...this.keys()] 29 | } 30 | 31 | entriesArray() { 32 | return [...this.entries()] 33 | } 34 | 35 | valuesArray() { 36 | return [...this.values()] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/uniswapDayData.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | 4 | @Entity_() 5 | export class UniswapDayData { 6 | constructor(props?: Partial) { 7 | Object.assign(this, props) 8 | } 9 | 10 | @PrimaryColumn_() 11 | id!: string 12 | 13 | @Column_("timestamp with time zone", {nullable: false}) 14 | date!: Date 15 | 16 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 17 | volumeETH!: number 18 | 19 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 20 | volumeUSD!: number 21 | 22 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 23 | volumeUSDUntracked!: number 24 | 25 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 26 | feesUSD!: number 27 | 28 | @Column_("int4", {nullable: false}) 29 | txCount!: number 30 | 31 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 32 | tvlUSD!: number 33 | } 34 | -------------------------------------------------------------------------------- /uniswap-squid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "squid-evm-template", 3 | "private": true, 4 | "scripts": { 5 | "build": "rm -rf lib && tsc", 6 | "db:migrate": "npx squid-typeorm-migration apply", 7 | "processor:start": "node lib/processor.js", 8 | "query-node:start": "squid-graphql-server", 9 | "lint": "eslint . --ext .ts --ignore-path .gitignore --ignore-pattern src/abi/**/* --fix" 10 | }, 11 | "dependencies": { 12 | "@ethersproject/abi": "^5.6.1", 13 | "@subsquid/big-decimal": "^0.0.0", 14 | "@subsquid/evm-processor": "^0.1.0", 15 | "@subsquid/evm-typegen": "1.3.0", 16 | "@subsquid/graphql-server": "3.2.0", 17 | "@subsquid/typeorm-migration": "0.1.2", 18 | "@subsquid/typeorm-store": "0.1.3", 19 | "dotenv": "^16.0.0", 20 | "ethers": "^5.6.4", 21 | "pg": "^8.7.3", 22 | "typeorm": "^0.3.6" 23 | }, 24 | "devDependencies": { 25 | "@subsquid/typeorm-codegen": "0.2.1", 26 | "@typechain/ethers-v5": "^10.1.0", 27 | "@types/node": "^17.0.23", 28 | "@typescript-eslint/eslint-plugin": "^4.33.0", 29 | "typechain": "^8.1.0", 30 | "typescript": "~4.6.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /uniswap-squid/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable prefer-const */ 2 | import {BigDecimal} from '@subsquid/big-decimal' 3 | 4 | // return 0 if denominator is 0 in division 5 | export function safeDiv(amount0: number, amount1: number): number { 6 | return amount1 === 0 ? 0 : amount0 / amount1 7 | } 8 | 9 | export function isNullEthValue(value: string): boolean { 10 | return value == '0x0000000000000000000000000000000000000000000000000000000000000001' 11 | } 12 | 13 | export function bigDecimalExp18(): BigDecimal { 14 | return BigDecimal('1000000000000000000') 15 | } 16 | 17 | export function convertEthToDecimal(eth: bigint): BigDecimal { 18 | return BigDecimal(eth, 18) 19 | } 20 | 21 | export function bigDecimalExponated(value: number, power: number): number { 22 | if (power === 0) { 23 | return 1 24 | } 25 | let negativePower = power < 0 26 | let result = 0 + value 27 | let powerAbs = negativePower ? -power : power 28 | for (let i = 1; i < powerAbs; i++) { 29 | result = result * value 30 | } 31 | 32 | if (negativePower) { 33 | result = safeDiv(1, result) 34 | } 35 | 36 | return result 37 | } 38 | -------------------------------------------------------------------------------- /uniswap-squid/src/utils/tools.ts: -------------------------------------------------------------------------------- 1 | import {BatchBlock, EvmBlock} from '@subsquid/evm-processor' 2 | import assert from 'assert' 3 | 4 | export function last(array: T[]): T { 5 | assert(array.length > 0) 6 | return array[array.length - 1] 7 | } 8 | 9 | export function createEntityMap(entities: T[]) { 10 | return new Map(entities.map((e) => [e.id, e])) 11 | } 12 | 13 | export function mergeMaps(a: Map, b: Map): Map { 14 | for (const [k, v] of b) { 15 | a.set(k, v) 16 | } 17 | return a 18 | } 19 | 20 | export function removeNullBytes(str: string): string { 21 | return str.replace(/\0/g, '') 22 | } 23 | 24 | export function processItem(blocks: BatchBlock[], fn: (block: EvmBlock, item: I) => void) { 25 | for (let block of blocks) { 26 | for (let item of block.items) { 27 | fn(block.header, item) 28 | } 29 | } 30 | } 31 | 32 | export function* splitIntoBatches(list: T[], maxBatchSize: number): Generator { 33 | if (list.length <= maxBatchSize) { 34 | yield list 35 | } else { 36 | let offset = 0 37 | while (list.length - offset > maxBatchSize) { 38 | yield list.slice(offset, offset + maxBatchSize) 39 | offset += maxBatchSize 40 | } 41 | yield list.slice(offset) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/transaction.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, OneToMany as OneToMany_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Mint} from "./mint.model" 4 | import {Burn} from "./burn.model" 5 | import {Swap} from "./swap.model" 6 | import {Flash} from "./flash.model" 7 | import {Collect} from "./collect.model" 8 | 9 | @Entity_() 10 | export class Transaction { 11 | constructor(props?: Partial) { 12 | Object.assign(this, props) 13 | } 14 | 15 | @PrimaryColumn_() 16 | id!: string 17 | 18 | @Column_("int4", {nullable: false}) 19 | blockNumber!: number 20 | 21 | @Column_("timestamp with time zone", {nullable: false}) 22 | timestamp!: Date 23 | 24 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 25 | gasUsed!: bigint 26 | 27 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 28 | gasPrice!: bigint 29 | 30 | @OneToMany_(() => Mint, e => e.transaction) 31 | mints!: Mint[] 32 | 33 | @OneToMany_(() => Burn, e => e.transaction) 34 | burns!: Burn[] 35 | 36 | @OneToMany_(() => Swap, e => e.transaction) 37 | swaps!: Swap[] 38 | 39 | @OneToMany_(() => Flash, e => e.transaction) 40 | flashed!: Flash[] 41 | 42 | @OneToMany_(() => Collect, e => e.transaction) 43 | collects!: Collect[] 44 | } 45 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/tickHourData.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Pool} from "./pool.model" 4 | import {Tick} from "./tick.model" 5 | 6 | @Entity_() 7 | export class TickHourData { 8 | constructor(props?: Partial) { 9 | Object.assign(this, props) 10 | } 11 | 12 | @PrimaryColumn_() 13 | id!: string 14 | 15 | @Column_("timestamp with time zone", {nullable: false}) 16 | date!: Date 17 | 18 | @Column_("text", {nullable: false}) 19 | poolId!: string 20 | 21 | @Index_() 22 | @ManyToOne_(() => Pool, {nullable: true}) 23 | pool!: Pool 24 | 25 | @Column_("text", {nullable: false}) 26 | tickId!: string 27 | 28 | @Index_() 29 | @ManyToOne_(() => Tick, {nullable: true}) 30 | tick!: Tick 31 | 32 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 33 | liquidityGross!: bigint 34 | 35 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 36 | liquidityNet!: bigint 37 | 38 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 39 | volumeToken0!: number 40 | 41 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 42 | volumeToken1!: number 43 | 44 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 45 | volumeUSD!: number 46 | 47 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 48 | feesUSD!: number 49 | } 50 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/collect.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Transaction} from "./transaction.model" 4 | import {Pool} from "./pool.model" 5 | 6 | @Entity_() 7 | export class Collect { 8 | constructor(props?: Partial) { 9 | Object.assign(this, props) 10 | } 11 | 12 | @PrimaryColumn_() 13 | id!: string 14 | 15 | @Column_("text", {nullable: false}) 16 | transactionId!: string 17 | 18 | @Index_() 19 | @ManyToOne_(() => Transaction, {nullable: true}) 20 | transaction!: Transaction 21 | 22 | @Column_("timestamp with time zone", {nullable: false}) 23 | timestamp!: Date 24 | 25 | @Column_("text", {nullable: false}) 26 | poolId!: string 27 | 28 | @Index_() 29 | @ManyToOne_(() => Pool, {nullable: true}) 30 | pool!: Pool 31 | 32 | @Column_("text", {nullable: true}) 33 | owner!: string | undefined | null 34 | 35 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 36 | amount0!: number 37 | 38 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 39 | amount1!: number 40 | 41 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: true}) 42 | amountUSD!: number | undefined | null 43 | 44 | @Column_("int4", {nullable: false}) 45 | tickLower!: number 46 | 47 | @Column_("int4", {nullable: false}) 48 | tickUpper!: number 49 | 50 | @Column_("int4", {nullable: true}) 51 | logIndex!: number | undefined | null 52 | } 53 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/tickDayData.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Tick} from "./tick.model" 4 | 5 | @Entity_() 6 | export class TickDayData { 7 | constructor(props?: Partial) { 8 | Object.assign(this, props) 9 | } 10 | 11 | @PrimaryColumn_() 12 | id!: string 13 | 14 | @Column_("timestamp with time zone", {nullable: false}) 15 | date!: Date 16 | 17 | @Column_("text", {nullable: false}) 18 | tickId!: string 19 | 20 | @Index_() 21 | @ManyToOne_(() => Tick, {nullable: true}) 22 | tick!: Tick 23 | 24 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 25 | liquidityGross!: bigint 26 | 27 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 28 | liquidityNet!: bigint 29 | 30 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 31 | volumeToken0!: number 32 | 33 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 34 | volumeToken1!: number 35 | 36 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 37 | volumeUSD!: number 38 | 39 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 40 | feesUSD!: number 41 | 42 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 43 | feeGrowthOutside0X128!: bigint 44 | 45 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 46 | feeGrowthOutside1X128!: bigint 47 | } 48 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/factory.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | 4 | @Entity_() 5 | export class Factory { 6 | constructor(props?: Partial) { 7 | Object.assign(this, props) 8 | } 9 | 10 | @PrimaryColumn_() 11 | id!: string 12 | 13 | @Column_("int4", {nullable: false}) 14 | poolCount!: number 15 | 16 | @Column_("int4", {nullable: false}) 17 | txCount!: number 18 | 19 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 20 | totalVolumeUSD!: number 21 | 22 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 23 | totalVolumeETH!: number 24 | 25 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 26 | totalFeesUSD!: number 27 | 28 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 29 | totalFeesETH!: number 30 | 31 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 32 | untrackedVolumeUSD!: number 33 | 34 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 35 | totalValueLockedUSD!: number 36 | 37 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 38 | totalValueLockedETH!: number 39 | 40 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 41 | totalValueLockedUSDUntracked!: number 42 | 43 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 44 | totalValueLockedETHUntracked!: number 45 | 46 | @Column_("text", {nullable: false}) 47 | owner!: string 48 | } 49 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/flash.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Transaction} from "./transaction.model" 4 | import {Pool} from "./pool.model" 5 | 6 | @Entity_() 7 | export class Flash { 8 | constructor(props?: Partial) { 9 | Object.assign(this, props) 10 | } 11 | 12 | @PrimaryColumn_() 13 | id!: string 14 | 15 | @Index_() 16 | @ManyToOne_(() => Transaction, {nullable: true}) 17 | transaction!: Transaction 18 | 19 | @Column_("text", {nullable: false}) 20 | transactionId!: string 21 | 22 | @Column_("timestamp with time zone", {nullable: false}) 23 | timestamp!: Date 24 | 25 | @Column_("text", {nullable: false}) 26 | poolId!: string 27 | 28 | @Index_() 29 | @ManyToOne_(() => Pool, {nullable: true}) 30 | pool!: Pool 31 | 32 | @Column_("text", {nullable: false}) 33 | sender!: string 34 | 35 | @Column_("text", {nullable: false}) 36 | recipient!: string 37 | 38 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 39 | amount0!: number 40 | 41 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 42 | amount1!: number 43 | 44 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 45 | amountUSD!: number 46 | 47 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 48 | amount0Paid!: number 49 | 50 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 51 | amount1Paid!: number 52 | 53 | @Column_("int4", {nullable: true}) 54 | logIndex!: number | undefined | null 55 | } 56 | -------------------------------------------------------------------------------- /uniswap-squid/src/utils/tick.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable prefer-const */ 2 | import {} from '@subsquid/big-decimal' 3 | import {BlockHandlerContext, LogHandlerContext} from '@subsquid/evm-processor' 4 | import {Store} from '@subsquid/typeorm-store' 5 | import {safeDiv} from '.' 6 | import {Tick} from '../model' 7 | 8 | export function createTick(tickId: string, tickIdx: number, poolId: string): Tick { 9 | let tick = new Tick({id: tickId}) 10 | tick.tickIdx = BigInt(tickIdx) 11 | tick.poolId = poolId 12 | tick.poolAddress = poolId 13 | 14 | // tick.createdAtTimestamp = BigInt(ctx.block.timestamp) 15 | // tick.createdAtBlockNumber = BigInt(ctx.block.height) 16 | tick.liquidityGross = 0n 17 | tick.liquidityNet = 0n 18 | tick.liquidityProviderCount = 0n 19 | 20 | // 1.0001^tick is token1/token0. 21 | let price0 = Math.pow(1.0001, tickIdx) 22 | tick.price0 = price0 23 | tick.price1 = safeDiv(1, price0) 24 | 25 | tick.volumeToken0 = 0 26 | tick.volumeToken1 = 0 27 | tick.volumeUSD = 0 28 | tick.feesUSD = 0 29 | tick.untrackedVolumeUSD = 0 30 | tick.collectedFeesToken0 = 0 31 | tick.collectedFeesToken1 = 0 32 | tick.collectedFeesUSD = 0 33 | tick.liquidityProviderCount = 0n 34 | tick.feeGrowthOutside0X128 = 0n 35 | tick.feeGrowthOutside1X128 = 0n 36 | 37 | return tick 38 | } 39 | 40 | export function feeTierToTickSpacing(feeTier: number): number { 41 | if (feeTier === 10000) { 42 | return 200 43 | } 44 | if (feeTier === 3000) { 45 | return 60 46 | } 47 | if (feeTier === 500) { 48 | return 10 49 | } 50 | if (feeTier === 100) { 51 | return 1 52 | } 53 | 54 | throw Error('Unexpected fee tier') 55 | } 56 | -------------------------------------------------------------------------------- /uniswap-squid/README.md: -------------------------------------------------------------------------------- 1 | # Uniswap V3 squid 2 | 3 | This is a reference squid migrated from the [Uniswap-V3 Subgraph](https://github.com/Uniswap/v3-subgraph) as a reference implementation. Fully indexes Uniswap v3 4 | trading data in about 3 hours on a Mac M1, saves it into a Postgres database and serves as GraphQL API. 5 | The squid fetches data from a test Ethereum Archive maintained by Subsquid Labs, which is currently in beta. 6 | 7 | ## Run 8 | 9 | ### Prerequisites 10 | 11 | - `Node.js 14+`, `npm` 12 | - `Docker` 13 | - A `wss` endpoint 14 | 15 | To run the squid, set `CHAIN_NODE` in `.env` to a `wss` endpoint to a Ethereum mainnet node. It can be obtained e.g. from Infura, or [Node Real](https://nodereal.io) 16 | 17 | 1) Install dependencies: 18 | ```bash 19 | npm ci 20 | ``` 21 | 2) Build the squid: 22 | ```bash 23 | npm run build 24 | ``` 25 | 3) Start the database: 26 | ```bash 27 | make up 28 | ``` 29 | 4) Start the squid ETL (processor): 30 | ```bash 31 | make process 32 | ``` 33 | 5) Start the squid GraphQL API in a separate terminal window: 34 | ```bash 35 | make serve 36 | ``` 37 | 38 | For mode details, inspect the [docs](https://docs.subsquid.io) 39 | 40 | ## Deploy 41 | 42 | The squid can be [deployed to the Aquarium cloud](https://app.subsquid.io). 43 | 1) Create an Aquarium account and obtain a deployment key 44 | 2) [Add a secret](https://docs.subsquid.io/deploy-squid/env-variables/#secrets) called `CHAIN_NODE` 45 | 3) [Deploy the squid](https://docs.subsquid.io/deploy-squid) 46 | 47 | ## Disclaimer 48 | 49 | The Ethereum support of Subsquid is currently in beta. Subsquid Labs provides no guarantees of the stability of the test Archive endpoint used by the squid. 50 | Future releases of the Squid SDK and the EVM archives may introduce breaking changes. 51 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/tokenDayData.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Token} from "./token.model" 4 | 5 | @Entity_() 6 | export class TokenDayData { 7 | constructor(props?: Partial) { 8 | Object.assign(this, props) 9 | } 10 | 11 | @PrimaryColumn_() 12 | id!: string 13 | 14 | @Column_("timestamp with time zone", {nullable: false}) 15 | date!: Date 16 | 17 | @Column_("text", {nullable: false}) 18 | tokenId!: string 19 | 20 | @Index_() 21 | @ManyToOne_(() => Token, {nullable: true}) 22 | token!: Token 23 | 24 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 25 | volume!: number 26 | 27 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 28 | volumeUSD!: number 29 | 30 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 31 | untrackedVolumeUSD!: number 32 | 33 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 34 | totalValueLocked!: number 35 | 36 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 37 | totalValueLockedUSD!: number 38 | 39 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 40 | priceUSD!: number 41 | 42 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 43 | feesUSD!: number 44 | 45 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 46 | open!: number 47 | 48 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 49 | high!: number 50 | 51 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 52 | low!: number 53 | 54 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 55 | close!: number 56 | } 57 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/tokenHourData.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Token} from "./token.model" 4 | 5 | @Entity_() 6 | export class TokenHourData { 7 | constructor(props?: Partial) { 8 | Object.assign(this, props) 9 | } 10 | 11 | @PrimaryColumn_() 12 | id!: string 13 | 14 | @Column_("timestamp with time zone", {nullable: false}) 15 | date!: Date 16 | 17 | @Column_("text", {nullable: false}) 18 | tokenId!: string 19 | 20 | @Index_() 21 | @ManyToOne_(() => Token, {nullable: true}) 22 | token!: Token 23 | 24 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 25 | volume!: number 26 | 27 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 28 | volumeUSD!: number 29 | 30 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 31 | untrackedVolumeUSD!: number 32 | 33 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 34 | totalValueLocked!: number 35 | 36 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 37 | totalValueLockedUSD!: number 38 | 39 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 40 | priceUSD!: number 41 | 42 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 43 | feesUSD!: number 44 | 45 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 46 | open!: number 47 | 48 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 49 | high!: number 50 | 51 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 52 | low!: number 53 | 54 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 55 | close!: number 56 | } 57 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/token.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, OneToMany as OneToMany_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {TokenDayData} from "./tokenDayData.model" 4 | 5 | @Entity_() 6 | export class Token { 7 | constructor(props?: Partial) { 8 | Object.assign(this, props) 9 | } 10 | 11 | @PrimaryColumn_() 12 | id!: string 13 | 14 | @Column_("text", {nullable: false}) 15 | symbol!: string 16 | 17 | @Column_("text", {nullable: false}) 18 | name!: string 19 | 20 | @Column_("int4", {nullable: false}) 21 | decimals!: number 22 | 23 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 24 | totalSupply!: bigint 25 | 26 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 27 | volume!: number 28 | 29 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 30 | volumeUSD!: number 31 | 32 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 33 | untrackedVolumeUSD!: number 34 | 35 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 36 | feesUSD!: number 37 | 38 | @Column_("int4", {nullable: false}) 39 | txCount!: number 40 | 41 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 42 | poolCount!: bigint 43 | 44 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 45 | totalValueLocked!: number 46 | 47 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 48 | totalValueLockedUSD!: number 49 | 50 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 51 | totalValueLockedUSDUntracked!: number 52 | 53 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 54 | derivedETH!: number 55 | 56 | @Column_("text", {array: true, nullable: false}) 57 | whitelistPools!: (string)[] 58 | 59 | @OneToMany_(() => TokenDayData, e => e.token) 60 | tokenDayData!: TokenDayData[] 61 | } 62 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/swap.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Transaction} from "./transaction.model" 4 | import {Pool} from "./pool.model" 5 | import {Token} from "./token.model" 6 | 7 | @Entity_() 8 | export class Swap { 9 | constructor(props?: Partial) { 10 | Object.assign(this, props) 11 | } 12 | 13 | @PrimaryColumn_() 14 | id!: string 15 | 16 | @Index_() 17 | @ManyToOne_(() => Transaction, {nullable: true}) 18 | transaction!: Transaction 19 | 20 | @Column_("text", {nullable: false}) 21 | transactionId!: string 22 | 23 | @Column_("timestamp with time zone", {nullable: false}) 24 | timestamp!: Date 25 | 26 | @Index_() 27 | @ManyToOne_(() => Pool, {nullable: true}) 28 | pool!: Pool 29 | 30 | @Column_("text", {nullable: false}) 31 | poolId!: string 32 | 33 | @Column_("text", {nullable: false}) 34 | token0Id!: string 35 | 36 | @Index_() 37 | @ManyToOne_(() => Token, {nullable: true}) 38 | token0!: Token 39 | 40 | @Column_("text", {nullable: false}) 41 | token1Id!: string 42 | 43 | @Index_() 44 | @ManyToOne_(() => Token, {nullable: true}) 45 | token1!: Token 46 | 47 | @Column_("text", {nullable: false}) 48 | sender!: string 49 | 50 | @Column_("text", {nullable: false}) 51 | recipient!: string 52 | 53 | @Column_("text", {nullable: false}) 54 | origin!: string 55 | 56 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 57 | amount0!: number 58 | 59 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 60 | amount1!: number 61 | 62 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 63 | amountUSD!: number 64 | 65 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 66 | sqrtPriceX96!: bigint 67 | 68 | @Column_("int4", {nullable: false}) 69 | tick!: number 70 | 71 | @Column_("int4", {nullable: true}) 72 | logIndex!: number | undefined | null 73 | } 74 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/position.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Pool} from "./pool.model" 4 | import {Token} from "./token.model" 5 | 6 | @Entity_() 7 | export class Position { 8 | constructor(props?: Partial) { 9 | Object.assign(this, props) 10 | } 11 | 12 | @PrimaryColumn_() 13 | id!: string 14 | 15 | @Column_("text", {nullable: false}) 16 | owner!: string 17 | 18 | @Column_("text", {nullable: false}) 19 | poolId!: string 20 | 21 | @Index_() 22 | @ManyToOne_(() => Pool, {nullable: true}) 23 | pool!: Pool 24 | 25 | @Column_("text", {nullable: false}) 26 | token0Id!: string 27 | 28 | @Index_() 29 | @ManyToOne_(() => Token, {nullable: true}) 30 | token0!: Token 31 | 32 | @Column_("text", {nullable: false}) 33 | token1Id!: string 34 | 35 | @Index_() 36 | @ManyToOne_(() => Token, {nullable: true}) 37 | token1!: Token 38 | 39 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 40 | liquidity!: bigint 41 | 42 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 43 | depositedToken0!: number 44 | 45 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 46 | depositedToken1!: number 47 | 48 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 49 | withdrawnToken0!: number 50 | 51 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 52 | withdrawnToken1!: number 53 | 54 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 55 | collectedFeesToken0!: number 56 | 57 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 58 | collectedFeesToken1!: number 59 | 60 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 61 | feeGrowthInside0LastX128!: bigint 62 | 63 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 64 | feeGrowthInside1LastX128!: bigint 65 | } 66 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/burn.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Transaction} from "./transaction.model" 4 | import {Pool} from "./pool.model" 5 | import {Token} from "./token.model" 6 | 7 | @Entity_() 8 | export class Burn { 9 | constructor(props?: Partial) { 10 | Object.assign(this, props) 11 | } 12 | 13 | @PrimaryColumn_() 14 | id!: string 15 | 16 | @Column_("text", {nullable: false}) 17 | transactionId!: string 18 | 19 | @Index_() 20 | @ManyToOne_(() => Transaction, {nullable: true}) 21 | transaction!: Transaction 22 | 23 | @Column_("text", {nullable: false}) 24 | poolId!: string 25 | 26 | @Index_() 27 | @ManyToOne_(() => Pool, {nullable: true}) 28 | pool!: Pool 29 | 30 | @Column_("text", {nullable: false}) 31 | token0Id!: string 32 | 33 | @Index_() 34 | @ManyToOne_(() => Token, {nullable: true}) 35 | token0!: Token 36 | 37 | @Column_("text", {nullable: false}) 38 | token1Id!: string 39 | 40 | @Index_() 41 | @ManyToOne_(() => Token, {nullable: true}) 42 | token1!: Token 43 | 44 | @Column_("timestamp with time zone", {nullable: false}) 45 | timestamp!: Date 46 | 47 | @Column_("text", {nullable: true}) 48 | owner!: string | undefined | null 49 | 50 | @Column_("text", {nullable: false}) 51 | origin!: string 52 | 53 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 54 | amount!: bigint 55 | 56 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 57 | amount0!: number 58 | 59 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 60 | amount1!: number 61 | 62 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: true}) 63 | amountUSD!: number | undefined | null 64 | 65 | @Column_("int4", {nullable: false}) 66 | tickLower!: number 67 | 68 | @Column_("int4", {nullable: false}) 69 | tickUpper!: number 70 | 71 | @Column_("int4", {nullable: true}) 72 | logIndex!: number | undefined | null 73 | } 74 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/mint.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Transaction} from "./transaction.model" 4 | import {Pool} from "./pool.model" 5 | import {Token} from "./token.model" 6 | 7 | @Entity_() 8 | export class Mint { 9 | constructor(props?: Partial) { 10 | Object.assign(this, props) 11 | } 12 | 13 | @PrimaryColumn_() 14 | id!: string 15 | 16 | @Column_("text", {nullable: false}) 17 | transactionId!: string 18 | 19 | @Index_() 20 | @ManyToOne_(() => Transaction, {nullable: true}) 21 | transaction!: Transaction 22 | 23 | @Column_("timestamp with time zone", {nullable: false}) 24 | timestamp!: Date 25 | 26 | @Column_("text", {nullable: false}) 27 | poolId!: string 28 | 29 | @Index_() 30 | @ManyToOne_(() => Pool, {nullable: true}) 31 | pool!: Pool 32 | 33 | @Column_("text", {nullable: false}) 34 | token0Id!: string 35 | 36 | @Index_() 37 | @ManyToOne_(() => Token, {nullable: true}) 38 | token0!: Token 39 | 40 | @Column_("text", {nullable: false}) 41 | token1Id!: string 42 | 43 | @Index_() 44 | @ManyToOne_(() => Token, {nullable: true}) 45 | token1!: Token 46 | 47 | @Column_("text", {nullable: false}) 48 | owner!: string 49 | 50 | @Column_("text", {nullable: true}) 51 | sender!: string | undefined | null 52 | 53 | @Column_("text", {nullable: false}) 54 | origin!: string 55 | 56 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 57 | amount!: bigint 58 | 59 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 60 | amount0!: number 61 | 62 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 63 | amount1!: number 64 | 65 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: true}) 66 | amountUSD!: number | undefined | null 67 | 68 | @Column_("int4", {nullable: false}) 69 | tickLower!: number 70 | 71 | @Column_("int4", {nullable: false}) 72 | tickUpper!: number 73 | 74 | @Column_("int4", {nullable: true}) 75 | logIndex!: number | undefined | null 76 | } 77 | -------------------------------------------------------------------------------- /uniswap-squid/src/abi/factory.ts: -------------------------------------------------------------------------------- 1 | import * as ethers from 'ethers' 2 | import {LogEvent, Func, ContractBase} from './abi.support' 3 | import {ABI_JSON} from './factory.abi' 4 | 5 | export const abi = new ethers.utils.Interface(ABI_JSON); 6 | 7 | export const events = { 8 | FeeAmountEnabled: new LogEvent<([fee: number, tickSpacing: number] & {fee: number, tickSpacing: number})>( 9 | abi, '0xc66a3fdf07232cdd185febcc6579d408c241b47ae2f9907d84be655141eeaecc' 10 | ), 11 | OwnerChanged: new LogEvent<([oldOwner: string, newOwner: string] & {oldOwner: string, newOwner: string})>( 12 | abi, '0xb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c' 13 | ), 14 | PoolCreated: new LogEvent<([token0: string, token1: string, fee: number, tickSpacing: number, pool: string] & {token0: string, token1: string, fee: number, tickSpacing: number, pool: string})>( 15 | abi, '0x783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118' 16 | ), 17 | } 18 | 19 | export const functions = { 20 | createPool: new Func<[tokenA: string, tokenB: string, fee: number], {tokenA: string, tokenB: string, fee: number}, string>( 21 | abi, '0xa1671295' 22 | ), 23 | enableFeeAmount: new Func<[fee: number, tickSpacing: number], {fee: number, tickSpacing: number}, []>( 24 | abi, '0x8a7c195f' 25 | ), 26 | feeAmountTickSpacing: new Func<[fee: number], {fee: number}, number>( 27 | abi, '0x22afcccb' 28 | ), 29 | getPool: new Func<[tokenA: string, tokenB: string, fee: number], {tokenA: string, tokenB: string, fee: number}, string>( 30 | abi, '0x1698ee82' 31 | ), 32 | owner: new Func<[], {}, string>( 33 | abi, '0x8da5cb5b' 34 | ), 35 | setOwner: new Func<[_owner: string], {_owner: string}, []>( 36 | abi, '0x13af4035' 37 | ), 38 | } 39 | 40 | export class Contract extends ContractBase { 41 | 42 | feeAmountTickSpacing(fee: number): Promise { 43 | return this.eth_call(functions.feeAmountTickSpacing, [fee]) 44 | } 45 | 46 | getPool(tokenA: string, tokenB: string, fee: number): Promise { 47 | return this.eth_call(functions.getPool, [tokenA, tokenB, fee]) 48 | } 49 | 50 | owner(): Promise { 51 | return this.eth_call(functions.owner, []) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/positionSnapshot.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Pool} from "./pool.model" 4 | import {Position} from "./position.model" 5 | import {Transaction} from "./transaction.model" 6 | 7 | @Entity_() 8 | export class PositionSnapshot { 9 | constructor(props?: Partial) { 10 | Object.assign(this, props) 11 | } 12 | 13 | @PrimaryColumn_() 14 | id!: string 15 | 16 | @Column_("text", {nullable: false}) 17 | owner!: string 18 | 19 | @Column_("text", {nullable: false}) 20 | poolId!: string 21 | 22 | @Index_() 23 | @ManyToOne_(() => Pool, {nullable: true}) 24 | pool!: Pool 25 | 26 | @Column_("text", {nullable: false}) 27 | positionId!: string 28 | 29 | @Index_() 30 | @ManyToOne_(() => Position, {nullable: true}) 31 | position!: Position 32 | 33 | @Column_("int4", {nullable: false}) 34 | blockNumber!: number 35 | 36 | @Column_("timestamp with time zone", {nullable: false}) 37 | timestamp!: Date 38 | 39 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 40 | liquidity!: bigint 41 | 42 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 43 | depositedToken0!: number 44 | 45 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 46 | depositedToken1!: number 47 | 48 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 49 | withdrawnToken0!: number 50 | 51 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 52 | withdrawnToken1!: number 53 | 54 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 55 | collectedFeesToken0!: number 56 | 57 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 58 | collectedFeesToken1!: number 59 | 60 | @Column_("text", {nullable: false}) 61 | transactionId!: string 62 | 63 | @Index_() 64 | @ManyToOne_(() => Transaction, {nullable: true}) 65 | transaction!: Transaction 66 | 67 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 68 | feeGrowthInside0LastX128!: bigint 69 | 70 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 71 | feeGrowthInside1LastX128!: bigint 72 | } 73 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/poolDayData.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Pool} from "./pool.model" 4 | 5 | @Entity_() 6 | export class PoolDayData { 7 | constructor(props?: Partial) { 8 | Object.assign(this, props) 9 | } 10 | 11 | @PrimaryColumn_() 12 | id!: string 13 | 14 | @Column_("timestamp with time zone", {nullable: false}) 15 | date!: Date 16 | 17 | @Column_("text", {nullable: false}) 18 | poolId!: string 19 | 20 | @Index_() 21 | @ManyToOne_(() => Pool, {nullable: true}) 22 | pool!: Pool 23 | 24 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 25 | liquidity!: bigint 26 | 27 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 28 | sqrtPrice!: bigint 29 | 30 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 31 | token0Price!: number 32 | 33 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 34 | token1Price!: number 35 | 36 | @Column_("int4", {nullable: true}) 37 | tick!: number | undefined | null 38 | 39 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 40 | feeGrowthGlobal0X128!: bigint 41 | 42 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 43 | feeGrowthGlobal1X128!: bigint 44 | 45 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 46 | tvlUSD!: number 47 | 48 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 49 | volumeToken0!: number 50 | 51 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 52 | volumeToken1!: number 53 | 54 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 55 | volumeUSD!: number 56 | 57 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 58 | feesUSD!: number 59 | 60 | @Column_("int4", {nullable: false}) 61 | txCount!: number 62 | 63 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 64 | open!: number 65 | 66 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 67 | high!: number 68 | 69 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 70 | low!: number 71 | 72 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 73 | close!: number 74 | } 75 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/poolHourData.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Pool} from "./pool.model" 4 | 5 | @Entity_() 6 | export class PoolHourData { 7 | constructor(props?: Partial) { 8 | Object.assign(this, props) 9 | } 10 | 11 | @PrimaryColumn_() 12 | id!: string 13 | 14 | @Column_("timestamp with time zone", {nullable: false}) 15 | date!: Date 16 | 17 | @Column_("text", {nullable: false}) 18 | poolId!: string 19 | 20 | @Index_() 21 | @ManyToOne_(() => Pool, {nullable: true}) 22 | pool!: Pool 23 | 24 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 25 | liquidity!: bigint 26 | 27 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 28 | sqrtPrice!: bigint 29 | 30 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 31 | token0Price!: number 32 | 33 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 34 | token1Price!: number 35 | 36 | @Column_("int4", {nullable: true}) 37 | tick!: number | undefined | null 38 | 39 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 40 | feeGrowthGlobal0X128!: bigint 41 | 42 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 43 | feeGrowthGlobal1X128!: bigint 44 | 45 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 46 | tvlUSD!: number 47 | 48 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 49 | volumeToken0!: number 50 | 51 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 52 | volumeToken1!: number 53 | 54 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 55 | volumeUSD!: number 56 | 57 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 58 | feesUSD!: number 59 | 60 | @Column_("int4", {nullable: false}) 61 | txCount!: number 62 | 63 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 64 | open!: number 65 | 66 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 67 | high!: number 68 | 69 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 70 | low!: number 71 | 72 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 73 | close!: number 74 | } 75 | -------------------------------------------------------------------------------- /uniswap-squid/src/utils/staticTokenDefinition.ts: -------------------------------------------------------------------------------- 1 | // Initialize a Token Definition with the attributes 2 | export class StaticTokenDefinition { 3 | address: string 4 | symbol: string 5 | name: string 6 | decimals: number 7 | 8 | // Initialize a Token Definition with its attributes 9 | constructor(address: string, symbol: string, name: string, decimals: number) { 10 | this.address = address 11 | this.symbol = symbol 12 | this.name = name 13 | this.decimals = decimals 14 | } 15 | 16 | // Get all tokens with a static defintion 17 | static getStaticDefinitions(): Array { 18 | let staticDefinitions = new Array() 19 | 20 | // Add DGD 21 | let tokenDGD = new StaticTokenDefinition('0xe0b7927c4af23765cb51314a0e0521a9645f0e2a', 'DGD', 'DGD', 9) 22 | staticDefinitions.push(tokenDGD) 23 | 24 | // Add AAVE 25 | let tokenAAVE = new StaticTokenDefinition( 26 | '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9', 27 | 'AAVE', 28 | 'Aave Token', 29 | 18 30 | ) 31 | staticDefinitions.push(tokenAAVE) 32 | 33 | // Add LIF 34 | let tokenLIF = new StaticTokenDefinition('0xeb9951021698b42e4399f9cbb6267aa35f82d59d', 'LIF', 'Lif', 18) 35 | staticDefinitions.push(tokenLIF) 36 | 37 | // Add SVD 38 | let tokenSVD = new StaticTokenDefinition('0xbdeb4b83251fb146687fa19d1c660f99411eefe3', 'SVD', 'savedroid', 18) 39 | staticDefinitions.push(tokenSVD) 40 | 41 | // Add TheDAO 42 | let tokenTheDAO = new StaticTokenDefinition( 43 | '0xbb9bc244d798123fde783fcc1c72d3bb8c189413', 44 | 'TheDAO', 45 | 'TheDAO', 46 | 16 47 | ) 48 | staticDefinitions.push(tokenTheDAO) 49 | 50 | // Add HPB 51 | let tokenHPB = new StaticTokenDefinition('0x38c6a68304cdefb9bec48bbfaaba5c5b47818bb2', 'HPB', 'HPBCoin', 18) 52 | staticDefinitions.push(tokenHPB) 53 | 54 | return staticDefinitions 55 | } 56 | 57 | // Helper for hardcoded tokens 58 | static fromAddress(tokenAddress: string): StaticTokenDefinition | null { 59 | let staticDefinitions = this.getStaticDefinitions() 60 | 61 | // Search the definition using the address 62 | for (let i = 0; i < staticDefinitions.length; i++) { 63 | let staticDefinition = staticDefinitions[i] 64 | if (staticDefinition.address === tokenAddress) { 65 | return staticDefinition 66 | } 67 | } 68 | 69 | // If not found, return null 70 | return null 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /uniswap-squid/src/abi/ERC20.ts: -------------------------------------------------------------------------------- 1 | import * as ethers from 'ethers' 2 | import {LogEvent, Func, ContractBase} from './abi.support' 3 | import {ABI_JSON} from './ERC20.abi' 4 | 5 | export const abi = new ethers.utils.Interface(ABI_JSON); 6 | 7 | export const events = { 8 | Approval: new LogEvent<([owner: string, spender: string, value: ethers.BigNumber] & {owner: string, spender: string, value: ethers.BigNumber})>( 9 | abi, '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925' 10 | ), 11 | Transfer: new LogEvent<([from: string, to: string, value: ethers.BigNumber] & {from: string, to: string, value: ethers.BigNumber})>( 12 | abi, '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' 13 | ), 14 | } 15 | 16 | export const functions = { 17 | name: new Func<[], {}, string>( 18 | abi, '0x06fdde03' 19 | ), 20 | approve: new Func<[_spender: string, _value: ethers.BigNumber], {_spender: string, _value: ethers.BigNumber}, boolean>( 21 | abi, '0x095ea7b3' 22 | ), 23 | totalSupply: new Func<[], {}, ethers.BigNumber>( 24 | abi, '0x18160ddd' 25 | ), 26 | transferFrom: new Func<[_from: string, _to: string, _value: ethers.BigNumber], {_from: string, _to: string, _value: ethers.BigNumber}, boolean>( 27 | abi, '0x23b872dd' 28 | ), 29 | decimals: new Func<[], {}, number>( 30 | abi, '0x313ce567' 31 | ), 32 | balanceOf: new Func<[_owner: string], {_owner: string}, ethers.BigNumber>( 33 | abi, '0x70a08231' 34 | ), 35 | symbol: new Func<[], {}, string>( 36 | abi, '0x95d89b41' 37 | ), 38 | transfer: new Func<[_to: string, _value: ethers.BigNumber], {_to: string, _value: ethers.BigNumber}, boolean>( 39 | abi, '0xa9059cbb' 40 | ), 41 | allowance: new Func<[_owner: string, _spender: string], {_owner: string, _spender: string}, ethers.BigNumber>( 42 | abi, '0xdd62ed3e' 43 | ), 44 | } 45 | 46 | export class Contract extends ContractBase { 47 | 48 | name(): Promise { 49 | return this.eth_call(functions.name, []) 50 | } 51 | 52 | totalSupply(): Promise { 53 | return this.eth_call(functions.totalSupply, []) 54 | } 55 | 56 | decimals(): Promise { 57 | return this.eth_call(functions.decimals, []) 58 | } 59 | 60 | balanceOf(_owner: string): Promise { 61 | return this.eth_call(functions.balanceOf, [_owner]) 62 | } 63 | 64 | symbol(): Promise { 65 | return this.eth_call(functions.symbol, []) 66 | } 67 | 68 | allowance(_owner: string, _spender: string): Promise { 69 | return this.eth_call(functions.allowance, [_owner, _spender]) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/tick.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Pool} from "./pool.model" 4 | 5 | @Entity_() 6 | export class Tick { 7 | constructor(props?: Partial) { 8 | Object.assign(this, props) 9 | } 10 | 11 | @PrimaryColumn_() 12 | id!: string 13 | 14 | @Column_("text", {nullable: true}) 15 | poolAddress!: string | undefined | null 16 | 17 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 18 | tickIdx!: bigint 19 | 20 | @Column_("text", {nullable: false}) 21 | poolId!: string 22 | 23 | @Index_() 24 | @ManyToOne_(() => Pool, {nullable: true}) 25 | pool!: Pool 26 | 27 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 28 | liquidityGross!: bigint 29 | 30 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 31 | liquidityNet!: bigint 32 | 33 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 34 | price0!: number 35 | 36 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 37 | price1!: number 38 | 39 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 40 | volumeToken0!: number 41 | 42 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 43 | volumeToken1!: number 44 | 45 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 46 | volumeUSD!: number 47 | 48 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 49 | untrackedVolumeUSD!: number 50 | 51 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 52 | feesUSD!: number 53 | 54 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 55 | collectedFeesToken0!: number 56 | 57 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 58 | collectedFeesToken1!: number 59 | 60 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 61 | collectedFeesUSD!: number 62 | 63 | @Column_("timestamp with time zone", {nullable: false}) 64 | createdAtTimestamp!: Date 65 | 66 | @Column_("int4", {nullable: false}) 67 | createdAtBlockNumber!: number 68 | 69 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 70 | liquidityProviderCount!: bigint 71 | 72 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 73 | feeGrowthOutside0X128!: bigint 74 | 75 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 76 | feeGrowthOutside1X128!: bigint 77 | } 78 | -------------------------------------------------------------------------------- /uniswap-squid/src/utils/entityManager.ts: -------------------------------------------------------------------------------- 1 | import {assertNotNull, CommonHandlerContext} from '@subsquid/evm-processor' 2 | import {Store} from '@subsquid/typeorm-store' 3 | import {In} from 'typeorm' 4 | import {splitIntoBatches} from './tools' 5 | 6 | export interface EntityClass { 7 | new (): T 8 | } 9 | 10 | export interface Entity { 11 | id: string 12 | } 13 | 14 | export class EntityManager { 15 | private deferredIds = new Map, Set>() 16 | private cache = new Map, Map>() 17 | 18 | constructor(private store: Store) {} 19 | 20 | defer(entity: EntityClass, ...ids: string[]) { 21 | let set = this.deferredIds.get(entity) 22 | if (set == null) { 23 | set = new Set() 24 | this.deferredIds.set(entity, set) 25 | } 26 | 27 | let cache = this.getCache(entity) 28 | for (const id of ids) { 29 | if (!cache.has(id)) set.add(id) 30 | } 31 | return this 32 | } 33 | 34 | async load(entity: EntityClass) { 35 | const fetched = new Map() 36 | 37 | const cache = this.getCache(entity) 38 | 39 | const ids = this.deferredIds.get(entity) 40 | if (!ids || ids.size == 0) return fetched 41 | 42 | for (const idBatch of splitIntoBatches([...ids], 1000)) 43 | await this.store.findBy(entity, {id: In(idBatch)} as any).then((es) => 44 | es.forEach((e) => { 45 | cache.set(e.id, e) 46 | fetched.set(e.id, e) 47 | }) 48 | ) 49 | ids.clear() 50 | 51 | return fetched 52 | } 53 | 54 | get(entity: EntityClass, id: string): Promise 55 | get(entity: EntityClass, id: string, search: false): T | undefined 56 | get(entity: EntityClass, id: string, search = true): Promise | (T | undefined) { 57 | const cache = this.getCache(entity) 58 | let value = cache.get(id) 59 | if (search) { 60 | return value != null 61 | ? new Promise((resolve) => resolve(value)) 62 | : this.store.get(entity, id).then((e) => { 63 | if (e) cache.set(e.id, e) 64 | return e 65 | }) 66 | } else { 67 | return value 68 | } 69 | } 70 | 71 | getOrFail(entity: EntityClass, id: string): Promise 72 | getOrFail(entity: EntityClass, id: string, search: false): T 73 | getOrFail(entity: EntityClass, id: string, search = true): Promise | T { 74 | if (search) { 75 | return this.get(entity, id).then((e) => assertNotNull(e)) 76 | } else { 77 | return assertNotNull(this.get(entity, id, search)) 78 | } 79 | } 80 | 81 | add(entity: T) { 82 | this.getCache(entity.constructor as EntityClass).set(entity.id, entity) 83 | return this 84 | } 85 | 86 | values(entity: EntityClass) { 87 | return [...this.getCache(entity).values()] 88 | } 89 | 90 | private getCache(entity: EntityClass) { 91 | let value = this.cache.get(entity) 92 | if (value == null) { 93 | value = new Map() 94 | this.cache.set(entity, value) 95 | } 96 | return value 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /uniswap-squid/src/utils/pricing.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable prefer-const */ 2 | import {safeDiv} from '../utils/index' 3 | import {BigDecimal} from '@subsquid/big-decimal' 4 | 5 | export const WETH_ADDRESS = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2' 6 | export const USDC_WETH_03_POOL = '0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8' 7 | 8 | // token where amounts should contribute to tracked volume and liquidity 9 | // usually tokens that many tokens are paired with s 10 | export let WHITELIST_TOKENS: string[] = [ 11 | WETH_ADDRESS, // WETH 12 | '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI 13 | '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC 14 | '0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT 15 | '0x0000000000085d4780b73119b644ae5ecd22b376', // TUSD 16 | '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', // WBTC 17 | '0x5d3a536e4d6dbd6114cc1ead35777bab948e3643', // cDAI 18 | '0x39aa39c021dfbae8fac545936693ac917d5e7563', // cUSDC 19 | '0x86fadb80d8d2cff3c3680819e4da99c10232ba0f', // EBASE 20 | '0x57ab1ec28d129707052df4df418d58a2d46d5f51', // sUSD 21 | '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', // MKR 22 | '0xc00e94cb662c3520282e6f5717214004a7f26888', // COMP 23 | '0x514910771af9ca656af840dff83e8264ecf986ca', // LINK 24 | '0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f', // SNX 25 | '0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e', // YFI 26 | '0x111111111117dc0aa78b770fa6a738034120c302', // 1INCH 27 | '0xdf5e0e81dff6faf3a7e52ba697820c5e32d806a8', // yCurv 28 | '0x956f47f50a910163d8bf957cf5846d573e7f87ca', // FEI 29 | '0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0', // MATIC 30 | '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9', // AAVE 31 | '0xfe2e637202056d30016725477c5da089ab0a043a', // sETH2 32 | ] 33 | 34 | export let STABLE_COINS: string[] = [ 35 | '0x6b175474e89094c44da98b954eedeac495271d0f', 36 | '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 37 | '0xdac17f958d2ee523a2206206994597c13d831ec7', 38 | '0x0000000000085d4780b73119b644ae5ecd22b376', 39 | '0x956f47f50a910163d8bf957cf5846d573e7f87ca', 40 | '0x4dd28568d05f09b02220b09c2cb307bfd837cb95', 41 | ] 42 | 43 | export let MINIMUM_ETH_LOCKED = 60 44 | 45 | let Q192 = 2 ** 192 46 | export function sqrtPriceX96ToTokenPrices(sqrtPriceX96: bigint, decimals0: number, decimals1: number): number[] { 47 | let num = sqrtPriceX96 ** 2n 48 | let denom = BigInt(Q192) 49 | 50 | let price1 = BigDecimal(num / denom, decimals1 - decimals0).toNumber() 51 | 52 | let price0 = safeDiv(1, price1) 53 | return [price0, price1] 54 | } 55 | 56 | /** 57 | * Accepts tokens and amounts, return tracked amount based on token whitelist 58 | * If one token on whitelist, return amount in that token converted to USD * 2. 59 | * If both are, return sum of two amounts 60 | * If neither is, return 0 61 | */ 62 | export function getTrackedAmountUSD(token0: string, amount0USD: number, token1: string, amount1USD: number): number { 63 | // both are whitelist tokens, return sum of both amounts 64 | if (WHITELIST_TOKENS.includes(token0) && WHITELIST_TOKENS.includes(token1)) { 65 | return (amount0USD + amount1USD) / 2 66 | } 67 | 68 | // take double value of the whitelisted token amount 69 | if (WHITELIST_TOKENS.includes(token0) && !WHITELIST_TOKENS.includes(token1)) { 70 | return amount0USD 71 | } 72 | 73 | // take double value of the whitelisted token amount 74 | if (!WHITELIST_TOKENS.includes(token0) && WHITELIST_TOKENS.includes(token1)) { 75 | return amount1USD 76 | } 77 | 78 | // neither token is on white list, tracked amount is 0 79 | return 0 80 | } 81 | -------------------------------------------------------------------------------- /uniswap-squid/src/utils/token.ts: -------------------------------------------------------------------------------- 1 | import {BlockHandlerContext} from '@subsquid/evm-processor' 2 | import * as ERC20 from '../abi/ERC20' 3 | import * as ERC20NameBytes from '../abi/ERC20NameBytes' 4 | import * as ERC20SymbolBytes from '../abi/ERC20SymbolBytes' 5 | import {Multicall} from "../abi/multicall" 6 | import {MULTICALL_ADDRESS} from './constants' 7 | import {StaticTokenDefinition} from './staticTokenDefinition' 8 | import {removeNullBytes} from './tools' 9 | 10 | export async function fetchTokensSymbol(ctx: BlockHandlerContext, tokenAddresses: string[]) { 11 | const multicall = new Multicall(ctx, MULTICALL_ADDRESS) 12 | 13 | const symbols = new Map() 14 | 15 | const results = await multicall.tryAggregate(ERC20.functions.symbol, tokenAddresses.map(a => [a, []])) 16 | 17 | results.forEach((res, i) => { 18 | const address = tokenAddresses[i] 19 | let sym: string | undefined 20 | if (res.success) { 21 | sym = res.value 22 | } else if (res.returnData) { 23 | sym = ERC20SymbolBytes.functions.symbol.tryDecodeResult(res.returnData) 24 | } 25 | if (sym) { 26 | symbols.set(address, removeNullBytes(sym)) 27 | } else { 28 | const value = StaticTokenDefinition.fromAddress(address)?.symbol 29 | if (value == null) ctx.log.warn(`Missing symbol for token ${address}`) 30 | symbols.set(address, value || 'unknown') 31 | } 32 | }) 33 | 34 | return symbols 35 | } 36 | 37 | export async function fetchTokensName(ctx: BlockHandlerContext, tokenAddresses: string[]) { 38 | const multicall = new Multicall(ctx, MULTICALL_ADDRESS) 39 | 40 | const names = new Map() 41 | 42 | const results = await multicall.tryAggregate(ERC20.functions.name, tokenAddresses.map(a => [a, []])) 43 | 44 | results.forEach((res, i) => { 45 | const address = tokenAddresses[i] 46 | let name: string | undefined 47 | if (res.success) { 48 | name = res.value 49 | } else if (res.returnData) { 50 | name = ERC20NameBytes.functions.name.tryDecodeResult(res.returnData) 51 | } 52 | if (name) { 53 | names.set(address, removeNullBytes(name)) 54 | } else { 55 | const value = StaticTokenDefinition.fromAddress(address)?.name 56 | if (value == null) ctx.log.warn(`Missing name for token ${address}`) 57 | names.set(address, value || 'unknown') 58 | } 59 | }) 60 | 61 | return names 62 | } 63 | 64 | export async function fetchTokensTotalSupply(ctx: BlockHandlerContext, tokenAddresses: string[]) { 65 | let multicall = new Multicall(ctx, MULTICALL_ADDRESS) 66 | 67 | let results = await multicall.tryAggregate(ERC20.functions.totalSupply, tokenAddresses.map(a => [a, []])) 68 | 69 | return new Map( 70 | results.map((res, i) => { 71 | let address = tokenAddresses[i] 72 | let supply = res.success ? res.value.toBigInt() : 0n 73 | return [address, supply] 74 | }) 75 | ) 76 | } 77 | 78 | export async function fetchTokensDecimals(ctx: BlockHandlerContext, tokenAddresses: string[]) { 79 | let multicall = new Multicall(ctx, MULTICALL_ADDRESS) 80 | 81 | let results = await multicall.tryAggregate(ERC20.functions.decimals, tokenAddresses.map(a => [a, []])) 82 | 83 | return new Map( 84 | results.map((res, i) => { 85 | let address = tokenAddresses[i] 86 | let decimals = res.success ? res.value : 0 87 | return [address, decimals] 88 | }) 89 | ) 90 | } 91 | -------------------------------------------------------------------------------- /uniswap-squid/src/abi/abi.support.ts: -------------------------------------------------------------------------------- 1 | import * as ethers from 'ethers' 2 | 3 | 4 | export interface LogRecord { 5 | topics: string[] 6 | data: string 7 | } 8 | 9 | 10 | export class LogEvent { 11 | private fragment: ethers.utils.EventFragment 12 | 13 | constructor(private abi: ethers.utils.Interface, public readonly topic: string) { 14 | this.fragment = abi.getEvent(topic) 15 | } 16 | 17 | decode(rec: LogRecord): Args { 18 | return this.abi.decodeEventLog(this.fragment, rec.data, rec.topics) as any as Args 19 | } 20 | } 21 | 22 | 23 | export class Func { 24 | private fragment: ethers.utils.FunctionFragment 25 | 26 | constructor(private abi: ethers.utils.Interface, public readonly sighash: string) { 27 | this.fragment = abi.getFunction(sighash) 28 | } 29 | 30 | decode(input: ethers.utils.BytesLike): Args & FieldArgs { 31 | return this.abi.decodeFunctionData(this.fragment, input) as any as Args & FieldArgs 32 | } 33 | 34 | encode(args: Args): string { 35 | return this.abi.encodeFunctionData(this.fragment, args) 36 | } 37 | 38 | decodeResult(output: ethers.utils.BytesLike): Result { 39 | const decoded = this.abi.decodeFunctionResult(this.fragment, output) 40 | return decoded.length > 1 ? decoded : decoded[0] 41 | } 42 | 43 | tryDecodeResult(output: ethers.utils.BytesLike): Result | undefined { 44 | try { 45 | return this.decodeResult(output) 46 | } catch(err: any) { 47 | return undefined 48 | } 49 | } 50 | } 51 | 52 | 53 | export function isFunctionResultDecodingError(val: unknown): val is Error & {data: string} { 54 | if (!(val instanceof Error)) return false 55 | let err = val as any 56 | return err.code == 'CALL_EXCEPTION' 57 | && typeof err.data == 'string' 58 | && !err.errorArgs 59 | && !err.errorName 60 | } 61 | 62 | 63 | export interface ChainContext { 64 | _chain: Chain 65 | } 66 | 67 | 68 | export interface BlockContext { 69 | _chain: Chain 70 | block: Block 71 | } 72 | 73 | 74 | export interface Block { 75 | height: number 76 | } 77 | 78 | 79 | export interface Chain { 80 | client: { 81 | call: (method: string, params?: unknown[]) => Promise 82 | } 83 | } 84 | 85 | 86 | export class ContractBase { 87 | private readonly _chain: Chain 88 | private readonly blockHeight: number 89 | readonly address: string 90 | 91 | constructor(ctx: BlockContext, address: string) 92 | constructor(ctx: ChainContext, block: Block, address: string) 93 | constructor(ctx: BlockContext, blockOrAddress: Block | string, address?: string) { 94 | this._chain = ctx._chain 95 | if (typeof blockOrAddress === 'string') { 96 | this.blockHeight = ctx.block.height 97 | this.address = ethers.utils.getAddress(blockOrAddress) 98 | } else { 99 | if (address == null) { 100 | throw new Error('missing contract address') 101 | } 102 | this.blockHeight = blockOrAddress.height 103 | this.address = ethers.utils.getAddress(address) 104 | } 105 | } 106 | 107 | async eth_call(func: Func, args: Args): Promise { 108 | let data = func.encode(args) 109 | let result = await this._chain.client.call('eth_call', [ 110 | {to: this.address, data}, 111 | '0x'+this.blockHeight.toString(16) 112 | ]) 113 | return func.decodeResult(result) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /uniswap-squid/src/processor.ts: -------------------------------------------------------------------------------- 1 | import {EvmBatchProcessor} from '@subsquid/evm-processor' 2 | import {TypeormDatabase} from '@subsquid/typeorm-store' 3 | import {FACTORY_ADDRESS, POSITIONS_ADDRESS} from './utils/constants' 4 | import {processFactory} from './mappings/factory' 5 | import {processPairs} from './mappings/core' 6 | import {processPositions} from './mappings/positionManager' 7 | import * as factoryAbi from './abi/factory' 8 | import * as poolAbi from './abi/pool' 9 | import * as positionsAbi from './abi/NonfungiblePositionManager' 10 | import {EntityManager} from './utils/entityManager' 11 | import { 12 | Bundle, 13 | Burn, 14 | Factory, 15 | Mint, 16 | Pool, 17 | PoolDayData, 18 | PoolHourData, 19 | Position, 20 | Swap, 21 | Tick, 22 | TickDayData, 23 | Token, 24 | TokenDayData, 25 | TokenHourData, 26 | Transaction, 27 | UniswapDayData, 28 | } from './model' 29 | 30 | let processor = new EvmBatchProcessor() 31 | .setBlockRange({from: 12369621}) 32 | .setDataSource({ 33 | archive: 'https://eth.archive.subsquid.io', 34 | chain: process.env.ETH_CHAIN_NODE, 35 | }) 36 | .addLog(FACTORY_ADDRESS, { 37 | filter: [[factoryAbi.events.PoolCreated.topic]], 38 | data: { 39 | evmLog: { 40 | topics: true, 41 | data: true, 42 | }, 43 | } as const, 44 | }) 45 | .addLog([], { 46 | filter: [ 47 | [ 48 | poolAbi.events.Burn.topic, 49 | poolAbi.events.Mint.topic, 50 | poolAbi.events.Initialize.topic, 51 | poolAbi.events.Swap.topic, 52 | ], 53 | ], 54 | data: { 55 | evmLog: { 56 | topics: true, 57 | data: true, 58 | }, 59 | transaction: { 60 | hash: true, 61 | gasPrice: true, 62 | gas: true, 63 | from: true, 64 | }, 65 | } as const, 66 | }) 67 | .addLog(POSITIONS_ADDRESS, { 68 | filter: [ 69 | [ 70 | positionsAbi.events.IncreaseLiquidity.topic, 71 | positionsAbi.events.DecreaseLiquidity.topic, 72 | positionsAbi.events.Collect.topic, 73 | positionsAbi.events.Transfer.topic, 74 | ], 75 | ], 76 | data: { 77 | evmLog: { 78 | topics: true, 79 | data: true, 80 | }, 81 | } as const, 82 | }) 83 | 84 | processor.run(new TypeormDatabase(), async (ctx) => { 85 | const entities = new EntityManager(ctx.store) 86 | const entitiesCtx = {...ctx, entities} 87 | 88 | await processFactory(entitiesCtx, ctx.blocks) 89 | await processPairs(entitiesCtx, ctx.blocks) 90 | await processPositions(entitiesCtx, ctx.blocks) 91 | 92 | await ctx.store.save(entities.values(Bundle)) 93 | await ctx.store.save(entities.values(Factory)) 94 | await ctx.store.save(entities.values(Token)) 95 | await ctx.store.save(entities.values(Pool)) 96 | await ctx.store.save(entities.values(Tick)) 97 | await ctx.store.insert(entities.values(Transaction)) 98 | await ctx.store.insert(entities.values(Mint)) 99 | await ctx.store.insert(entities.values(Burn)) 100 | await ctx.store.insert(entities.values(Swap)) 101 | await ctx.store.save(entities.values(UniswapDayData)) 102 | await ctx.store.save(entities.values(PoolDayData)) 103 | await ctx.store.save(entities.values(PoolHourData)) 104 | await ctx.store.save(entities.values(TokenDayData)) 105 | await ctx.store.save(entities.values(TokenHourData)) 106 | await ctx.store.save(entities.values(TickDayData)) 107 | await ctx.store.save(entities.values(Position)) 108 | }) 109 | -------------------------------------------------------------------------------- /uniswap-squid/src/utils/intervalUpdates.ts: -------------------------------------------------------------------------------- 1 | import {UniswapDayData, PoolDayData, TokenDayData, TokenHourData, TickDayData} from '../model' 2 | 3 | const HOUR_MS = 60 * 60 * 1000 4 | const DAY_MS = 24 * HOUR_MS 5 | 6 | export function snapshotId(id: string, timeIndex: number) { 7 | return `${id}-${timeIndex}` 8 | } 9 | 10 | export function getDayIndex(timestamp: number) { 11 | return Math.floor(timestamp / DAY_MS) 12 | } 13 | 14 | export function getHourIndex(timestamp: number) { 15 | return Math.floor(timestamp / HOUR_MS) 16 | } 17 | 18 | export function createUniswapDayData(factoryId: string, dayIndex: number) { 19 | const data = new UniswapDayData({id: snapshotId(factoryId, dayIndex)}) 20 | data.date = new Date(dayIndex * DAY_MS) 21 | data.volumeETH = 0 22 | data.volumeUSD = 0 23 | data.volumeUSDUntracked = 0 24 | data.feesUSD = 0 25 | data.tvlUSD = 0 26 | data.txCount = 0 27 | 28 | return data 29 | } 30 | 31 | export function createPoolDayData(poolId: string, dayIndex: number) { 32 | const data = new PoolDayData({id: snapshotId(poolId, dayIndex)}) 33 | data.date = new Date(dayIndex * DAY_MS) 34 | data.poolId = poolId 35 | data.volumeToken0 = 0 36 | data.volumeToken1 = 0 37 | data.volumeUSD = 0 38 | data.feesUSD = 0 39 | data.txCount = 0 40 | data.feeGrowthGlobal0X128 = 0n 41 | data.feeGrowthGlobal1X128 = 0n 42 | data.open = 0 43 | data.high = 0 44 | data.low = 0 45 | data.close = 0 46 | 47 | return data 48 | } 49 | 50 | export function createPoolHourData(poolId: string, hourIndex: number) { 51 | const data = new PoolDayData({id: snapshotId(poolId, hourIndex)}) 52 | data.date = new Date(hourIndex * HOUR_MS) 53 | data.poolId = poolId 54 | data.volumeToken0 = 0 55 | data.volumeToken1 = 0 56 | data.volumeUSD = 0 57 | data.feesUSD = 0 58 | data.txCount = 0 59 | data.feeGrowthGlobal0X128 = 0n 60 | data.feeGrowthGlobal1X128 = 0n 61 | data.open = 0 62 | data.high = 0 63 | data.low = 0 64 | data.close = 0 65 | data.liquidity = 0n 66 | data.sqrtPrice = 0n 67 | data.token0Price = 0 68 | data.token1Price = 0 69 | data.tick = 0 70 | data.tvlUSD = 0 71 | 72 | return data 73 | } 74 | 75 | export function createTokenDayData(tokenId: string, dayIndex: number) { 76 | const data = new TokenDayData({id: snapshotId(tokenId, dayIndex)}) 77 | data.date = new Date(dayIndex * DAY_MS) 78 | data.tokenId = tokenId 79 | data.volume = 0 80 | data.volumeUSD = 0 81 | data.feesUSD = 0 82 | data.untrackedVolumeUSD = 0 83 | data.open = 0 84 | data.high = 0 85 | data.low = 0 86 | data.close = 0 87 | data.priceUSD = 0 88 | data.totalValueLocked = 0 89 | data.totalValueLockedUSD = 0 90 | 91 | return data 92 | } 93 | 94 | export function createTokenHourData(tokenId: string, hourIndex: number) { 95 | const data = new TokenHourData({id: snapshotId(tokenId, hourIndex)}) 96 | data.date = new Date(hourIndex * DAY_MS) 97 | data.tokenId = tokenId 98 | data.volume = 0 99 | data.volumeUSD = 0 100 | data.feesUSD = 0 101 | data.untrackedVolumeUSD = 0 102 | data.open = 0 103 | data.high = 0 104 | data.low = 0 105 | data.close = 0 106 | data.priceUSD = 0 107 | data.totalValueLocked = 0 108 | data.totalValueLockedUSD = 0 109 | 110 | return data 111 | } 112 | 113 | export function createTickDayData(tickId: string, dayIndex: number) { 114 | const data = new TickDayData({id: snapshotId(tickId, dayIndex)}) 115 | data.date = new Date(dayIndex * DAY_MS) 116 | data.tickId = tickId 117 | data.liquidityGross = 0n 118 | data.liquidityNet = 0n 119 | data.volumeToken0 = 0 120 | data.volumeToken1 = 0 121 | data.volumeUSD = 0 122 | data.feesUSD = 0 123 | data.feeGrowthOutside0X128 = 0n 124 | data.feeGrowthOutside1X128 = 0n 125 | 126 | return data 127 | } 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | Subsquid Logo 5 | 6 |

7 | 8 | [![docs.rs](https://docs.rs/leptos/badge.svg)](https://docs.subsquid.io/) 9 | [![Discord](https://img.shields.io/discord/1031524867910148188?color=%237289DA&label=discord)](https://discord.gg/subsquid) 10 | 11 | [Website](https://subsquid.io) | [Docs](https://docs.rs/leptos/badge.svg) | [Discord](https://discord.gg/subsquid) 12 | 13 | # Subsquid 14 | 15 | Subsquid is the most dev-friendly way to develop and deploy custom GraphQL APIs and ETLs for advanced blockchain use cases. Subsquid is a full-stack blockchain indexing SDK and specialized data lakes (Archives) optimized for extraction of large volumes of historical on-chain data. The SDK offers a highly customizable Extract-Transform-Load-Query stack and indexing speeds of up to and beyond 50,000 blocks per second when indexing events and transactions. 16 | 17 | Subsquid is on a mission to democratize Web3 data access by providing unrestricted access to the data lake without any charges or rate limits. To achieve this, Subsquid uses an Archive, which is a specialized data lake designed for on-chain data and optimized for batch data access. The Archive API is meant to be used as a data source for Squid SDK processors. 18 | Compared to data access using a conventional chain node RPC, an Archive allows one to access data at near zero cost, in a more granular fashion and from multiple blocks at once, thanks to its rich batching and filtering capabilities. 19 | 20 | # Documentation and Useful Links 21 | 22 | - [How To Create a Squid](https://docs.subsquid.io/tutorials/) 23 | - [How To Query a Squid](https://docs.subsquid.io/query-squid/) 24 | - [How To Deploy a Squid](https://docs.subsquid.io/deploy-squid/) 25 | - [How To query a Squid From an Application](https://thegraph.com/docs/en/querying/querying-from-an-application/) 26 | - [How to Create a Squid for EVM chains](https://docs.subsquid.io/examples/evm/) 27 | - [How to Use Subsquid with Substrate](https://docs.subsquid.io/examples/substrate/) 28 | - [Subsquid vs The Graph](https://docs.subsquid.io/migrate/subsquid-vs-thegraph/) 29 | - [Supported Networks](https://docs.subsquid.io/evm-indexing/supported-networks/) 30 | - [Giant Squid API](https://docs.subsquid.io/giant-squid-api/) 31 | - [Developer FAQs](https://docs.subsquid.io/faq/) 32 | 33 | # Tutorials 34 | 35 | - [Subsquid Basics](https://docs.subsquid.io/basics/) 36 | - [How to Index Bored Ape Yacht Club NFTs](https://docs.subsquid.io/tutorials/bayc/) 37 | - [How to Create a Simple Substrate Squid](https://docs.subsquid.io/tutorials/create-a-simple-squid/) 38 | - [How to Index ink! Contract with Subsquid](https://docs.subsquid.io/tutorials/create-a-wasm-processing-squid/) 39 | - [How to Build Full-Stack NFT Marketplace with Subsquid](https://www.youtube.com/watch?v=Kt8qDmREDKU&ab_channel=subsquid) 40 | - [Subsquid Academy](https://www.youtube.com/watch?v=x4fEP0KJ3OE&list=PLH2948XqklrgTvG6-ro3eqS17j7n_raiN&ab_channel=subsquid) 41 | - [How to Streamline Cross-Chain and EVM Processing with Subsquid](https://academy.moonbeam.network/p/moonbeam-subsquid) 42 | - [How to Build Defi Dashaboard using Subsquid](https://www.youtube.com/watch?v=KVabPQqdKno&ab_channel=subsquid) 43 | - [How to Analyze Lens Protocol using Subsquid SDK](https://www.youtube.com/watch?v=6xAEEwlNm4E&ab_channel=subsquid) 44 | - [How to Build an Airdrop Dapp with React, Wagmi and Subsquid](https://medium.com/subsquid/build-an-airdrop-dapp-with-react-wagmi-and-subsquid-ca0256dd74f5) 45 | 46 | # GitHub Repositories 47 | 48 | - [Sample Squid Indexing Transactions To and From vitalik.eth](https://github.com/subsquid-labs/evm-transactions-example) 49 | - [UniswapV3 Squid](https://github.com/subsquid-labs/uniswapv3-squid) 50 | - [Parquet Datasets](https://github.com/subsquid-labs/Subsquid-Datasets) 51 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/pool.model.ts: -------------------------------------------------------------------------------- 1 | import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, ManyToOne as ManyToOne_, Index as Index_, OneToMany as OneToMany_} from "typeorm" 2 | import * as marshal from "./marshal" 3 | import {Token} from "./token.model" 4 | import {PoolHourData} from "./poolHourData.model" 5 | import {PoolDayData} from "./poolDayData.model" 6 | import {Mint} from "./mint.model" 7 | import {Burn} from "./burn.model" 8 | import {Swap} from "./swap.model" 9 | import {Collect} from "./collect.model" 10 | import {Tick} from "./tick.model" 11 | 12 | @Entity_() 13 | export class Pool { 14 | constructor(props?: Partial) { 15 | Object.assign(this, props) 16 | } 17 | 18 | @PrimaryColumn_() 19 | id!: string 20 | 21 | @Column_("timestamp with time zone", {nullable: false}) 22 | createdAtTimestamp!: Date 23 | 24 | @Column_("int4", {nullable: false}) 25 | createdAtBlockNumber!: number 26 | 27 | @Column_("text", {nullable: false}) 28 | token0Id!: string 29 | 30 | @Index_() 31 | @ManyToOne_(() => Token, {nullable: true}) 32 | token0!: Token 33 | 34 | @Column_("text", {nullable: false}) 35 | token1Id!: string 36 | 37 | @Index_() 38 | @ManyToOne_(() => Token, {nullable: true}) 39 | token1!: Token 40 | 41 | @Column_("int4", {nullable: false}) 42 | feeTier!: number 43 | 44 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 45 | liquidity!: bigint 46 | 47 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 48 | sqrtPrice!: bigint 49 | 50 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 51 | feeGrowthGlobal0X128!: bigint 52 | 53 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 54 | feeGrowthGlobal1X128!: bigint 55 | 56 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 57 | token0Price!: number 58 | 59 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 60 | token1Price!: number 61 | 62 | @Column_("int4", {nullable: true}) 63 | tick!: number | undefined | null 64 | 65 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 66 | observationIndex!: bigint 67 | 68 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 69 | volumeToken0!: number 70 | 71 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 72 | volumeToken1!: number 73 | 74 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 75 | volumeUSD!: number 76 | 77 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 78 | untrackedVolumeUSD!: number 79 | 80 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 81 | feesUSD!: number 82 | 83 | @Column_("int4", {nullable: false}) 84 | txCount!: number 85 | 86 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 87 | collectedFeesToken0!: number 88 | 89 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 90 | collectedFeesToken1!: number 91 | 92 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 93 | collectedFeesUSD!: number 94 | 95 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 96 | totalValueLockedToken0!: number 97 | 98 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 99 | totalValueLockedToken1!: number 100 | 101 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 102 | totalValueLockedETH!: number 103 | 104 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 105 | totalValueLockedUSD!: number 106 | 107 | @Column_("numeric", {transformer: marshal.floatTransformer, nullable: false}) 108 | totalValueLockedUSDUntracked!: number 109 | 110 | @Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false}) 111 | liquidityProviderCount!: bigint 112 | 113 | @OneToMany_(() => PoolHourData, e => e.pool) 114 | poolHourData!: PoolHourData[] 115 | 116 | @OneToMany_(() => PoolDayData, e => e.pool) 117 | poolDayData!: PoolDayData[] 118 | 119 | @OneToMany_(() => Mint, e => e.pool) 120 | mints!: Mint[] 121 | 122 | @OneToMany_(() => Burn, e => e.pool) 123 | burns!: Burn[] 124 | 125 | @OneToMany_(() => Swap, e => e.pool) 126 | swaps!: Swap[] 127 | 128 | @OneToMany_(() => Collect, e => e.pool) 129 | collects!: Collect[] 130 | 131 | @OneToMany_(() => Tick, e => e.pool) 132 | ticks!: Tick[] 133 | } 134 | -------------------------------------------------------------------------------- /uniswap-squid/abis/factory.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "uint24", 8 | "name": "fee", 9 | "type": "uint24" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "int24", 14 | "name": "tickSpacing", 15 | "type": "int24" 16 | } 17 | ], 18 | "name": "FeeAmountEnabled", 19 | "type": "event" 20 | }, 21 | { 22 | "anonymous": false, 23 | "inputs": [ 24 | { 25 | "indexed": true, 26 | "internalType": "address", 27 | "name": "oldOwner", 28 | "type": "address" 29 | }, 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "newOwner", 34 | "type": "address" 35 | } 36 | ], 37 | "name": "OwnerChanged", 38 | "type": "event" 39 | }, 40 | { 41 | "anonymous": false, 42 | "inputs": [ 43 | { 44 | "indexed": true, 45 | "internalType": "address", 46 | "name": "token0", 47 | "type": "address" 48 | }, 49 | { 50 | "indexed": true, 51 | "internalType": "address", 52 | "name": "token1", 53 | "type": "address" 54 | }, 55 | { 56 | "indexed": true, 57 | "internalType": "uint24", 58 | "name": "fee", 59 | "type": "uint24" 60 | }, 61 | { 62 | "indexed": false, 63 | "internalType": "int24", 64 | "name": "tickSpacing", 65 | "type": "int24" 66 | }, 67 | { 68 | "indexed": false, 69 | "internalType": "address", 70 | "name": "pool", 71 | "type": "address" 72 | } 73 | ], 74 | "name": "PoolCreated", 75 | "type": "event" 76 | }, 77 | { 78 | "inputs": [ 79 | { 80 | "internalType": "address", 81 | "name": "tokenA", 82 | "type": "address" 83 | }, 84 | { 85 | "internalType": "address", 86 | "name": "tokenB", 87 | "type": "address" 88 | }, 89 | { 90 | "internalType": "uint24", 91 | "name": "fee", 92 | "type": "uint24" 93 | } 94 | ], 95 | "name": "createPool", 96 | "outputs": [ 97 | { 98 | "internalType": "address", 99 | "name": "pool", 100 | "type": "address" 101 | } 102 | ], 103 | "stateMutability": "nonpayable", 104 | "type": "function" 105 | }, 106 | { 107 | "inputs": [ 108 | { 109 | "internalType": "uint24", 110 | "name": "fee", 111 | "type": "uint24" 112 | }, 113 | { 114 | "internalType": "int24", 115 | "name": "tickSpacing", 116 | "type": "int24" 117 | } 118 | ], 119 | "name": "enableFeeAmount", 120 | "outputs": [], 121 | "stateMutability": "nonpayable", 122 | "type": "function" 123 | }, 124 | { 125 | "inputs": [ 126 | { 127 | "internalType": "uint24", 128 | "name": "fee", 129 | "type": "uint24" 130 | } 131 | ], 132 | "name": "feeAmountTickSpacing", 133 | "outputs": [ 134 | { 135 | "internalType": "int24", 136 | "name": "", 137 | "type": "int24" 138 | } 139 | ], 140 | "stateMutability": "view", 141 | "type": "function" 142 | }, 143 | { 144 | "inputs": [ 145 | { 146 | "internalType": "address", 147 | "name": "tokenA", 148 | "type": "address" 149 | }, 150 | { 151 | "internalType": "address", 152 | "name": "tokenB", 153 | "type": "address" 154 | }, 155 | { 156 | "internalType": "uint24", 157 | "name": "fee", 158 | "type": "uint24" 159 | } 160 | ], 161 | "name": "getPool", 162 | "outputs": [ 163 | { 164 | "internalType": "address", 165 | "name": "pool", 166 | "type": "address" 167 | } 168 | ], 169 | "stateMutability": "view", 170 | "type": "function" 171 | }, 172 | { 173 | "inputs": [], 174 | "name": "owner", 175 | "outputs": [ 176 | { 177 | "internalType": "address", 178 | "name": "", 179 | "type": "address" 180 | } 181 | ], 182 | "stateMutability": "view", 183 | "type": "function" 184 | }, 185 | { 186 | "inputs": [ 187 | { 188 | "internalType": "address", 189 | "name": "_owner", 190 | "type": "address" 191 | } 192 | ], 193 | "name": "setOwner", 194 | "outputs": [], 195 | "stateMutability": "nonpayable", 196 | "type": "function" 197 | } 198 | ] -------------------------------------------------------------------------------- /uniswap-squid/abis/ERC20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "name", 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "string" 10 | } 11 | ], 12 | "payable": false, 13 | "stateMutability": "view", 14 | "type": "function" 15 | }, 16 | { 17 | "constant": false, 18 | "inputs": [ 19 | { 20 | "name": "_spender", 21 | "type": "address" 22 | }, 23 | { 24 | "name": "_value", 25 | "type": "uint256" 26 | } 27 | ], 28 | "name": "approve", 29 | "outputs": [ 30 | { 31 | "name": "", 32 | "type": "bool" 33 | } 34 | ], 35 | "payable": false, 36 | "stateMutability": "nonpayable", 37 | "type": "function" 38 | }, 39 | { 40 | "constant": true, 41 | "inputs": [], 42 | "name": "totalSupply", 43 | "outputs": [ 44 | { 45 | "name": "", 46 | "type": "uint256" 47 | } 48 | ], 49 | "payable": false, 50 | "stateMutability": "view", 51 | "type": "function" 52 | }, 53 | { 54 | "constant": false, 55 | "inputs": [ 56 | { 57 | "name": "_from", 58 | "type": "address" 59 | }, 60 | { 61 | "name": "_to", 62 | "type": "address" 63 | }, 64 | { 65 | "name": "_value", 66 | "type": "uint256" 67 | } 68 | ], 69 | "name": "transferFrom", 70 | "outputs": [ 71 | { 72 | "name": "", 73 | "type": "bool" 74 | } 75 | ], 76 | "payable": false, 77 | "stateMutability": "nonpayable", 78 | "type": "function" 79 | }, 80 | { 81 | "constant": true, 82 | "inputs": [], 83 | "name": "decimals", 84 | "outputs": [ 85 | { 86 | "name": "", 87 | "type": "uint8" 88 | } 89 | ], 90 | "payable": false, 91 | "stateMutability": "view", 92 | "type": "function" 93 | }, 94 | { 95 | "constant": true, 96 | "inputs": [ 97 | { 98 | "name": "_owner", 99 | "type": "address" 100 | } 101 | ], 102 | "name": "balanceOf", 103 | "outputs": [ 104 | { 105 | "name": "balance", 106 | "type": "uint256" 107 | } 108 | ], 109 | "payable": false, 110 | "stateMutability": "view", 111 | "type": "function" 112 | }, 113 | { 114 | "constant": true, 115 | "inputs": [], 116 | "name": "symbol", 117 | "outputs": [ 118 | { 119 | "name": "", 120 | "type": "string" 121 | } 122 | ], 123 | "payable": false, 124 | "stateMutability": "view", 125 | "type": "function" 126 | }, 127 | { 128 | "constant": false, 129 | "inputs": [ 130 | { 131 | "name": "_to", 132 | "type": "address" 133 | }, 134 | { 135 | "name": "_value", 136 | "type": "uint256" 137 | } 138 | ], 139 | "name": "transfer", 140 | "outputs": [ 141 | { 142 | "name": "", 143 | "type": "bool" 144 | } 145 | ], 146 | "payable": false, 147 | "stateMutability": "nonpayable", 148 | "type": "function" 149 | }, 150 | { 151 | "constant": true, 152 | "inputs": [ 153 | { 154 | "name": "_owner", 155 | "type": "address" 156 | }, 157 | { 158 | "name": "_spender", 159 | "type": "address" 160 | } 161 | ], 162 | "name": "allowance", 163 | "outputs": [ 164 | { 165 | "name": "", 166 | "type": "uint256" 167 | } 168 | ], 169 | "payable": false, 170 | "stateMutability": "view", 171 | "type": "function" 172 | }, 173 | { 174 | "payable": true, 175 | "stateMutability": "payable", 176 | "type": "fallback" 177 | }, 178 | { 179 | "anonymous": false, 180 | "inputs": [ 181 | { 182 | "indexed": true, 183 | "name": "owner", 184 | "type": "address" 185 | }, 186 | { 187 | "indexed": true, 188 | "name": "spender", 189 | "type": "address" 190 | }, 191 | { 192 | "indexed": false, 193 | "name": "value", 194 | "type": "uint256" 195 | } 196 | ], 197 | "name": "Approval", 198 | "type": "event" 199 | }, 200 | { 201 | "anonymous": false, 202 | "inputs": [ 203 | { 204 | "indexed": true, 205 | "name": "from", 206 | "type": "address" 207 | }, 208 | { 209 | "indexed": true, 210 | "name": "to", 211 | "type": "address" 212 | }, 213 | { 214 | "indexed": false, 215 | "name": "value", 216 | "type": "uint256" 217 | } 218 | ], 219 | "name": "Transfer", 220 | "type": "event" 221 | } 222 | ] 223 | -------------------------------------------------------------------------------- /uniswap-squid/src/abi/factory.abi.ts: -------------------------------------------------------------------------------- 1 | export const ABI_JSON = [ 2 | { 3 | "type": "event", 4 | "anonymous": false, 5 | "name": "FeeAmountEnabled", 6 | "inputs": [ 7 | { 8 | "type": "uint24", 9 | "name": "fee", 10 | "indexed": true 11 | }, 12 | { 13 | "type": "int24", 14 | "name": "tickSpacing", 15 | "indexed": true 16 | } 17 | ] 18 | }, 19 | { 20 | "type": "event", 21 | "anonymous": false, 22 | "name": "OwnerChanged", 23 | "inputs": [ 24 | { 25 | "type": "address", 26 | "name": "oldOwner", 27 | "indexed": true 28 | }, 29 | { 30 | "type": "address", 31 | "name": "newOwner", 32 | "indexed": true 33 | } 34 | ] 35 | }, 36 | { 37 | "type": "event", 38 | "anonymous": false, 39 | "name": "PoolCreated", 40 | "inputs": [ 41 | { 42 | "type": "address", 43 | "name": "token0", 44 | "indexed": true 45 | }, 46 | { 47 | "type": "address", 48 | "name": "token1", 49 | "indexed": true 50 | }, 51 | { 52 | "type": "uint24", 53 | "name": "fee", 54 | "indexed": true 55 | }, 56 | { 57 | "type": "int24", 58 | "name": "tickSpacing", 59 | "indexed": false 60 | }, 61 | { 62 | "type": "address", 63 | "name": "pool", 64 | "indexed": false 65 | } 66 | ] 67 | }, 68 | { 69 | "type": "function", 70 | "name": "createPool", 71 | "constant": false, 72 | "payable": false, 73 | "inputs": [ 74 | { 75 | "type": "address", 76 | "name": "tokenA" 77 | }, 78 | { 79 | "type": "address", 80 | "name": "tokenB" 81 | }, 82 | { 83 | "type": "uint24", 84 | "name": "fee" 85 | } 86 | ], 87 | "outputs": [ 88 | { 89 | "type": "address", 90 | "name": "pool" 91 | } 92 | ] 93 | }, 94 | { 95 | "type": "function", 96 | "name": "enableFeeAmount", 97 | "constant": false, 98 | "payable": false, 99 | "inputs": [ 100 | { 101 | "type": "uint24", 102 | "name": "fee" 103 | }, 104 | { 105 | "type": "int24", 106 | "name": "tickSpacing" 107 | } 108 | ], 109 | "outputs": [] 110 | }, 111 | { 112 | "type": "function", 113 | "name": "feeAmountTickSpacing", 114 | "constant": true, 115 | "stateMutability": "view", 116 | "payable": false, 117 | "inputs": [ 118 | { 119 | "type": "uint24", 120 | "name": "fee" 121 | } 122 | ], 123 | "outputs": [ 124 | { 125 | "type": "int24" 126 | } 127 | ] 128 | }, 129 | { 130 | "type": "function", 131 | "name": "getPool", 132 | "constant": true, 133 | "stateMutability": "view", 134 | "payable": false, 135 | "inputs": [ 136 | { 137 | "type": "address", 138 | "name": "tokenA" 139 | }, 140 | { 141 | "type": "address", 142 | "name": "tokenB" 143 | }, 144 | { 145 | "type": "uint24", 146 | "name": "fee" 147 | } 148 | ], 149 | "outputs": [ 150 | { 151 | "type": "address", 152 | "name": "pool" 153 | } 154 | ] 155 | }, 156 | { 157 | "type": "function", 158 | "name": "owner", 159 | "constant": true, 160 | "stateMutability": "view", 161 | "payable": false, 162 | "inputs": [], 163 | "outputs": [ 164 | { 165 | "type": "address" 166 | } 167 | ] 168 | }, 169 | { 170 | "type": "function", 171 | "name": "setOwner", 172 | "constant": false, 173 | "payable": false, 174 | "inputs": [ 175 | { 176 | "type": "address", 177 | "name": "_owner" 178 | } 179 | ], 180 | "outputs": [] 181 | } 182 | ] 183 | -------------------------------------------------------------------------------- /uniswap-squid/src/model/generated/marshal.ts: -------------------------------------------------------------------------------- 1 | import assert from 'assert' 2 | 3 | 4 | export interface Marshal { 5 | fromJSON(value: unknown): T 6 | toJSON(value: T): S 7 | } 8 | 9 | 10 | export const string: Marshal = { 11 | fromJSON(value: unknown): string { 12 | assert(typeof value === 'string', 'invalid String') 13 | return value 14 | }, 15 | toJSON(value) { 16 | return value 17 | } 18 | } 19 | 20 | 21 | export const id = string 22 | 23 | 24 | export const int: Marshal = { 25 | fromJSON(value: unknown): number { 26 | assert(Number.isInteger(value), 'invalid Int') 27 | return value as number 28 | }, 29 | toJSON(value) { 30 | return value 31 | } 32 | } 33 | 34 | 35 | export const float: Marshal = { 36 | fromJSON(value: unknown): number { 37 | assert(typeof value === 'number', 'invalid Float') 38 | return value as number 39 | }, 40 | toJSON(value) { 41 | return value 42 | } 43 | } 44 | 45 | 46 | export const boolean: Marshal = { 47 | fromJSON(value: unknown): boolean { 48 | assert(typeof value === 'boolean', 'invalid Boolean') 49 | return value 50 | }, 51 | toJSON(value: boolean): boolean { 52 | return value 53 | } 54 | } 55 | 56 | 57 | export const bigint: Marshal = { 58 | fromJSON(value: unknown): bigint { 59 | assert(typeof value === 'string', 'invalid BigInt') 60 | return BigInt(value) 61 | }, 62 | toJSON(value: bigint): string { 63 | return value.toString() 64 | } 65 | } 66 | 67 | 68 | export const bigdecimal: Marshal = { 69 | fromJSON(value: unknown): bigint { 70 | assert(typeof value === 'string', 'invalid BigDecimal') 71 | return decimal.BigDecimal(value) 72 | }, 73 | toJSON(value: any): string { 74 | return value.toString() 75 | } 76 | } 77 | 78 | 79 | // credit - https://github.com/Urigo/graphql-scalars/blob/91b4ea8df891be8af7904cf84751930cc0c6613d/src/scalars/iso-date/validator.ts#L122 80 | const RFC_3339_REGEX = 81 | /^(\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60))(\.\d{1,})?([Z])$/ 82 | 83 | 84 | function isIsoDateTimeString(s: string): boolean { 85 | return RFC_3339_REGEX.test(s) 86 | } 87 | 88 | 89 | export const datetime: Marshal = { 90 | fromJSON(value: unknown): Date { 91 | assert(typeof value === 'string', 'invalid DateTime') 92 | assert(isIsoDateTimeString(value), 'invalid DateTime') 93 | return new Date(value) 94 | }, 95 | toJSON(value: Date): string { 96 | return value.toISOString() 97 | } 98 | } 99 | 100 | 101 | export const bytes: Marshal = { 102 | fromJSON(value: unknown): Buffer { 103 | assert(typeof value === 'string', 'invalid Bytes') 104 | assert(value.length % 2 === 0, 'invalid Bytes') 105 | assert(/^0x[0-9a-f]+$/i.test(value), 'invalid Bytes') 106 | return Buffer.from(value.slice(2), 'hex') 107 | }, 108 | toJSON(value: Uint8Array): string { 109 | if (Buffer.isBuffer(value)) { 110 | return '0x' + value.toString('hex') 111 | } else { 112 | return '0x' + Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString('hex') 113 | } 114 | } 115 | } 116 | 117 | 118 | export function fromList(list: unknown, f: (val: unknown) => T): T[] { 119 | assert(Array.isArray(list)) 120 | return list.map((val) => f(val)) 121 | } 122 | 123 | 124 | export function nonNull(val: T | undefined | null): T { 125 | assert(val != null, 'non-nullable value is null') 126 | return val 127 | } 128 | 129 | 130 | export const bigintTransformer = { 131 | to(x?: bigint) { 132 | return x?.toString() 133 | }, 134 | from(s?: string): bigint | undefined { 135 | return s == null ? undefined : BigInt(s) 136 | } 137 | } 138 | 139 | 140 | export const floatTransformer = { 141 | to(x?: number) { 142 | return x?.toString() 143 | }, 144 | from(s?: string): number | undefined { 145 | return s == null ? undefined : Number(s) 146 | } 147 | } 148 | 149 | 150 | export const bigdecimalTransformer = { 151 | to(x?: any) { 152 | return x?.toString() 153 | }, 154 | from(s?: any): any | undefined { 155 | return s == null ? undefined : decimal.BigDecimal(s) 156 | } 157 | } 158 | 159 | 160 | export function enumFromJson(json: unknown, enumObject: E): E[keyof E] { 161 | assert(typeof json == 'string', 'invalid enum value') 162 | let val = (enumObject as any)[json] 163 | assert(typeof val == 'string', `invalid enum value`) 164 | return val as any 165 | } 166 | 167 | 168 | const decimal = { 169 | get BigDecimal(): any { 170 | throw new Error('Package `@subsquid/big-decimal` is not installed') 171 | } 172 | } 173 | 174 | 175 | try { 176 | Object.defineProperty(decimal, "BigDecimal", { 177 | value: require('@subsquid/big-decimal').BigDecimal 178 | }) 179 | } catch (e) {} 180 | -------------------------------------------------------------------------------- /uniswap-squid/src/abi/ERC20.abi.ts: -------------------------------------------------------------------------------- 1 | export const ABI_JSON = [ 2 | { 3 | "type": "function", 4 | "name": "name", 5 | "constant": true, 6 | "stateMutability": "view", 7 | "payable": false, 8 | "inputs": [], 9 | "outputs": [ 10 | { 11 | "type": "string" 12 | } 13 | ] 14 | }, 15 | { 16 | "type": "function", 17 | "name": "approve", 18 | "constant": false, 19 | "payable": false, 20 | "inputs": [ 21 | { 22 | "type": "address", 23 | "name": "_spender" 24 | }, 25 | { 26 | "type": "uint256", 27 | "name": "_value" 28 | } 29 | ], 30 | "outputs": [ 31 | { 32 | "type": "bool" 33 | } 34 | ] 35 | }, 36 | { 37 | "type": "function", 38 | "name": "totalSupply", 39 | "constant": true, 40 | "stateMutability": "view", 41 | "payable": false, 42 | "inputs": [], 43 | "outputs": [ 44 | { 45 | "type": "uint256" 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "function", 51 | "name": "transferFrom", 52 | "constant": false, 53 | "payable": false, 54 | "inputs": [ 55 | { 56 | "type": "address", 57 | "name": "_from" 58 | }, 59 | { 60 | "type": "address", 61 | "name": "_to" 62 | }, 63 | { 64 | "type": "uint256", 65 | "name": "_value" 66 | } 67 | ], 68 | "outputs": [ 69 | { 70 | "type": "bool" 71 | } 72 | ] 73 | }, 74 | { 75 | "type": "function", 76 | "name": "decimals", 77 | "constant": true, 78 | "stateMutability": "view", 79 | "payable": false, 80 | "inputs": [], 81 | "outputs": [ 82 | { 83 | "type": "uint8" 84 | } 85 | ] 86 | }, 87 | { 88 | "type": "function", 89 | "name": "balanceOf", 90 | "constant": true, 91 | "stateMutability": "view", 92 | "payable": false, 93 | "inputs": [ 94 | { 95 | "type": "address", 96 | "name": "_owner" 97 | } 98 | ], 99 | "outputs": [ 100 | { 101 | "type": "uint256", 102 | "name": "balance" 103 | } 104 | ] 105 | }, 106 | { 107 | "type": "function", 108 | "name": "symbol", 109 | "constant": true, 110 | "stateMutability": "view", 111 | "payable": false, 112 | "inputs": [], 113 | "outputs": [ 114 | { 115 | "type": "string" 116 | } 117 | ] 118 | }, 119 | { 120 | "type": "function", 121 | "name": "transfer", 122 | "constant": false, 123 | "payable": false, 124 | "inputs": [ 125 | { 126 | "type": "address", 127 | "name": "_to" 128 | }, 129 | { 130 | "type": "uint256", 131 | "name": "_value" 132 | } 133 | ], 134 | "outputs": [ 135 | { 136 | "type": "bool" 137 | } 138 | ] 139 | }, 140 | { 141 | "type": "function", 142 | "name": "allowance", 143 | "constant": true, 144 | "stateMutability": "view", 145 | "payable": false, 146 | "inputs": [ 147 | { 148 | "type": "address", 149 | "name": "_owner" 150 | }, 151 | { 152 | "type": "address", 153 | "name": "_spender" 154 | } 155 | ], 156 | "outputs": [ 157 | { 158 | "type": "uint256" 159 | } 160 | ] 161 | }, 162 | { 163 | "type": "event", 164 | "anonymous": false, 165 | "name": "Approval", 166 | "inputs": [ 167 | { 168 | "type": "address", 169 | "name": "owner", 170 | "indexed": true 171 | }, 172 | { 173 | "type": "address", 174 | "name": "spender", 175 | "indexed": true 176 | }, 177 | { 178 | "type": "uint256", 179 | "name": "value", 180 | "indexed": false 181 | } 182 | ] 183 | }, 184 | { 185 | "type": "event", 186 | "anonymous": false, 187 | "name": "Transfer", 188 | "inputs": [ 189 | { 190 | "type": "address", 191 | "name": "from", 192 | "indexed": true 193 | }, 194 | { 195 | "type": "address", 196 | "name": "to", 197 | "indexed": true 198 | }, 199 | { 200 | "type": "uint256", 201 | "name": "value", 202 | "indexed": false 203 | } 204 | ] 205 | } 206 | ] 207 | -------------------------------------------------------------------------------- /uniswap-squid/src/abi/multicall.ts: -------------------------------------------------------------------------------- 1 | import * as ethers from 'ethers' 2 | import {ContractBase, Func} from './abi.support' 3 | 4 | 5 | const abi = new ethers.utils.Interface([ 6 | { 7 | type: 'function', 8 | name: 'aggregate', 9 | stateMutability: 'nonpayable', 10 | inputs: [ 11 | { 12 | name: 'calls', 13 | type: 'tuple[]', 14 | components: [ 15 | {name: 'target', type: 'address'}, 16 | {name: 'callData', type: 'bytes'}, 17 | ] 18 | } 19 | ], 20 | outputs: [ 21 | {name: 'blockNumber', type: 'uint256'}, 22 | {name: 'returnData', type: 'bytes[]'}, 23 | ] 24 | }, 25 | { 26 | name: 'tryAggregate', 27 | type: 'function', 28 | stateMutability: 'nonpayable', 29 | inputs: [ 30 | {name: 'requireSuccess', type: 'bool'}, 31 | { 32 | name: 'calls', 33 | type: 'tuple[]', 34 | components: [ 35 | {name: 'target', type: 'address'}, 36 | {name: 'callData', type: 'bytes'}, 37 | ] 38 | } 39 | ], 40 | outputs: [ 41 | { 42 | name: 'returnData', 43 | type: 'tuple[]', 44 | components: [ 45 | {name: 'success', type: 'bool'}, 46 | {name: 'returnData', type: 'bytes'}, 47 | ] 48 | }, 49 | ] 50 | } 51 | ]) 52 | 53 | 54 | type AnyFunc = Func 55 | type Call = [address: string, bytes: string] 56 | 57 | 58 | const aggregate = new Func<[calls: Call[]], {}, {blockNumber: ethers.BigNumber, returnData: string[]}>( 59 | abi, abi.getSighash('aggregate') 60 | ) 61 | 62 | 63 | const try_aggregate = new Func<[requireSuccess: boolean, calls: Array<[target: string, callData: string]>], {}, Array<{success: boolean, returnData: string}>>( 64 | abi, abi.getSighash('tryAggregate') 65 | ) 66 | 67 | 68 | export type MulticallResult = { 69 | success: true 70 | value: T 71 | } | { 72 | success: false 73 | returnData?: string 74 | value?: undefined 75 | } 76 | 77 | 78 | export class Multicall extends ContractBase { 79 | static aggregate = aggregate 80 | static try_aggregate = try_aggregate 81 | 82 | aggregate( 83 | func: Func, 84 | address: string, 85 | calls: Args[], 86 | paging?: number 87 | ): Promise 88 | 89 | aggregate( 90 | func: Func, 91 | calls: [address: string, args: Args][], 92 | paging?: number 93 | ): Promise 94 | 95 | aggregate( 96 | calls: [func: AnyFunc, address: string, args: any[]][], 97 | paging?: number 98 | ): Promise 99 | 100 | async aggregate(...args: any[]): Promise { 101 | let [calls, funcs, page] = this.makeCalls(args) 102 | let size = calls.length 103 | let results = new Array(size) 104 | for (let [from, to] of splitIntoPages(size, page)) { 105 | let {returnData} = await this.eth_call(aggregate, [calls.slice(from, to)]) 106 | for (let i = from; i < to; i++) { 107 | let data = returnData[i - from] 108 | results[i] = funcs[i].decodeResult(data) 109 | } 110 | } 111 | return results 112 | } 113 | 114 | tryAggregate( 115 | func: Func, 116 | address: string, 117 | calls: Args[], 118 | paging?: number 119 | ): Promise[]> 120 | 121 | tryAggregate( 122 | func: Func, 123 | calls: [address: string, args: Args][], 124 | paging?: number 125 | ): Promise[]> 126 | 127 | tryAggregate( 128 | calls: [func: AnyFunc, address: string, args: any[]][], 129 | paging?: number 130 | ): Promise[]> 131 | 132 | async tryAggregate(...args: any[]): Promise { 133 | let [calls, funcs, page] = this.makeCalls(args) 134 | let size = calls.length 135 | let results = new Array(size) 136 | for (let [from, to] of splitIntoPages(size, page)) { 137 | let response = await this.eth_call(try_aggregate, [false, calls.slice(from, to)]) 138 | for (let i = from; i < to; i++) { 139 | let res = response[i - from] 140 | if (res.success) { 141 | try { 142 | results[i] = { 143 | success: true, 144 | value: funcs[i].decodeResult(res.returnData) 145 | } 146 | } catch(err: any) { 147 | results[i] = {success: false, returnData: res.returnData} 148 | } 149 | } else { 150 | results[i] = {success: false} 151 | } 152 | } 153 | } 154 | return results 155 | } 156 | 157 | private makeCalls(args: any[]): [calls: Call[], funcs: AnyFunc[], page: number] { 158 | let page = typeof args[args.length-1] == 'number' ? args.pop()! : Number.MAX_SAFE_INTEGER 159 | switch(args.length) { 160 | case 1: { 161 | let list: [func: AnyFunc, address: string, args: any[]][] = args[0] 162 | let calls = new Array(list.length) 163 | let funcs = new Array(list.length) 164 | for (let i = 0; i < list.length; i++) { 165 | let [func, address, args] = list[i] 166 | calls[i] = [address, func.encode(args)] 167 | funcs[i] = func 168 | } 169 | return [calls, funcs, page] 170 | } 171 | case 2: { 172 | let func: AnyFunc = args[0] 173 | let list: [address: string, args: any[]][] = args[1] 174 | let calls = new Array(list.length) 175 | let funcs = new Array(list.length) 176 | for (let i = 0; i < list.length; i++) { 177 | let [address, args] = list[i] 178 | calls[i] = [address, func.encode(args)] 179 | funcs[i] = func 180 | } 181 | return [calls, funcs, page] 182 | } 183 | case 3: { 184 | let func: AnyFunc = args[0] 185 | let address: string = args[1] 186 | let list: any[][] = args[2] 187 | let calls = new Array(list.length) 188 | let funcs = new Array(list.length) 189 | for (let i = 0; i < list.length; i++) { 190 | let args = list[i] 191 | calls[i] = [address, func.encode(args)] 192 | funcs[i] = func 193 | } 194 | return [calls, funcs, page] 195 | } 196 | default: 197 | throw new Error('unexpected number of arguments') 198 | } 199 | } 200 | } 201 | 202 | 203 | function* splitIntoPages(size: number, page: number): Iterable<[from: number, to: number]> { 204 | let from = 0 205 | while (size) { 206 | let step = Math.min(page, size) 207 | let to = from + step 208 | yield [from, to] 209 | size -= step 210 | from = to 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /uniswap-squid/src/mappings/factory.ts: -------------------------------------------------------------------------------- 1 | import {assertNotNull, BatchBlock, BlockHandlerContext, CommonHandlerContext} from '@subsquid/evm-processor' 2 | import {LogItem, TransactionItem} from '@subsquid/evm-processor/lib/interfaces/dataSelection' 3 | import * as factoryAbi from '../abi/factory' 4 | import {Bundle, Factory, Pool, Token} from '../model' 5 | import {BlockMap} from '../utils/blockMap' 6 | import {ADDRESS_ZERO, FACTORY_ADDRESS} from '../utils/constants' 7 | import {EntityManager} from '../utils/entityManager' 8 | import {WHITELIST_TOKENS} from '../utils/pricing' 9 | import {fetchTokensDecimals, fetchTokensName, fetchTokensSymbol, fetchTokensTotalSupply} from '../utils/token' 10 | import {last, processItem} from '../utils/tools' 11 | 12 | interface PairCreatedData { 13 | poolId: string 14 | token0Id: string 15 | token1Id: string 16 | fee: number 17 | } 18 | 19 | type ContextWithEntityManager = CommonHandlerContext & {entities: EntityManager} 20 | 21 | // export class FactoryProcessor extends MappingProcessor { 22 | export async function processFactory(ctx: ContextWithEntityManager, blocks: BatchBlock[]): Promise { 23 | const newPairsData = await processItems(ctx, blocks) 24 | if (newPairsData.size == 0) return 25 | 26 | await prefetch(ctx, newPairsData) 27 | 28 | let bundle = await ctx.entities.get(Bundle, '1') 29 | if (!bundle) { 30 | bundle = createBundle('1') 31 | ctx.entities.add(bundle) 32 | } 33 | 34 | let factory = await ctx.entities.get(Factory, FACTORY_ADDRESS) 35 | if (!factory) { 36 | factory = createFactory(FACTORY_ADDRESS) 37 | ctx.entities.add(factory) 38 | } 39 | 40 | factory.poolCount++ 41 | 42 | for (const [block, blockEventsData] of newPairsData) { 43 | for (const data of blockEventsData) { 44 | const pool = createPool(data.poolId, data.token0Id, data.token1Id) 45 | pool.feeTier = data.fee 46 | pool.createdAtTimestamp = new Date(block.timestamp) 47 | pool.createdAtBlockNumber = block.height 48 | 49 | ctx.entities.add(pool) 50 | 51 | let token0 = ctx.entities.get(Token, pool.token0Id, false) 52 | if (!token0) { 53 | token0 = createToken(pool.token0Id) 54 | ctx.entities.add(token0) 55 | } 56 | 57 | let token1 = ctx.entities.get(Token, pool.token1Id, false) 58 | if (!token1) { 59 | token1 = createToken(pool.token1Id) 60 | ctx.entities.add(token1) 61 | } 62 | 63 | // update whitelisted pools 64 | if (WHITELIST_TOKENS.includes(token0.id)) token1.whitelistPools.push(pool.id) 65 | if (WHITELIST_TOKENS.includes(token1.id)) token0.whitelistPools.push(pool.id) 66 | } 67 | } 68 | 69 | await syncTokens({...ctx, block: last(blocks).header}, ctx.entities.values(Token)) 70 | 71 | // await ctx.store.save(bundle) 72 | // await ctx.store.save(factory) 73 | // await ctx.store.save(ctx.entities.values(Token)) 74 | // await ctx.store.save(ctx.entities.values(Pool)) 75 | } 76 | 77 | async function prefetch(ctx: ContextWithEntityManager, eventsData: BlockMap) { 78 | for (const [, blockEventsData] of eventsData) { 79 | for (const data of blockEventsData) { 80 | ctx.entities.defer(Token, data.token0Id, data.token1Id) 81 | } 82 | } 83 | 84 | await ctx.entities.load(Token) 85 | } 86 | 87 | async function processItems(ctx: CommonHandlerContext, blocks: BatchBlock[]) { 88 | let newPairsData = new BlockMap() 89 | 90 | processItem(blocks, (block, item) => { 91 | if ( 92 | item.address !== FACTORY_ADDRESS || 93 | item.kind !== 'evmLog' || 94 | item.evmLog.topics[0] !== factoryAbi.events.PoolCreated.topic 95 | ) 96 | return 97 | 98 | const event = factoryAbi.events.PoolCreated.decode(item.evmLog) 99 | 100 | // // temp fix (i don't know what is this) 101 | // if (event.pool.toLowerCase() == '0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248') return 102 | 103 | newPairsData.push(block, { 104 | poolId: event.pool.toLowerCase(), 105 | token0Id: event.token0.toLowerCase(), 106 | token1Id: event.token1.toLowerCase(), 107 | fee: event.fee, 108 | }) 109 | }) 110 | 111 | return newPairsData 112 | } 113 | 114 | function createFactory(id: string) { 115 | const factory = new Factory({id}) 116 | factory.poolCount = 0 117 | factory.totalVolumeETH = 0 118 | factory.totalVolumeUSD = 0 119 | factory.untrackedVolumeUSD = 0 120 | factory.totalFeesUSD = 0 121 | factory.totalFeesETH = 0 122 | factory.totalValueLockedETH = 0 123 | factory.totalValueLockedUSD = 0 124 | factory.totalValueLockedUSDUntracked = 0 125 | factory.totalValueLockedETHUntracked = 0 126 | factory.txCount = 0 127 | factory.owner = ADDRESS_ZERO 128 | 129 | return factory 130 | } 131 | 132 | function createToken(id: string) { 133 | let token = new Token({id}) 134 | token.symbol = 'unknown' 135 | token.name = 'unknown' 136 | token.totalSupply = 0n 137 | token.decimals = 0 138 | token.derivedETH = 0 139 | token.volume = 0 140 | token.volumeUSD = 0 141 | token.feesUSD = 0 142 | token.untrackedVolumeUSD = 0 143 | token.totalValueLocked = 0 144 | token.totalValueLockedUSD = 0 145 | token.totalValueLockedUSDUntracked = 0 146 | token.txCount = 0 147 | token.poolCount = 0n 148 | token.whitelistPools = [] 149 | 150 | return token 151 | } 152 | 153 | function createBundle(id: string) { 154 | const bundle = new Bundle({id}) 155 | bundle.ethPriceUSD = 0 156 | 157 | return bundle 158 | } 159 | 160 | function createPool(id: string, token0Id: string, token1Id: string) { 161 | let pool = new Pool({id}) 162 | 163 | pool.token0Id = token0Id 164 | pool.token1Id = token1Id 165 | pool.feeTier = 0 166 | pool.liquidityProviderCount = 0n 167 | pool.txCount = 0 168 | pool.liquidity = 0n 169 | pool.sqrtPrice = 0n 170 | pool.feeGrowthGlobal0X128 = 0n 171 | pool.feeGrowthGlobal1X128 = 0n 172 | pool.token0Price = 0 173 | pool.token1Price = 0 174 | pool.observationIndex = 0n 175 | pool.totalValueLockedToken0 = 0 176 | pool.totalValueLockedToken1 = 0 177 | pool.totalValueLockedUSD = 0 178 | pool.totalValueLockedETH = 0 179 | pool.totalValueLockedUSDUntracked = 0 180 | pool.volumeToken0 = 0 181 | pool.volumeToken1 = 0 182 | pool.volumeUSD = 0 183 | pool.feesUSD = 0 184 | pool.untrackedVolumeUSD = 0 185 | 186 | pool.collectedFeesToken0 = 0 187 | pool.collectedFeesToken1 = 0 188 | pool.collectedFeesUSD = 0 189 | 190 | return pool 191 | } 192 | 193 | async function syncTokens(ctx: BlockHandlerContext, tokens: Token[]) { 194 | const ids = tokens.map((t) => t.id) 195 | 196 | const [symbols, names, totalSupplies, decimals] = await Promise.all([ 197 | fetchTokensSymbol(ctx, ids), 198 | fetchTokensName(ctx, ids), 199 | fetchTokensTotalSupply(ctx, ids), 200 | fetchTokensDecimals(ctx, ids), 201 | ]) 202 | 203 | for (const token of tokens) { 204 | token.symbol = assertNotNull(symbols.get(token.id)) 205 | token.name = assertNotNull(names.get(token.id)) 206 | token.totalSupply = assertNotNull(totalSupplies.get(token.id)) 207 | token.decimals = assertNotNull(decimals.get(token.id)) 208 | } 209 | } 210 | 211 | type Item = 212 | | LogItem<{ 213 | evmLog: { 214 | topics: true 215 | data: true 216 | } 217 | }> 218 | | TransactionItem 219 | -------------------------------------------------------------------------------- /uniswap-squid/src/abi/NonfungiblePositionManager.ts: -------------------------------------------------------------------------------- 1 | import * as ethers from 'ethers' 2 | import {LogEvent, Func, ContractBase} from './abi.support' 3 | import {ABI_JSON} from './NonfungiblePositionManager.abi' 4 | 5 | export const abi = new ethers.utils.Interface(ABI_JSON); 6 | 7 | export const events = { 8 | Approval: new LogEvent<([owner: string, approved: string, tokenId: ethers.BigNumber] & {owner: string, approved: string, tokenId: ethers.BigNumber})>( 9 | abi, '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925' 10 | ), 11 | ApprovalForAll: new LogEvent<([owner: string, operator: string, approved: boolean] & {owner: string, operator: string, approved: boolean})>( 12 | abi, '0x17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31' 13 | ), 14 | Collect: new LogEvent<([tokenId: ethers.BigNumber, recipient: string, amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {tokenId: ethers.BigNumber, recipient: string, amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 15 | abi, '0x40d0efd1a53d60ecbf40971b9daf7dc90178c3aadc7aab1765632738fa8b8f01' 16 | ), 17 | DecreaseLiquidity: new LogEvent<([tokenId: ethers.BigNumber, liquidity: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {tokenId: ethers.BigNumber, liquidity: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 18 | abi, '0x26f6a048ee9138f2c0ce266f322cb99228e8d619ae2bff30c67f8dcf9d2377b4' 19 | ), 20 | IncreaseLiquidity: new LogEvent<([tokenId: ethers.BigNumber, liquidity: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {tokenId: ethers.BigNumber, liquidity: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 21 | abi, '0x3067048beee31b25b2f1681f88dac838c8bba36af25bfb2b7cf7473a5847e35f' 22 | ), 23 | Transfer: new LogEvent<([from: string, to: string, tokenId: ethers.BigNumber] & {from: string, to: string, tokenId: ethers.BigNumber})>( 24 | abi, '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' 25 | ), 26 | } 27 | 28 | export const functions = { 29 | DOMAIN_SEPARATOR: new Func<[], {}, string>( 30 | abi, '0x3644e515' 31 | ), 32 | PERMIT_TYPEHASH: new Func<[], {}, string>( 33 | abi, '0x30adf81f' 34 | ), 35 | WETH9: new Func<[], {}, string>( 36 | abi, '0x4aa4a4fc' 37 | ), 38 | approve: new Func<[to: string, tokenId: ethers.BigNumber], {to: string, tokenId: ethers.BigNumber}, []>( 39 | abi, '0x095ea7b3' 40 | ), 41 | balanceOf: new Func<[owner: string], {owner: string}, ethers.BigNumber>( 42 | abi, '0x70a08231' 43 | ), 44 | baseURI: new Func<[], {}, string>( 45 | abi, '0x6c0360eb' 46 | ), 47 | burn: new Func<[tokenId: ethers.BigNumber], {tokenId: ethers.BigNumber}, []>( 48 | abi, '0x42966c68' 49 | ), 50 | collect: new Func<[tokenId: ethers.BigNumber, recipient: string, amount0Max: ethers.BigNumber, amount1Max: ethers.BigNumber], {tokenId: ethers.BigNumber, recipient: string, amount0Max: ethers.BigNumber, amount1Max: ethers.BigNumber}, ([amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 51 | abi, '0x260e12b0' 52 | ), 53 | createAndInitializePoolIfNecessary: new Func<[tokenA: string, tokenB: string, fee: number, sqrtPriceX96: ethers.BigNumber], {tokenA: string, tokenB: string, fee: number, sqrtPriceX96: ethers.BigNumber}, string>( 54 | abi, '0x13ead562' 55 | ), 56 | decreaseLiquidity: new Func<[tokenId: ethers.BigNumber, liquidity: ethers.BigNumber, amount0Min: ethers.BigNumber, amount1Min: ethers.BigNumber, deadline: ethers.BigNumber], {tokenId: ethers.BigNumber, liquidity: ethers.BigNumber, amount0Min: ethers.BigNumber, amount1Min: ethers.BigNumber, deadline: ethers.BigNumber}, ([amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 57 | abi, '0x03a3f2ab' 58 | ), 59 | factory: new Func<[], {}, string>( 60 | abi, '0xc45a0155' 61 | ), 62 | getApproved: new Func<[tokenId: ethers.BigNumber], {tokenId: ethers.BigNumber}, string>( 63 | abi, '0x081812fc' 64 | ), 65 | increaseLiquidity: new Func<[tokenId: ethers.BigNumber, amount0Desired: ethers.BigNumber, amount1Desired: ethers.BigNumber, amount0Min: ethers.BigNumber, amount1Min: ethers.BigNumber, deadline: ethers.BigNumber], {tokenId: ethers.BigNumber, amount0Desired: ethers.BigNumber, amount1Desired: ethers.BigNumber, amount0Min: ethers.BigNumber, amount1Min: ethers.BigNumber, deadline: ethers.BigNumber}, ([liquidity: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {liquidity: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 66 | abi, '0x12d7b2c4' 67 | ), 68 | isApprovedForAll: new Func<[owner: string, operator: string], {owner: string, operator: string}, boolean>( 69 | abi, '0xe985e9c5' 70 | ), 71 | mint: new Func<[params: ([token0: string, token1: string, fee: number, tickLower: number, tickUpper: number, amount0Desired: ethers.BigNumber, amount1Desired: ethers.BigNumber, amount0Min: ethers.BigNumber, amount1Min: ethers.BigNumber, recipient: string, deadline: ethers.BigNumber] & {token0: string, token1: string, fee: number, tickLower: number, tickUpper: number, amount0Desired: ethers.BigNumber, amount1Desired: ethers.BigNumber, amount0Min: ethers.BigNumber, amount1Min: ethers.BigNumber, recipient: string, deadline: ethers.BigNumber})], {params: ([token0: string, token1: string, fee: number, tickLower: number, tickUpper: number, amount0Desired: ethers.BigNumber, amount1Desired: ethers.BigNumber, amount0Min: ethers.BigNumber, amount1Min: ethers.BigNumber, recipient: string, deadline: ethers.BigNumber] & {token0: string, token1: string, fee: number, tickLower: number, tickUpper: number, amount0Desired: ethers.BigNumber, amount1Desired: ethers.BigNumber, amount0Min: ethers.BigNumber, amount1Min: ethers.BigNumber, recipient: string, deadline: ethers.BigNumber})}, ([tokenId: ethers.BigNumber, liquidity: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {tokenId: ethers.BigNumber, liquidity: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 72 | abi, '0x88316456' 73 | ), 74 | multicall: new Func<[data: Array], {data: Array}, Array>( 75 | abi, '0xac9650d8' 76 | ), 77 | name: new Func<[], {}, string>( 78 | abi, '0x06fdde03' 79 | ), 80 | ownerOf: new Func<[tokenId: ethers.BigNumber], {tokenId: ethers.BigNumber}, string>( 81 | abi, '0x6352211e' 82 | ), 83 | permit: new Func<[spender: string, tokenId: ethers.BigNumber, deadline: ethers.BigNumber, v: number, r: string, s: string], {spender: string, tokenId: ethers.BigNumber, deadline: ethers.BigNumber, v: number, r: string, s: string}, []>( 84 | abi, '0x7ac2ff7b' 85 | ), 86 | positions: new Func<[tokenId: ethers.BigNumber], {tokenId: ethers.BigNumber}, ([nonce: ethers.BigNumber, operator: string, token0: string, token1: string, fee: number, tickLower: number, tickUpper: number, liquidity: ethers.BigNumber, feeGrowthInside0LastX128: ethers.BigNumber, feeGrowthInside1LastX128: ethers.BigNumber, tokensOwed0: ethers.BigNumber, tokensOwed1: ethers.BigNumber] & {nonce: ethers.BigNumber, operator: string, token0: string, token1: string, fee: number, tickLower: number, tickUpper: number, liquidity: ethers.BigNumber, feeGrowthInside0LastX128: ethers.BigNumber, feeGrowthInside1LastX128: ethers.BigNumber, tokensOwed0: ethers.BigNumber, tokensOwed1: ethers.BigNumber})>( 87 | abi, '0x99fbab88' 88 | ), 89 | 'safeTransferFrom(address,address,uint256)': new Func<[from: string, to: string, tokenId: ethers.BigNumber], {from: string, to: string, tokenId: ethers.BigNumber}, []>( 90 | abi, '0x42842e0e' 91 | ), 92 | 'safeTransferFrom(address,address,uint256,bytes)': new Func<[from: string, to: string, tokenId: ethers.BigNumber, _data: string], {from: string, to: string, tokenId: ethers.BigNumber, _data: string}, []>( 93 | abi, '0xb88d4fde' 94 | ), 95 | selfPermit: new Func<[token: string, value: ethers.BigNumber, deadline: ethers.BigNumber, v: number, r: string, s: string], {token: string, value: ethers.BigNumber, deadline: ethers.BigNumber, v: number, r: string, s: string}, []>( 96 | abi, '0xf3995c67' 97 | ), 98 | selfPermitAllowed: new Func<[token: string, nonce: ethers.BigNumber, expiry: ethers.BigNumber, v: number, r: string, s: string], {token: string, nonce: ethers.BigNumber, expiry: ethers.BigNumber, v: number, r: string, s: string}, []>( 99 | abi, '0x4659a494' 100 | ), 101 | selfPermitAllowedIfNecessary: new Func<[token: string, nonce: ethers.BigNumber, expiry: ethers.BigNumber, v: number, r: string, s: string], {token: string, nonce: ethers.BigNumber, expiry: ethers.BigNumber, v: number, r: string, s: string}, []>( 102 | abi, '0xa4a78f0c' 103 | ), 104 | selfPermitIfNecessary: new Func<[token: string, value: ethers.BigNumber, deadline: ethers.BigNumber, v: number, r: string, s: string], {token: string, value: ethers.BigNumber, deadline: ethers.BigNumber, v: number, r: string, s: string}, []>( 105 | abi, '0xc2e3140a' 106 | ), 107 | setApprovalForAll: new Func<[operator: string, approved: boolean], {operator: string, approved: boolean}, []>( 108 | abi, '0xa22cb465' 109 | ), 110 | supportsInterface: new Func<[interfaceId: string], {interfaceId: string}, boolean>( 111 | abi, '0x01ffc9a7' 112 | ), 113 | sweepToken: new Func<[token: string, amountMinimum: ethers.BigNumber, recipient: string], {token: string, amountMinimum: ethers.BigNumber, recipient: string}, []>( 114 | abi, '0xdf2ab5bb' 115 | ), 116 | symbol: new Func<[], {}, string>( 117 | abi, '0x95d89b41' 118 | ), 119 | tokenByIndex: new Func<[index: ethers.BigNumber], {index: ethers.BigNumber}, ethers.BigNumber>( 120 | abi, '0x4f6ccce7' 121 | ), 122 | tokenOfOwnerByIndex: new Func<[owner: string, index: ethers.BigNumber], {owner: string, index: ethers.BigNumber}, ethers.BigNumber>( 123 | abi, '0x2f745c59' 124 | ), 125 | tokenURI: new Func<[tokenId: ethers.BigNumber], {tokenId: ethers.BigNumber}, string>( 126 | abi, '0xc87b56dd' 127 | ), 128 | totalSupply: new Func<[], {}, ethers.BigNumber>( 129 | abi, '0x18160ddd' 130 | ), 131 | transferFrom: new Func<[from: string, to: string, tokenId: ethers.BigNumber], {from: string, to: string, tokenId: ethers.BigNumber}, []>( 132 | abi, '0x23b872dd' 133 | ), 134 | uniswapV3MintCallback: new Func<[amount0Owed: ethers.BigNumber, amount1Owed: ethers.BigNumber, data: string], {amount0Owed: ethers.BigNumber, amount1Owed: ethers.BigNumber, data: string}, []>( 135 | abi, '0xd3487997' 136 | ), 137 | unwrapWETH9: new Func<[amountMinimum: ethers.BigNumber, recipient: string], {amountMinimum: ethers.BigNumber, recipient: string}, []>( 138 | abi, '0x49404b7c' 139 | ), 140 | } 141 | 142 | export class Contract extends ContractBase { 143 | 144 | DOMAIN_SEPARATOR(): Promise { 145 | return this.eth_call(functions.DOMAIN_SEPARATOR, []) 146 | } 147 | 148 | PERMIT_TYPEHASH(): Promise { 149 | return this.eth_call(functions.PERMIT_TYPEHASH, []) 150 | } 151 | 152 | WETH9(): Promise { 153 | return this.eth_call(functions.WETH9, []) 154 | } 155 | 156 | balanceOf(owner: string): Promise { 157 | return this.eth_call(functions.balanceOf, [owner]) 158 | } 159 | 160 | baseURI(): Promise { 161 | return this.eth_call(functions.baseURI, []) 162 | } 163 | 164 | factory(): Promise { 165 | return this.eth_call(functions.factory, []) 166 | } 167 | 168 | getApproved(tokenId: ethers.BigNumber): Promise { 169 | return this.eth_call(functions.getApproved, [tokenId]) 170 | } 171 | 172 | isApprovedForAll(owner: string, operator: string): Promise { 173 | return this.eth_call(functions.isApprovedForAll, [owner, operator]) 174 | } 175 | 176 | name(): Promise { 177 | return this.eth_call(functions.name, []) 178 | } 179 | 180 | ownerOf(tokenId: ethers.BigNumber): Promise { 181 | return this.eth_call(functions.ownerOf, [tokenId]) 182 | } 183 | 184 | positions(tokenId: ethers.BigNumber): Promise<([nonce: ethers.BigNumber, operator: string, token0: string, token1: string, fee: number, tickLower: number, tickUpper: number, liquidity: ethers.BigNumber, feeGrowthInside0LastX128: ethers.BigNumber, feeGrowthInside1LastX128: ethers.BigNumber, tokensOwed0: ethers.BigNumber, tokensOwed1: ethers.BigNumber] & {nonce: ethers.BigNumber, operator: string, token0: string, token1: string, fee: number, tickLower: number, tickUpper: number, liquidity: ethers.BigNumber, feeGrowthInside0LastX128: ethers.BigNumber, feeGrowthInside1LastX128: ethers.BigNumber, tokensOwed0: ethers.BigNumber, tokensOwed1: ethers.BigNumber})> { 185 | return this.eth_call(functions.positions, [tokenId]) 186 | } 187 | 188 | supportsInterface(interfaceId: string): Promise { 189 | return this.eth_call(functions.supportsInterface, [interfaceId]) 190 | } 191 | 192 | symbol(): Promise { 193 | return this.eth_call(functions.symbol, []) 194 | } 195 | 196 | tokenByIndex(index: ethers.BigNumber): Promise { 197 | return this.eth_call(functions.tokenByIndex, [index]) 198 | } 199 | 200 | tokenOfOwnerByIndex(owner: string, index: ethers.BigNumber): Promise { 201 | return this.eth_call(functions.tokenOfOwnerByIndex, [owner, index]) 202 | } 203 | 204 | tokenURI(tokenId: ethers.BigNumber): Promise { 205 | return this.eth_call(functions.tokenURI, [tokenId]) 206 | } 207 | 208 | totalSupply(): Promise { 209 | return this.eth_call(functions.totalSupply, []) 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /uniswap-squid/src/abi/pool.ts: -------------------------------------------------------------------------------- 1 | import * as ethers from 'ethers' 2 | import {LogEvent, Func, ContractBase} from './abi.support' 3 | import {ABI_JSON} from './pool.abi' 4 | 5 | export const abi = new ethers.utils.Interface(ABI_JSON); 6 | 7 | export const events = { 8 | Burn: new LogEvent<([owner: string, tickLower: number, tickUpper: number, amount: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {owner: string, tickLower: number, tickUpper: number, amount: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 9 | abi, '0x0c396cd989a39f4459b5fa1aed6a9a8dcdbc45908acfd67e028cd568da98982c' 10 | ), 11 | Collect: new LogEvent<([owner: string, recipient: string, tickLower: number, tickUpper: number, amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {owner: string, recipient: string, tickLower: number, tickUpper: number, amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 12 | abi, '0x70935338e69775456a85ddef226c395fb668b63fa0115f5f20610b388e6ca9c0' 13 | ), 14 | CollectProtocol: new LogEvent<([sender: string, recipient: string, amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {sender: string, recipient: string, amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 15 | abi, '0x596b573906218d3411850b26a6b437d6c4522fdb43d2d2386263f86d50b8b151' 16 | ), 17 | Flash: new LogEvent<([sender: string, recipient: string, amount0: ethers.BigNumber, amount1: ethers.BigNumber, paid0: ethers.BigNumber, paid1: ethers.BigNumber] & {sender: string, recipient: string, amount0: ethers.BigNumber, amount1: ethers.BigNumber, paid0: ethers.BigNumber, paid1: ethers.BigNumber})>( 18 | abi, '0xbdbdb71d7860376ba52b25a5028beea23581364a40522f6bcfb86bb1f2dca633' 19 | ), 20 | IncreaseObservationCardinalityNext: new LogEvent<([observationCardinalityNextOld: number, observationCardinalityNextNew: number] & {observationCardinalityNextOld: number, observationCardinalityNextNew: number})>( 21 | abi, '0xac49e518f90a358f652e4400164f05a5d8f7e35e7747279bc3a93dbf584e125a' 22 | ), 23 | Initialize: new LogEvent<([sqrtPriceX96: ethers.BigNumber, tick: number] & {sqrtPriceX96: ethers.BigNumber, tick: number})>( 24 | abi, '0x98636036cb66a9c19a37435efc1e90142190214e8abeb821bdba3f2990dd4c95' 25 | ), 26 | Mint: new LogEvent<([sender: string, owner: string, tickLower: number, tickUpper: number, amount: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {sender: string, owner: string, tickLower: number, tickUpper: number, amount: ethers.BigNumber, amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 27 | abi, '0x7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde' 28 | ), 29 | SetFeeProtocol: new LogEvent<([feeProtocol0Old: number, feeProtocol1Old: number, feeProtocol0New: number, feeProtocol1New: number] & {feeProtocol0Old: number, feeProtocol1Old: number, feeProtocol0New: number, feeProtocol1New: number})>( 30 | abi, '0x973d8d92bb299f4af6ce49b52a8adb85ae46b9f214c4c4fc06ac77401237b133' 31 | ), 32 | Swap: new LogEvent<([sender: string, recipient: string, amount0: ethers.BigNumber, amount1: ethers.BigNumber, sqrtPriceX96: ethers.BigNumber, liquidity: ethers.BigNumber, tick: number] & {sender: string, recipient: string, amount0: ethers.BigNumber, amount1: ethers.BigNumber, sqrtPriceX96: ethers.BigNumber, liquidity: ethers.BigNumber, tick: number})>( 33 | abi, '0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67' 34 | ), 35 | } 36 | 37 | export const functions = { 38 | burn: new Func<[tickLower: number, tickUpper: number, amount: ethers.BigNumber], {tickLower: number, tickUpper: number, amount: ethers.BigNumber}, ([amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 39 | abi, '0xa34123a7' 40 | ), 41 | collect: new Func<[recipient: string, tickLower: number, tickUpper: number, amount0Requested: ethers.BigNumber, amount1Requested: ethers.BigNumber], {recipient: string, tickLower: number, tickUpper: number, amount0Requested: ethers.BigNumber, amount1Requested: ethers.BigNumber}, ([amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 42 | abi, '0x4f1eb3d8' 43 | ), 44 | collectProtocol: new Func<[recipient: string, amount0Requested: ethers.BigNumber, amount1Requested: ethers.BigNumber], {recipient: string, amount0Requested: ethers.BigNumber, amount1Requested: ethers.BigNumber}, ([amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 45 | abi, '0x85b66729' 46 | ), 47 | factory: new Func<[], {}, string>( 48 | abi, '0xc45a0155' 49 | ), 50 | fee: new Func<[], {}, number>( 51 | abi, '0xddca3f43' 52 | ), 53 | feeGrowthGlobal0X128: new Func<[], {}, ethers.BigNumber>( 54 | abi, '0xf3058399' 55 | ), 56 | feeGrowthGlobal1X128: new Func<[], {}, ethers.BigNumber>( 57 | abi, '0x46141319' 58 | ), 59 | flash: new Func<[recipient: string, amount0: ethers.BigNumber, amount1: ethers.BigNumber, data: string], {recipient: string, amount0: ethers.BigNumber, amount1: ethers.BigNumber, data: string}, []>( 60 | abi, '0x490e6cbc' 61 | ), 62 | increaseObservationCardinalityNext: new Func<[observationCardinalityNext: number], {observationCardinalityNext: number}, []>( 63 | abi, '0x32148f67' 64 | ), 65 | initialize: new Func<[sqrtPriceX96: ethers.BigNumber], {sqrtPriceX96: ethers.BigNumber}, []>( 66 | abi, '0xf637731d' 67 | ), 68 | liquidity: new Func<[], {}, ethers.BigNumber>( 69 | abi, '0x1a686502' 70 | ), 71 | maxLiquidityPerTick: new Func<[], {}, ethers.BigNumber>( 72 | abi, '0x70cf754a' 73 | ), 74 | mint: new Func<[recipient: string, tickLower: number, tickUpper: number, amount: ethers.BigNumber, data: string], {recipient: string, tickLower: number, tickUpper: number, amount: ethers.BigNumber, data: string}, ([amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 75 | abi, '0x3c8a7d8d' 76 | ), 77 | observations: new Func<[index: ethers.BigNumber], {index: ethers.BigNumber}, ([blockTimestamp: number, tickCumulative: ethers.BigNumber, secondsPerLiquidityCumulativeX128: ethers.BigNumber, initialized: boolean] & {blockTimestamp: number, tickCumulative: ethers.BigNumber, secondsPerLiquidityCumulativeX128: ethers.BigNumber, initialized: boolean})>( 78 | abi, '0x252c09d7' 79 | ), 80 | observe: new Func<[secondsAgos: Array], {secondsAgos: Array}, ([tickCumulatives: Array, secondsPerLiquidityCumulativeX128s: Array] & {tickCumulatives: Array, secondsPerLiquidityCumulativeX128s: Array})>( 81 | abi, '0x883bdbfd' 82 | ), 83 | positions: new Func<[key: string], {key: string}, ([_liquidity: ethers.BigNumber, feeGrowthInside0LastX128: ethers.BigNumber, feeGrowthInside1LastX128: ethers.BigNumber, tokensOwed0: ethers.BigNumber, tokensOwed1: ethers.BigNumber] & {_liquidity: ethers.BigNumber, feeGrowthInside0LastX128: ethers.BigNumber, feeGrowthInside1LastX128: ethers.BigNumber, tokensOwed0: ethers.BigNumber, tokensOwed1: ethers.BigNumber})>( 84 | abi, '0x514ea4bf' 85 | ), 86 | protocolFees: new Func<[], {}, ([token0: ethers.BigNumber, token1: ethers.BigNumber] & {token0: ethers.BigNumber, token1: ethers.BigNumber})>( 87 | abi, '0x1ad8b03b' 88 | ), 89 | setFeeProtocol: new Func<[feeProtocol0: number, feeProtocol1: number], {feeProtocol0: number, feeProtocol1: number}, []>( 90 | abi, '0x8206a4d1' 91 | ), 92 | slot0: new Func<[], {}, ([sqrtPriceX96: ethers.BigNumber, tick: number, observationIndex: number, observationCardinality: number, observationCardinalityNext: number, feeProtocol: number, unlocked: boolean] & {sqrtPriceX96: ethers.BigNumber, tick: number, observationIndex: number, observationCardinality: number, observationCardinalityNext: number, feeProtocol: number, unlocked: boolean})>( 93 | abi, '0x3850c7bd' 94 | ), 95 | snapshotCumulativesInside: new Func<[tickLower: number, tickUpper: number], {tickLower: number, tickUpper: number}, ([tickCumulativeInside: ethers.BigNumber, secondsPerLiquidityInsideX128: ethers.BigNumber, secondsInside: number] & {tickCumulativeInside: ethers.BigNumber, secondsPerLiquidityInsideX128: ethers.BigNumber, secondsInside: number})>( 96 | abi, '0xa38807f2' 97 | ), 98 | swap: new Func<[recipient: string, zeroForOne: boolean, amountSpecified: ethers.BigNumber, sqrtPriceLimitX96: ethers.BigNumber, data: string], {recipient: string, zeroForOne: boolean, amountSpecified: ethers.BigNumber, sqrtPriceLimitX96: ethers.BigNumber, data: string}, ([amount0: ethers.BigNumber, amount1: ethers.BigNumber] & {amount0: ethers.BigNumber, amount1: ethers.BigNumber})>( 99 | abi, '0x128acb08' 100 | ), 101 | tickBitmap: new Func<[wordPosition: number], {wordPosition: number}, ethers.BigNumber>( 102 | abi, '0x5339c296' 103 | ), 104 | tickSpacing: new Func<[], {}, number>( 105 | abi, '0xd0c93a7c' 106 | ), 107 | ticks: new Func<[tick: number], {tick: number}, ([liquidityGross: ethers.BigNumber, liquidityNet: ethers.BigNumber, feeGrowthOutside0X128: ethers.BigNumber, feeGrowthOutside1X128: ethers.BigNumber, tickCumulativeOutside: ethers.BigNumber, secondsPerLiquidityOutsideX128: ethers.BigNumber, secondsOutside: number, initialized: boolean] & {liquidityGross: ethers.BigNumber, liquidityNet: ethers.BigNumber, feeGrowthOutside0X128: ethers.BigNumber, feeGrowthOutside1X128: ethers.BigNumber, tickCumulativeOutside: ethers.BigNumber, secondsPerLiquidityOutsideX128: ethers.BigNumber, secondsOutside: number, initialized: boolean})>( 108 | abi, '0xf30dba93' 109 | ), 110 | token0: new Func<[], {}, string>( 111 | abi, '0x0dfe1681' 112 | ), 113 | token1: new Func<[], {}, string>( 114 | abi, '0xd21220a7' 115 | ), 116 | } 117 | 118 | export class Contract extends ContractBase { 119 | 120 | factory(): Promise { 121 | return this.eth_call(functions.factory, []) 122 | } 123 | 124 | fee(): Promise { 125 | return this.eth_call(functions.fee, []) 126 | } 127 | 128 | feeGrowthGlobal0X128(): Promise { 129 | return this.eth_call(functions.feeGrowthGlobal0X128, []) 130 | } 131 | 132 | feeGrowthGlobal1X128(): Promise { 133 | return this.eth_call(functions.feeGrowthGlobal1X128, []) 134 | } 135 | 136 | liquidity(): Promise { 137 | return this.eth_call(functions.liquidity, []) 138 | } 139 | 140 | maxLiquidityPerTick(): Promise { 141 | return this.eth_call(functions.maxLiquidityPerTick, []) 142 | } 143 | 144 | observations(index: ethers.BigNumber): Promise<([blockTimestamp: number, tickCumulative: ethers.BigNumber, secondsPerLiquidityCumulativeX128: ethers.BigNumber, initialized: boolean] & {blockTimestamp: number, tickCumulative: ethers.BigNumber, secondsPerLiquidityCumulativeX128: ethers.BigNumber, initialized: boolean})> { 145 | return this.eth_call(functions.observations, [index]) 146 | } 147 | 148 | observe(secondsAgos: Array): Promise<([tickCumulatives: Array, secondsPerLiquidityCumulativeX128s: Array] & {tickCumulatives: Array, secondsPerLiquidityCumulativeX128s: Array})> { 149 | return this.eth_call(functions.observe, [secondsAgos]) 150 | } 151 | 152 | positions(key: string): Promise<([_liquidity: ethers.BigNumber, feeGrowthInside0LastX128: ethers.BigNumber, feeGrowthInside1LastX128: ethers.BigNumber, tokensOwed0: ethers.BigNumber, tokensOwed1: ethers.BigNumber] & {_liquidity: ethers.BigNumber, feeGrowthInside0LastX128: ethers.BigNumber, feeGrowthInside1LastX128: ethers.BigNumber, tokensOwed0: ethers.BigNumber, tokensOwed1: ethers.BigNumber})> { 153 | return this.eth_call(functions.positions, [key]) 154 | } 155 | 156 | protocolFees(): Promise<([token0: ethers.BigNumber, token1: ethers.BigNumber] & {token0: ethers.BigNumber, token1: ethers.BigNumber})> { 157 | return this.eth_call(functions.protocolFees, []) 158 | } 159 | 160 | slot0(): Promise<([sqrtPriceX96: ethers.BigNumber, tick: number, observationIndex: number, observationCardinality: number, observationCardinalityNext: number, feeProtocol: number, unlocked: boolean] & {sqrtPriceX96: ethers.BigNumber, tick: number, observationIndex: number, observationCardinality: number, observationCardinalityNext: number, feeProtocol: number, unlocked: boolean})> { 161 | return this.eth_call(functions.slot0, []) 162 | } 163 | 164 | snapshotCumulativesInside(tickLower: number, tickUpper: number): Promise<([tickCumulativeInside: ethers.BigNumber, secondsPerLiquidityInsideX128: ethers.BigNumber, secondsInside: number] & {tickCumulativeInside: ethers.BigNumber, secondsPerLiquidityInsideX128: ethers.BigNumber, secondsInside: number})> { 165 | return this.eth_call(functions.snapshotCumulativesInside, [tickLower, tickUpper]) 166 | } 167 | 168 | tickBitmap(wordPosition: number): Promise { 169 | return this.eth_call(functions.tickBitmap, [wordPosition]) 170 | } 171 | 172 | tickSpacing(): Promise { 173 | return this.eth_call(functions.tickSpacing, []) 174 | } 175 | 176 | ticks(tick: number): Promise<([liquidityGross: ethers.BigNumber, liquidityNet: ethers.BigNumber, feeGrowthOutside0X128: ethers.BigNumber, feeGrowthOutside1X128: ethers.BigNumber, tickCumulativeOutside: ethers.BigNumber, secondsPerLiquidityOutsideX128: ethers.BigNumber, secondsOutside: number, initialized: boolean] & {liquidityGross: ethers.BigNumber, liquidityNet: ethers.BigNumber, feeGrowthOutside0X128: ethers.BigNumber, feeGrowthOutside1X128: ethers.BigNumber, tickCumulativeOutside: ethers.BigNumber, secondsPerLiquidityOutsideX128: ethers.BigNumber, secondsOutside: number, initialized: boolean})> { 177 | return this.eth_call(functions.ticks, [tick]) 178 | } 179 | 180 | token0(): Promise { 181 | return this.eth_call(functions.token0, []) 182 | } 183 | 184 | token1(): Promise { 185 | return this.eth_call(functions.token1, []) 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /uniswap-squid/src/mappings/positionManager.ts: -------------------------------------------------------------------------------- 1 | import {BigDecimal} from '@subsquid/big-decimal' 2 | import { 3 | BatchBlock, 4 | BlockHandlerContext, 5 | CommonHandlerContext, 6 | EvmBlock, 7 | LogHandlerContext, 8 | } from '@subsquid/evm-processor' 9 | import {LogItem, TransactionItem} from '@subsquid/evm-processor/lib/interfaces/dataSelection' 10 | import {BigNumber} from 'ethers' 11 | import {Multicall} from "../abi/multicall" 12 | import {Position, PositionSnapshot, Token} from '../model' 13 | import {BlockMap} from '../utils/blockMap' 14 | import {ADDRESS_ZERO, FACTORY_ADDRESS, MULTICALL_ADDRESS, POSITIONS_ADDRESS} from '../utils/constants' 15 | import {EntityManager} from '../utils/entityManager' 16 | import {last, processItem} from '../utils/tools' 17 | import * as factoryAbi from './../abi/factory' 18 | import * as positionsAbi from './../abi/NonfungiblePositionManager' 19 | 20 | type EventData = 21 | | (TransferData & {type: 'Transfer'}) 22 | | (IncreaseData & {type: 'Increase'}) 23 | | (DecreaseData & {type: 'Decrease'}) 24 | | (CollectData & {type: 'Collect'}) 25 | 26 | type ContextWithEntityManager = CommonHandlerContext & {entities: EntityManager} 27 | 28 | export async function processPositions(ctx: ContextWithEntityManager, blocks: BatchBlock[]): Promise { 29 | const eventsData = processItems(ctx, blocks) 30 | if (eventsData.size == 0) return 31 | 32 | await prefetch(ctx, eventsData, last(blocks).header) 33 | 34 | for (const [block, blockEventsData] of eventsData) { 35 | for (const data of blockEventsData) { 36 | switch (data.type) { 37 | case 'Increase': 38 | await processIncreaseData(ctx, block, data) 39 | break 40 | case 'Decrease': 41 | await processDecreaseData(ctx, block, data) 42 | break 43 | case 'Collect': 44 | await processCollectData(ctx, block, data) 45 | break 46 | case 'Transfer': 47 | await processTransferData(ctx, block, data) 48 | break 49 | } 50 | } 51 | } 52 | 53 | // await updateFeeVars(createContext(last(blocks).header), ctx.entities.values(Position)) 54 | } 55 | 56 | async function prefetch(ctx: ContextWithEntityManager, eventsData: BlockMap, block: EvmBlock) { 57 | const positionIds = new Set() 58 | for (const [, blockEventsData] of eventsData) { 59 | for (const data of blockEventsData) { 60 | ctx.entities.defer(Position, data.tokenId) 61 | positionIds.add(data.tokenId) 62 | } 63 | } 64 | 65 | await ctx.entities.load(Position) 66 | 67 | const newPositionIds: string[] = [] 68 | for (const id of positionIds) { 69 | if (!ctx.entities.get(Position, id, false)) newPositionIds.push(id) 70 | } 71 | 72 | const newPositions = await initPositions({...ctx, block}, newPositionIds) 73 | for (const position of newPositions) { 74 | ctx.entities.add(position) 75 | } 76 | 77 | for (const position of ctx.entities.values(Position)) { 78 | ctx.entities.defer(Token, position.token0Id, position.token1Id) 79 | } 80 | 81 | await ctx.entities.load(Token) 82 | } 83 | 84 | function processItems(ctx: CommonHandlerContext, blocks: BatchBlock[]) { 85 | let eventsData = new BlockMap() 86 | 87 | processItem(blocks, (block, item) => { 88 | if (item.kind !== 'evmLog') return 89 | switch (item.evmLog.topics[0]) { 90 | case positionsAbi.events.IncreaseLiquidity.topic: { 91 | const data = processInreaseLiquidity({...ctx, block, ...item}) 92 | eventsData.push(block, { 93 | type: 'Increase', 94 | ...data, 95 | }) 96 | return 97 | } 98 | case positionsAbi.events.DecreaseLiquidity.topic: { 99 | const data = processDecreaseLiquidity({...ctx, block, ...item}) 100 | eventsData.push(block, { 101 | type: 'Decrease', 102 | ...data, 103 | }) 104 | return 105 | } 106 | case positionsAbi.events.Collect.topic: { 107 | const data = processCollect({...ctx, block, ...item}) 108 | eventsData.push(block, { 109 | type: 'Collect', 110 | ...data, 111 | }) 112 | return 113 | } 114 | case positionsAbi.events.Transfer.topic: { 115 | const data = processTransafer({...ctx, block, ...item}) 116 | eventsData.push(block, { 117 | type: 'Transfer', 118 | ...data, 119 | }) 120 | return 121 | } 122 | } 123 | }) 124 | 125 | return eventsData 126 | } 127 | 128 | async function processIncreaseData(ctx: ContextWithEntityManager, block: EvmBlock, data: IncreaseData) { 129 | let position = ctx.entities.get(Position, data.tokenId, false) 130 | if (position == null) return 131 | 132 | let token0 = await ctx.entities.get(Token, position.token0Id) 133 | let token1 = await ctx.entities.get(Token, position.token1Id) 134 | 135 | if (!token0 || !token1) return 136 | 137 | let amount0 = BigDecimal(data.amount0, token0.decimals).toNumber() 138 | let amount1 = BigDecimal(data.amount1, token1.decimals).toNumber() 139 | 140 | position.liquidity = position.liquidity + data.liquidity 141 | position.depositedToken0 = position.depositedToken0 + amount0 142 | position.depositedToken1 = position.depositedToken1 + amount1 143 | 144 | updatePositionSnapshot(ctx, block, position.id) 145 | } 146 | 147 | async function processDecreaseData(ctx: ContextWithEntityManager, block: EvmBlock, data: DecreaseData) { 148 | // temp fix 149 | if (block.height == 14317993) return 150 | 151 | let position = ctx.entities.get(Position, data.tokenId, false) 152 | if (position == null) return 153 | 154 | let token0 = await ctx.entities.get(Token, position.token0Id) 155 | let token1 = await ctx.entities.get(Token, position.token1Id) 156 | 157 | if (!token0 || !token1) return 158 | 159 | let amount0 = BigDecimal(data.amount0, token0.decimals).toNumber() 160 | let amount1 = BigDecimal(data.amount1, token1.decimals).toNumber() 161 | 162 | position.liquidity = position.liquidity - data.liquidity 163 | position.withdrawnToken0 = position.depositedToken0 + amount0 164 | position.withdrawnToken1 = position.depositedToken1 + amount1 165 | 166 | updatePositionSnapshot(ctx, block, position.id) 167 | } 168 | 169 | async function processCollectData(ctx: ContextWithEntityManager, block: EvmBlock, data: CollectData) { 170 | let position = ctx.entities.get(Position, data.tokenId, false) 171 | // position was not able to be fetched 172 | if (position == null) return 173 | 174 | let token0 = ctx.entities.getOrFail(Token, position.token0Id, false) 175 | let amount0 = BigDecimal(data.amount0, token0.decimals).toNumber() 176 | 177 | position.collectedFeesToken0 = position.collectedFeesToken0 + amount0 178 | position.collectedFeesToken1 = position.collectedFeesToken1 + amount0 179 | 180 | updatePositionSnapshot(ctx, block, position.id) 181 | } 182 | 183 | async function processTransferData(ctx: ContextWithEntityManager, block: EvmBlock, data: TransferData) { 184 | let position = ctx.entities.get(Position, data.tokenId, false) 185 | 186 | // position was not able to be fetched 187 | if (position == null) return 188 | 189 | position.owner = data.to 190 | 191 | updatePositionSnapshot(ctx, block, position.id) 192 | } 193 | 194 | async function updatePositionSnapshot(ctx: ContextWithEntityManager, block: EvmBlock, positionId: string) { 195 | const position = ctx.entities.getOrFail(Position, positionId, false) 196 | 197 | const positionBlockId = snapshotId(positionId, block.height) 198 | 199 | let positionSnapshot = ctx.entities.get(PositionSnapshot, positionBlockId, false) 200 | if (!positionSnapshot) { 201 | positionSnapshot = new PositionSnapshot({id: positionBlockId}) 202 | ctx.entities.add(positionSnapshot) 203 | } 204 | positionSnapshot.owner = position.owner 205 | positionSnapshot.pool = position.pool 206 | positionSnapshot.positionId = positionId 207 | positionSnapshot.blockNumber = block.height 208 | positionSnapshot.timestamp = new Date(block.timestamp) 209 | positionSnapshot.liquidity = position.liquidity 210 | positionSnapshot.depositedToken0 = position.depositedToken0 211 | positionSnapshot.depositedToken1 = position.depositedToken1 212 | positionSnapshot.withdrawnToken0 = position.withdrawnToken0 213 | positionSnapshot.withdrawnToken1 = position.withdrawnToken1 214 | positionSnapshot.collectedFeesToken0 = position.collectedFeesToken0 215 | positionSnapshot.collectedFeesToken1 = position.collectedFeesToken1 216 | return 217 | } 218 | 219 | function createPosition(positionId: string) { 220 | const position = new Position({id: positionId}) 221 | 222 | position.owner = ADDRESS_ZERO 223 | position.liquidity = 0n 224 | position.depositedToken0 = 0 225 | position.depositedToken1 = 0 226 | position.withdrawnToken0 = 0 227 | position.withdrawnToken1 = 0 228 | position.collectedFeesToken0 = 0 229 | position.collectedFeesToken1 = 0 230 | position.feeGrowthInside0LastX128 = 0n 231 | position.feeGrowthInside1LastX128 = 0n 232 | 233 | return position 234 | } 235 | 236 | async function initPositions(ctx: BlockHandlerContext, ids: string[]) { 237 | const multicall = new Multicall(ctx, MULTICALL_ADDRESS) 238 | 239 | const positionResults = await multicall.tryAggregate( 240 | positionsAbi.functions.positions, 241 | POSITIONS_ADDRESS, 242 | ids.map(id => { 243 | return [BigNumber.from(id)] 244 | }), 245 | 500 246 | ) 247 | 248 | const positionsData: {positionId: string; token0Id: string; token1Id: string; fee: number}[] = [] 249 | for (let i = 0; i < ids.length; i++) { 250 | const result = positionResults[i] 251 | if (result.success) { 252 | positionsData.push({ 253 | positionId: ids[i].toLowerCase(), 254 | token0Id: result.value.token0.toLowerCase(), 255 | token1Id: result.value.token1.toLowerCase(), 256 | fee: result.value.fee, 257 | }) 258 | } 259 | } 260 | 261 | const poolIds = await multicall.aggregate(factoryAbi.functions.getPool, FACTORY_ADDRESS, positionsData.map(p => { 262 | return [p.token0Id, p.token1Id, p.fee] 263 | }), 500) 264 | 265 | const positions: Position[] = [] 266 | for (let i = 0; i < positionsData.length; i++) { 267 | const position = createPosition(positionsData[i].positionId) 268 | position.token0Id = positionsData[i].token0Id 269 | position.token1Id = positionsData[i].token1Id 270 | position.poolId = poolIds[i].toLowerCase() 271 | 272 | // temp fix 273 | if (position.poolId === '0x8fe8d9bb8eeba3ed688069c3d6b556c9ca258248') continue 274 | 275 | positions.push(position) 276 | } 277 | 278 | return positions 279 | } 280 | 281 | async function updateFeeVars(ctx: BlockHandlerContext, positions: Position[]) { 282 | const multicall = new Multicall(ctx, MULTICALL_ADDRESS) 283 | 284 | const positionResult = await multicall.tryAggregate( 285 | positionsAbi.functions.positions, 286 | POSITIONS_ADDRESS, 287 | positions.map(p => { 288 | return [BigNumber.from(p.id)] 289 | }) 290 | ) 291 | 292 | for (let i = 0; i < positions.length; i++) { 293 | const result = positionResult[i] 294 | if (result.success) { 295 | positions[i].feeGrowthInside0LastX128 = result.value.feeGrowthInside0LastX128.toBigInt() 296 | positions[i].feeGrowthInside1LastX128 = result.value.feeGrowthInside1LastX128.toBigInt() 297 | } 298 | } 299 | } 300 | 301 | function snapshotId(positionId: string, block: number) { 302 | return `${positionId}#${block}` 303 | } 304 | 305 | interface IncreaseData { 306 | tokenId: string 307 | amount0: bigint 308 | amount1: bigint 309 | liquidity: bigint 310 | } 311 | 312 | function processInreaseLiquidity(ctx: LogHandlerContext): IncreaseData { 313 | const event = positionsAbi.events.IncreaseLiquidity.decode(ctx.evmLog) 314 | 315 | return { 316 | tokenId: event.tokenId.toString(), 317 | amount0: event.amount0.toBigInt(), 318 | amount1: event.amount1.toBigInt(), 319 | liquidity: event.liquidity.toBigInt(), 320 | } 321 | } 322 | 323 | interface DecreaseData { 324 | tokenId: string 325 | amount0: bigint 326 | amount1: bigint 327 | liquidity: bigint 328 | } 329 | 330 | function processDecreaseLiquidity(ctx: LogHandlerContext): DecreaseData { 331 | const event = positionsAbi.events.DecreaseLiquidity.decode(ctx.evmLog) 332 | 333 | return { 334 | tokenId: event.tokenId.toString(), 335 | amount0: event.amount0.toBigInt(), 336 | amount1: event.amount1.toBigInt(), 337 | liquidity: event.liquidity.toBigInt(), 338 | } 339 | } 340 | 341 | interface CollectData { 342 | tokenId: string 343 | amount0: bigint 344 | amount1: bigint 345 | } 346 | 347 | function processCollect(ctx: LogHandlerContext): CollectData { 348 | const event = positionsAbi.events.Collect.decode(ctx.evmLog) 349 | 350 | return { 351 | tokenId: event.tokenId.toString(), 352 | amount0: event.amount0.toBigInt(), 353 | amount1: event.amount1.toBigInt(), 354 | } 355 | } 356 | 357 | interface TransferData { 358 | tokenId: string 359 | to: string 360 | } 361 | 362 | function processTransafer(ctx: LogHandlerContext): TransferData { 363 | const event = positionsAbi.events.Transfer.decode(ctx.evmLog) 364 | 365 | return { 366 | tokenId: event.tokenId.toString(), 367 | to: event.to.toLowerCase(), 368 | } 369 | } 370 | 371 | 372 | type Item = 373 | | LogItem<{ 374 | evmLog: { 375 | topics: true 376 | data: true 377 | } 378 | }> 379 | | TransactionItem 380 | -------------------------------------------------------------------------------- /uniswap-squid/schema.graphql: -------------------------------------------------------------------------------- 1 | type Factory @entity { 2 | # factory address 3 | id: ID! 4 | # amount of pools created 5 | poolCount: Int! 6 | # amoutn of transactions all time 7 | txCount: Int! 8 | # total volume all time in derived USD 9 | totalVolumeUSD: Float! 10 | # total volume all time in derived ETH 11 | totalVolumeETH: Float! 12 | # total swap fees all time in USD 13 | totalFeesUSD: Float! 14 | # total swap fees all time in USD 15 | totalFeesETH: Float! 16 | # all volume even through less reliable USD values 17 | untrackedVolumeUSD: Float! 18 | # TVL derived in USD 19 | totalValueLockedUSD: Float! 20 | # TVL derived in ETH 21 | totalValueLockedETH: Float! 22 | # TVL derived in USD untracked 23 | totalValueLockedUSDUntracked: Float! 24 | # TVL derived in ETH untracked 25 | totalValueLockedETHUntracked: Float! 26 | 27 | # current owner of the factory 28 | owner: ID! 29 | } 30 | 31 | # stores for USD calculations 32 | type Bundle @entity { 33 | id: ID! 34 | # price of ETH in usd 35 | ethPriceUSD: Float! 36 | } 37 | 38 | type Token @entity { 39 | # token address 40 | id: ID! 41 | # token symbol 42 | symbol: String! 43 | # token name 44 | name: String! 45 | # token decimals 46 | decimals: Int! 47 | # token total supply 48 | totalSupply: BigInt! 49 | # volume in token units 50 | volume: Float! 51 | # volume in derived USD 52 | volumeUSD: Float! 53 | # volume in USD even on pools with less reliable USD values 54 | untrackedVolumeUSD: Float! 55 | # fees in USD 56 | feesUSD: Float! 57 | # transactions across all pools that include this token 58 | txCount: Int! 59 | # number of pools containing this token 60 | poolCount: BigInt! 61 | # liquidity across all pools in token units 62 | totalValueLocked: Float! 63 | # liquidity across all pools in derived USD 64 | totalValueLockedUSD: Float! 65 | # TVL derived in USD untracked 66 | totalValueLockedUSDUntracked: Float! 67 | # derived price in ETH 68 | derivedETH: Float! 69 | # pools token is in that are white listed for USD pricing 70 | whitelistPools: [ID!]! 71 | # derived fields 72 | tokenDayData: [TokenDayData!]! @derivedFrom(field: "token") 73 | } 74 | 75 | type Pool @entity { 76 | # pool address 77 | id: ID! 78 | # creation 79 | createdAtTimestamp: DateTime! 80 | # block pool was created at 81 | createdAtBlockNumber: Int! 82 | # token0 83 | token0Id: ID! 84 | token0: Token! 85 | # token1 86 | token1Id: ID! 87 | token1: Token! 88 | # fee amount 89 | feeTier: Int! 90 | # in range liquidity 91 | liquidity: BigInt! 92 | # current price tracker 93 | sqrtPrice: BigInt! 94 | # tracker for global fee growth 95 | feeGrowthGlobal0X128: BigInt! 96 | # tracker for global fee growth 97 | feeGrowthGlobal1X128: BigInt! 98 | # token0 per token1 99 | token0Price: Float! 100 | # token1 per token0 101 | token1Price: Float! 102 | # current tick 103 | tick: Int 104 | # current observation index 105 | observationIndex: BigInt! 106 | # all time token0 swapped 107 | volumeToken0: Float! 108 | # all time token1 swapped 109 | volumeToken1: Float! 110 | # all time USD swapped 111 | volumeUSD: Float! 112 | # all time USD swapped, unfiltered for unreliable USD pools 113 | untrackedVolumeUSD: Float! 114 | # fees in USD 115 | feesUSD: Float! 116 | # all time number of transactions 117 | txCount: Int! 118 | # all time fees collected token0 119 | collectedFeesToken0: Float! 120 | # all time fees collected token1 121 | collectedFeesToken1: Float! 122 | # all time fees collected derived USD 123 | collectedFeesUSD: Float! 124 | # total token 0 across all ticks 125 | totalValueLockedToken0: Float! 126 | # total token 1 across all ticks 127 | totalValueLockedToken1: Float! 128 | # tvl derived ETH 129 | totalValueLockedETH: Float! 130 | # tvl USD 131 | totalValueLockedUSD: Float! 132 | # TVL derived in USD untracked 133 | totalValueLockedUSDUntracked: Float! 134 | # Fields used to help derived relationship 135 | liquidityProviderCount: BigInt! # used to detect new exchanges 136 | # hourly snapshots of pool data 137 | poolHourData: [PoolHourData!]! @derivedFrom(field: "pool") 138 | # daily snapshots of pool data 139 | poolDayData: [PoolDayData!]! @derivedFrom(field: "pool") 140 | # derived fields 141 | mints: [Mint!]! @derivedFrom(field: "pool") 142 | burns: [Burn!]! @derivedFrom(field: "pool") 143 | swaps: [Swap!]! @derivedFrom(field: "pool") 144 | collects: [Collect!]! @derivedFrom(field: "pool") 145 | ticks: [Tick!]! @derivedFrom(field: "pool") 146 | } 147 | 148 | type Tick @entity { 149 | # format: # 150 | id: ID! 151 | # pool address 152 | poolAddress: String 153 | # tick index 154 | tickIdx: BigInt! 155 | # pointer to pool 156 | poolId: ID! 157 | pool: Pool! 158 | # total liquidity pool has as tick lower or upper 159 | liquidityGross: BigInt! 160 | # how much liquidity changes when tick crossed 161 | liquidityNet: BigInt! 162 | # calculated price of token0 of tick within this pool - constant 163 | price0: Float! 164 | # calculated price of token1 of tick within this pool - constant 165 | price1: Float! 166 | # lifetime volume of token0 with this tick in range 167 | volumeToken0: Float! 168 | # lifetime volume of token1 with this tick in range 169 | volumeToken1: Float! 170 | # lifetime volume in derived USD with this tick in range 171 | volumeUSD: Float! 172 | # lifetime volume in untracked USD with this tick in range 173 | untrackedVolumeUSD: Float! 174 | # fees in USD 175 | feesUSD: Float! 176 | # all time collected fees in token0 177 | collectedFeesToken0: Float! 178 | # all time collected fees in token1 179 | collectedFeesToken1: Float! 180 | # all time collected fees in USD 181 | collectedFeesUSD: Float! 182 | # created time 183 | createdAtTimestamp: DateTime! 184 | # created block 185 | createdAtBlockNumber: Int! 186 | # Fields used to help derived relationship 187 | liquidityProviderCount: BigInt! # used to detect new exchanges 188 | # derived fields 189 | # swaps: [Swap!]! @derivedFrom(field: "tick") 190 | # vars needed for fee computation 191 | feeGrowthOutside0X128: BigInt! 192 | feeGrowthOutside1X128: BigInt! 193 | } 194 | 195 | type Position @entity { 196 | # Positions created through NonfungiblePositionManager 197 | # NFT token id 198 | id: ID! 199 | # owner of the NFT 200 | owner: String! 201 | # pool position is within 202 | poolId: ID! 203 | pool: Pool! 204 | # allow indexing by tokens 205 | token0Id: ID! 206 | token0: Token! 207 | # allow indexing by tokens 208 | token1Id: ID! 209 | token1: Token! 210 | # total position liquidity 211 | liquidity: BigInt! 212 | # amount of token 0 ever deposited to position 213 | depositedToken0: Float! 214 | # amount of token 1 ever deposited to position 215 | depositedToken1: Float! 216 | # amount of token 0 ever withdrawn from position (without fees) 217 | withdrawnToken0: Float! 218 | # amount of token 1 ever withdrawn from position (without fees) 219 | withdrawnToken1: Float! 220 | # all time collected fees in token0 221 | collectedFeesToken0: Float! 222 | # all time collected fees in token1 223 | collectedFeesToken1: Float! 224 | # vars needed for fee computation 225 | feeGrowthInside0LastX128: BigInt! 226 | feeGrowthInside1LastX128: BigInt! 227 | } 228 | 229 | type PositionSnapshot @entity { 230 | # # 231 | id: ID! 232 | # owner of the NFT 233 | owner: String! 234 | # pool the position is within 235 | poolId: ID! 236 | pool: Pool! 237 | # position of which the snap was taken of 238 | positionId: ID! 239 | position: Position! 240 | # block in which the snap was created 241 | blockNumber: Int! 242 | # timestamp of block in which the snap was created 243 | timestamp: DateTime! 244 | # total position liquidity 245 | liquidity: BigInt! 246 | # amount of token 0 ever deposited to position 247 | depositedToken0: Float! 248 | # amount of token 1 ever deposited to position 249 | depositedToken1: Float! 250 | # amount of token 0 ever withdrawn from position (without fees) 251 | withdrawnToken0: Float! 252 | # amount of token 1 ever withdrawn from position (without fees) 253 | withdrawnToken1: Float! 254 | # all time collected fees in token0 255 | collectedFeesToken0: Float! 256 | # all time collected fees in token1 257 | collectedFeesToken1: Float! 258 | # tx in which the snapshot was initialized 259 | transactionId: ID! 260 | transaction: Transaction! 261 | # internal vars needed for fee computation 262 | feeGrowthInside0LastX128: BigInt! 263 | feeGrowthInside1LastX128: BigInt! 264 | } 265 | 266 | type Transaction @entity { 267 | # txn hash 268 | id: ID! 269 | # block txn was included in 270 | blockNumber: Int! 271 | # timestamp txn was confirmed 272 | timestamp: DateTime! 273 | # gas used during txn execution 274 | gasUsed: BigInt! 275 | gasPrice: BigInt! 276 | # derived values 277 | mints: [Mint]! @derivedFrom(field: "transaction") 278 | burns: [Burn]! @derivedFrom(field: "transaction") 279 | swaps: [Swap]! @derivedFrom(field: "transaction") 280 | flashed: [Flash]! @derivedFrom(field: "transaction") 281 | collects: [Collect]! @derivedFrom(field: "transaction") 282 | } 283 | 284 | type Mint @entity { 285 | # transaction hash + "#" + index in mints Transaction array 286 | id: ID! 287 | # which txn the mint was included in 288 | transactionId: ID! 289 | transaction: Transaction! 290 | # time of txn 291 | timestamp: DateTime! 292 | # pool position is within 293 | poolId: ID! 294 | pool: Pool! 295 | # allow indexing by tokens 296 | token0Id: ID! 297 | token0: Token! 298 | # allow indexing by tokens 299 | token1Id: ID! 300 | token1: Token! 301 | # owner of position where liquidity minted to 302 | owner: String! 303 | # the address that minted the liquidity 304 | sender: String 305 | # txn origin 306 | origin: String! # the EOA that initiated the txn 307 | # amount of liquidity minted 308 | amount: BigInt! 309 | # amount of token 0 minted 310 | amount0: Float! 311 | # amount of token 1 minted 312 | amount1: Float! 313 | # derived amount based on available prices of tokens 314 | amountUSD: Float 315 | # lower tick of the position 316 | tickLower: Int! 317 | # upper tick of the position 318 | tickUpper: Int! 319 | # order within the txn 320 | logIndex: Int 321 | } 322 | 323 | type Burn @entity { 324 | # transaction hash + "#" + index in mints Transaction array 325 | id: ID! 326 | # txn burn was included in 327 | transactionId: ID! 328 | transaction: Transaction! 329 | # pool position is within 330 | poolId: ID! 331 | pool: Pool! 332 | # allow indexing by tokens 333 | token0Id: ID! 334 | token0: Token! 335 | # allow indexing by tokens 336 | token1Id: ID! 337 | token1: Token! 338 | # need this to pull recent txns for specific token or pool 339 | timestamp: DateTime! 340 | # owner of position where liquidity was burned 341 | owner: String 342 | # txn origin 343 | origin: String! # the EOA that initiated the txn 344 | # amouny of liquidity burned 345 | amount: BigInt! 346 | # amount of token 0 burned 347 | amount0: Float! 348 | # amount of token 1 burned 349 | amount1: Float! 350 | # derived amount based on available prices of tokens 351 | amountUSD: Float 352 | # lower tick of position 353 | tickLower: Int! 354 | # upper tick of position 355 | tickUpper: Int! 356 | # position within the transactions 357 | logIndex: Int 358 | } 359 | 360 | type Swap @entity { 361 | # transaction hash + "#" + index in swaps Transaction array 362 | id: ID! 363 | # pointer to transaction 364 | transaction: Transaction! 365 | transactionId: ID! 366 | # timestamp of transaction 367 | timestamp: DateTime! 368 | # pool swap occured within 369 | pool: Pool! 370 | poolId: ID! 371 | # allow indexing by tokens 372 | token0Id: ID! 373 | token0: Token! 374 | # allow indexing by tokens 375 | token1Id: ID! 376 | token1: Token! 377 | # sender of the swap 378 | sender: String! 379 | # recipient of the swap 380 | recipient: String! 381 | # txn origin 382 | origin: String! # the EOA that initiated the txn 383 | # delta of token0 swapped 384 | amount0: Float! 385 | # delta of token1 swapped 386 | amount1: Float! 387 | # derived info 388 | amountUSD: Float! 389 | # The sqrt(price) of the pool after the swap, as a Q64.96 390 | sqrtPriceX96: BigInt! 391 | # the tick after the swap 392 | tick: Int! 393 | # index within the txn 394 | logIndex: Int 395 | } 396 | 397 | type Collect @entity { 398 | # transaction hash + "#" + index in collect Transaction array 399 | id: ID! 400 | # pointer to txn 401 | transactionId: ID! 402 | transaction: Transaction! 403 | # timestamp of event 404 | timestamp: DateTime! 405 | # pool collect occured within 406 | poolId: ID! 407 | pool: Pool! 408 | # owner of position collect was performed on 409 | owner: String 410 | # amount of token0 collected 411 | amount0: Float! 412 | # amount of token1 collected 413 | amount1: Float! 414 | # derived amount based on available prices of tokens 415 | amountUSD: Float 416 | # lower tick of position 417 | tickLower: Int! 418 | # uppper tick of position 419 | tickUpper: Int! 420 | # index within the txn 421 | logIndex: Int 422 | } 423 | 424 | type Flash @entity { 425 | # transaction hash + "-" + index in collect Transaction array 426 | id: ID! 427 | # pointer to txn 428 | transaction: Transaction! 429 | transactionId: ID! 430 | # timestamp of event 431 | timestamp: DateTime! 432 | # pool collect occured within 433 | poolId: ID! 434 | pool: Pool! 435 | # sender of the flash 436 | sender: String! 437 | # recipient of the flash 438 | recipient: String! 439 | # amount of token0 flashed 440 | amount0: Float! 441 | # amount of token1 flashed 442 | amount1: Float! 443 | # derived amount based on available prices of tokens 444 | amountUSD: Float! 445 | # amount token0 paid for flash 446 | amount0Paid: Float! 447 | # amount token1 paid for flash 448 | amount1Paid: Float! 449 | # index within the txn 450 | logIndex: Int 451 | } 452 | 453 | # Data accumulated and condensed into day stats for all of Uniswap 454 | type UniswapDayData @entity { 455 | # timestamp rounded to current day by dividing by 86400 456 | id: ID! 457 | # timestamp rounded to current day by dividing by 86400 458 | date: DateTime! 459 | # total daily volume in Uniswap derived in terms of ETH 460 | volumeETH: Float! 461 | # total daily volume in Uniswap derived in terms of USD 462 | volumeUSD: Float! 463 | # total daily volume in Uniswap derived in terms of USD untracked 464 | volumeUSDUntracked: Float! 465 | # fees in USD 466 | feesUSD: Float! 467 | # number of daily transactions 468 | txCount: Int! 469 | # tvl in terms of USD 470 | tvlUSD: Float! 471 | } 472 | 473 | # Data accumulated and condensed into day stats for each pool 474 | type PoolDayData @entity { 475 | # timestamp rounded to current day by dividing by 86400 476 | id: ID! 477 | # timestamp rounded to current day by dividing by 86400 478 | date: DateTime! 479 | # pointer to pool 480 | poolId: ID! 481 | pool: Pool! 482 | # in range liquidity at end of period 483 | liquidity: BigInt! 484 | # current price tracker at end of period 485 | sqrtPrice: BigInt! 486 | # price of token0 - derived from sqrtPrice 487 | token0Price: Float! 488 | # price of token1 - derived from sqrtPrice 489 | token1Price: Float! 490 | # current tick at end of period 491 | tick: Int 492 | # tracker for global fee growth 493 | feeGrowthGlobal0X128: BigInt! 494 | # tracker for global fee growth 495 | feeGrowthGlobal1X128: BigInt! 496 | # tvl derived in USD at end of period 497 | tvlUSD: Float! 498 | # volume in token0 499 | volumeToken0: Float! 500 | # volume in token1 501 | volumeToken1: Float! 502 | # volume in USD 503 | volumeUSD: Float! 504 | # fees in USD 505 | feesUSD: Float! 506 | # numebr of transactions during period 507 | txCount: Int! 508 | # opening price of token0 509 | open: Float! 510 | # high price of token0 511 | high: Float! 512 | # low price of token0 513 | low: Float! 514 | # close price of token0 515 | close: Float! 516 | } 517 | 518 | # hourly stats tracker for pool 519 | type PoolHourData @entity { 520 | # format: - 521 | id: ID! 522 | # unix timestamp for start of hour 523 | date: DateTime! 524 | # pointer to pool 525 | poolId: ID! 526 | pool: Pool! 527 | # in range liquidity at end of period 528 | liquidity: BigInt! 529 | # current price tracker at end of period 530 | sqrtPrice: BigInt! 531 | # price of token0 - derived from sqrtPrice 532 | token0Price: Float! 533 | # price of token1 - derived from sqrtPrice 534 | token1Price: Float! 535 | # current tick at end of period 536 | tick: Int 537 | # tracker for global fee growth 538 | feeGrowthGlobal0X128: BigInt! 539 | # tracker for global fee growth 540 | feeGrowthGlobal1X128: BigInt! 541 | # tvl derived in USD at end of period 542 | tvlUSD: Float! 543 | # volume in token0 544 | volumeToken0: Float! 545 | # volume in token1 546 | volumeToken1: Float! 547 | # volume in USD 548 | volumeUSD: Float! 549 | # fees in USD 550 | feesUSD: Float! 551 | # numebr of transactions during period 552 | txCount: Int! 553 | # opening price of token0 554 | open: Float! 555 | # high price of token0 556 | high: Float! 557 | # low price of token0 558 | low: Float! 559 | # close price of token0 560 | close: Float! 561 | } 562 | 563 | type TickHourData @entity { 564 | # format: -- 565 | id: ID! 566 | # unix timestamp for start of hour 567 | date: DateTime! 568 | # pointer to pool 569 | poolId: ID! 570 | pool: Pool! 571 | # pointer to tick 572 | tickId: ID! 573 | tick: Tick! 574 | # total liquidity pool has as tick lower or upper at end of period 575 | liquidityGross: BigInt! 576 | # how much liquidity changes when tick crossed at end of period 577 | liquidityNet: BigInt! 578 | # hourly volume of token0 with this tick in range 579 | volumeToken0: Float! 580 | # hourly volume of token1 with this tick in range 581 | volumeToken1: Float! 582 | # hourly volume in derived USD with this tick in range 583 | volumeUSD: Float! 584 | # fees in USD 585 | feesUSD: Float! 586 | } 587 | 588 | # Data accumulated and condensed into day stats for each exchange 589 | # Note: this entity gets saved only if there is a change during the day 590 | type TickDayData @entity { 591 | # format: -- 592 | id: ID! 593 | # timestamp rounded to current day by dividing by 86400 594 | date: DateTime! 595 | # pointer to tick 596 | tickId: ID! 597 | tick: Tick! 598 | # total liquidity pool has as tick lower or upper at end of period 599 | liquidityGross: BigInt! 600 | # how much liquidity changes when tick crossed at end of period 601 | liquidityNet: BigInt! 602 | # hourly volume of token0 with this tick in range 603 | volumeToken0: Float! 604 | # hourly volume of token1 with this tick in range 605 | volumeToken1: Float! 606 | # hourly volume in derived USD with this tick in range 607 | volumeUSD: Float! 608 | # fees in USD 609 | feesUSD: Float! 610 | # vars needed for fee computation 611 | feeGrowthOutside0X128: BigInt! 612 | feeGrowthOutside1X128: BigInt! 613 | } 614 | 615 | type TokenDayData @entity { 616 | # token address concatendated with date 617 | id: ID! 618 | # timestamp rounded to current day by dividing by 86400 619 | date: DateTime! 620 | # pointer to token 621 | tokenId: ID! 622 | token: Token! 623 | # volume in token units 624 | volume: Float! 625 | # volume in derived USD 626 | volumeUSD: Float! 627 | # volume in USD even on pools with less reliable USD values 628 | untrackedVolumeUSD: Float! 629 | # liquidity across all pools in token units 630 | totalValueLocked: Float! 631 | # liquidity across all pools in derived USD 632 | totalValueLockedUSD: Float! 633 | # price at end of period in USD 634 | priceUSD: Float! 635 | # fees in USD 636 | feesUSD: Float! 637 | # opening price USD 638 | open: Float! 639 | # high price USD 640 | high: Float! 641 | # low price USD 642 | low: Float! 643 | # close price USD 644 | close: Float! 645 | } 646 | 647 | type TokenHourData @entity { 648 | # token address concatendated with date 649 | id: ID! 650 | # unix timestamp for start of hour 651 | date: DateTime! 652 | # pointer to token 653 | tokenId: ID! 654 | token: Token! 655 | # volume in token units 656 | volume: Float! 657 | # volume in derived USD 658 | volumeUSD: Float! 659 | # volume in USD even on pools with less reliable USD values 660 | untrackedVolumeUSD: Float! 661 | # liquidity across all pools in token units 662 | totalValueLocked: Float! 663 | # liquidity across all pools in derived USD 664 | totalValueLockedUSD: Float! 665 | # price at end of period in USD 666 | priceUSD: Float! 667 | # fees in USD 668 | feesUSD: Float! 669 | # opening price USD 670 | open: Float! 671 | # high price USD 672 | high: Float! 673 | # low price USD 674 | low: Float! 675 | # close price USD 676 | close: Float! 677 | } 678 | -------------------------------------------------------------------------------- /uniswap-squid/abis/pool.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "anonymous": false, 9 | "inputs": [ 10 | { 11 | "indexed": true, 12 | "internalType": "address", 13 | "name": "owner", 14 | "type": "address" 15 | }, 16 | { 17 | "indexed": true, 18 | "internalType": "int24", 19 | "name": "tickLower", 20 | "type": "int24" 21 | }, 22 | { 23 | "indexed": true, 24 | "internalType": "int24", 25 | "name": "tickUpper", 26 | "type": "int24" 27 | }, 28 | { 29 | "indexed": false, 30 | "internalType": "uint128", 31 | "name": "amount", 32 | "type": "uint128" 33 | }, 34 | { 35 | "indexed": false, 36 | "internalType": "uint256", 37 | "name": "amount0", 38 | "type": "uint256" 39 | }, 40 | { 41 | "indexed": false, 42 | "internalType": "uint256", 43 | "name": "amount1", 44 | "type": "uint256" 45 | } 46 | ], 47 | "name": "Burn", 48 | "type": "event" 49 | }, 50 | { 51 | "anonymous": false, 52 | "inputs": [ 53 | { 54 | "indexed": true, 55 | "internalType": "address", 56 | "name": "owner", 57 | "type": "address" 58 | }, 59 | { 60 | "indexed": false, 61 | "internalType": "address", 62 | "name": "recipient", 63 | "type": "address" 64 | }, 65 | { 66 | "indexed": true, 67 | "internalType": "int24", 68 | "name": "tickLower", 69 | "type": "int24" 70 | }, 71 | { 72 | "indexed": true, 73 | "internalType": "int24", 74 | "name": "tickUpper", 75 | "type": "int24" 76 | }, 77 | { 78 | "indexed": false, 79 | "internalType": "uint128", 80 | "name": "amount0", 81 | "type": "uint128" 82 | }, 83 | { 84 | "indexed": false, 85 | "internalType": "uint128", 86 | "name": "amount1", 87 | "type": "uint128" 88 | } 89 | ], 90 | "name": "Collect", 91 | "type": "event" 92 | }, 93 | { 94 | "anonymous": false, 95 | "inputs": [ 96 | { 97 | "indexed": true, 98 | "internalType": "address", 99 | "name": "sender", 100 | "type": "address" 101 | }, 102 | { 103 | "indexed": true, 104 | "internalType": "address", 105 | "name": "recipient", 106 | "type": "address" 107 | }, 108 | { 109 | "indexed": false, 110 | "internalType": "uint128", 111 | "name": "amount0", 112 | "type": "uint128" 113 | }, 114 | { 115 | "indexed": false, 116 | "internalType": "uint128", 117 | "name": "amount1", 118 | "type": "uint128" 119 | } 120 | ], 121 | "name": "CollectProtocol", 122 | "type": "event" 123 | }, 124 | { 125 | "anonymous": false, 126 | "inputs": [ 127 | { 128 | "indexed": true, 129 | "internalType": "address", 130 | "name": "sender", 131 | "type": "address" 132 | }, 133 | { 134 | "indexed": true, 135 | "internalType": "address", 136 | "name": "recipient", 137 | "type": "address" 138 | }, 139 | { 140 | "indexed": false, 141 | "internalType": "uint256", 142 | "name": "amount0", 143 | "type": "uint256" 144 | }, 145 | { 146 | "indexed": false, 147 | "internalType": "uint256", 148 | "name": "amount1", 149 | "type": "uint256" 150 | }, 151 | { 152 | "indexed": false, 153 | "internalType": "uint256", 154 | "name": "paid0", 155 | "type": "uint256" 156 | }, 157 | { 158 | "indexed": false, 159 | "internalType": "uint256", 160 | "name": "paid1", 161 | "type": "uint256" 162 | } 163 | ], 164 | "name": "Flash", 165 | "type": "event" 166 | }, 167 | { 168 | "anonymous": false, 169 | "inputs": [ 170 | { 171 | "indexed": false, 172 | "internalType": "uint16", 173 | "name": "observationCardinalityNextOld", 174 | "type": "uint16" 175 | }, 176 | { 177 | "indexed": false, 178 | "internalType": "uint16", 179 | "name": "observationCardinalityNextNew", 180 | "type": "uint16" 181 | } 182 | ], 183 | "name": "IncreaseObservationCardinalityNext", 184 | "type": "event" 185 | }, 186 | { 187 | "anonymous": false, 188 | "inputs": [ 189 | { 190 | "indexed": false, 191 | "internalType": "uint160", 192 | "name": "sqrtPriceX96", 193 | "type": "uint160" 194 | }, 195 | { 196 | "indexed": false, 197 | "internalType": "int24", 198 | "name": "tick", 199 | "type": "int24" 200 | } 201 | ], 202 | "name": "Initialize", 203 | "type": "event" 204 | }, 205 | { 206 | "anonymous": false, 207 | "inputs": [ 208 | { 209 | "indexed": false, 210 | "internalType": "address", 211 | "name": "sender", 212 | "type": "address" 213 | }, 214 | { 215 | "indexed": true, 216 | "internalType": "address", 217 | "name": "owner", 218 | "type": "address" 219 | }, 220 | { 221 | "indexed": true, 222 | "internalType": "int24", 223 | "name": "tickLower", 224 | "type": "int24" 225 | }, 226 | { 227 | "indexed": true, 228 | "internalType": "int24", 229 | "name": "tickUpper", 230 | "type": "int24" 231 | }, 232 | { 233 | "indexed": false, 234 | "internalType": "uint128", 235 | "name": "amount", 236 | "type": "uint128" 237 | }, 238 | { 239 | "indexed": false, 240 | "internalType": "uint256", 241 | "name": "amount0", 242 | "type": "uint256" 243 | }, 244 | { 245 | "indexed": false, 246 | "internalType": "uint256", 247 | "name": "amount1", 248 | "type": "uint256" 249 | } 250 | ], 251 | "name": "Mint", 252 | "type": "event" 253 | }, 254 | { 255 | "anonymous": false, 256 | "inputs": [ 257 | { 258 | "indexed": false, 259 | "internalType": "uint8", 260 | "name": "feeProtocol0Old", 261 | "type": "uint8" 262 | }, 263 | { 264 | "indexed": false, 265 | "internalType": "uint8", 266 | "name": "feeProtocol1Old", 267 | "type": "uint8" 268 | }, 269 | { 270 | "indexed": false, 271 | "internalType": "uint8", 272 | "name": "feeProtocol0New", 273 | "type": "uint8" 274 | }, 275 | { 276 | "indexed": false, 277 | "internalType": "uint8", 278 | "name": "feeProtocol1New", 279 | "type": "uint8" 280 | } 281 | ], 282 | "name": "SetFeeProtocol", 283 | "type": "event" 284 | }, 285 | { 286 | "anonymous": false, 287 | "inputs": [ 288 | { 289 | "indexed": true, 290 | "internalType": "address", 291 | "name": "sender", 292 | "type": "address" 293 | }, 294 | { 295 | "indexed": true, 296 | "internalType": "address", 297 | "name": "recipient", 298 | "type": "address" 299 | }, 300 | { 301 | "indexed": false, 302 | "internalType": "int256", 303 | "name": "amount0", 304 | "type": "int256" 305 | }, 306 | { 307 | "indexed": false, 308 | "internalType": "int256", 309 | "name": "amount1", 310 | "type": "int256" 311 | }, 312 | { 313 | "indexed": false, 314 | "internalType": "uint160", 315 | "name": "sqrtPriceX96", 316 | "type": "uint160" 317 | }, 318 | { 319 | "indexed": false, 320 | "internalType": "uint128", 321 | "name": "liquidity", 322 | "type": "uint128" 323 | }, 324 | { 325 | "indexed": false, 326 | "internalType": "int24", 327 | "name": "tick", 328 | "type": "int24" 329 | } 330 | ], 331 | "name": "Swap", 332 | "type": "event" 333 | }, 334 | { 335 | "inputs": [ 336 | { 337 | "internalType": "int24", 338 | "name": "tickLower", 339 | "type": "int24" 340 | }, 341 | { 342 | "internalType": "int24", 343 | "name": "tickUpper", 344 | "type": "int24" 345 | }, 346 | { 347 | "internalType": "uint128", 348 | "name": "amount", 349 | "type": "uint128" 350 | } 351 | ], 352 | "name": "burn", 353 | "outputs": [ 354 | { 355 | "internalType": "uint256", 356 | "name": "amount0", 357 | "type": "uint256" 358 | }, 359 | { 360 | "internalType": "uint256", 361 | "name": "amount1", 362 | "type": "uint256" 363 | } 364 | ], 365 | "stateMutability": "nonpayable", 366 | "type": "function" 367 | }, 368 | { 369 | "inputs": [ 370 | { 371 | "internalType": "address", 372 | "name": "recipient", 373 | "type": "address" 374 | }, 375 | { 376 | "internalType": "int24", 377 | "name": "tickLower", 378 | "type": "int24" 379 | }, 380 | { 381 | "internalType": "int24", 382 | "name": "tickUpper", 383 | "type": "int24" 384 | }, 385 | { 386 | "internalType": "uint128", 387 | "name": "amount0Requested", 388 | "type": "uint128" 389 | }, 390 | { 391 | "internalType": "uint128", 392 | "name": "amount1Requested", 393 | "type": "uint128" 394 | } 395 | ], 396 | "name": "collect", 397 | "outputs": [ 398 | { 399 | "internalType": "uint128", 400 | "name": "amount0", 401 | "type": "uint128" 402 | }, 403 | { 404 | "internalType": "uint128", 405 | "name": "amount1", 406 | "type": "uint128" 407 | } 408 | ], 409 | "stateMutability": "nonpayable", 410 | "type": "function" 411 | }, 412 | { 413 | "inputs": [ 414 | { 415 | "internalType": "address", 416 | "name": "recipient", 417 | "type": "address" 418 | }, 419 | { 420 | "internalType": "uint128", 421 | "name": "amount0Requested", 422 | "type": "uint128" 423 | }, 424 | { 425 | "internalType": "uint128", 426 | "name": "amount1Requested", 427 | "type": "uint128" 428 | } 429 | ], 430 | "name": "collectProtocol", 431 | "outputs": [ 432 | { 433 | "internalType": "uint128", 434 | "name": "amount0", 435 | "type": "uint128" 436 | }, 437 | { 438 | "internalType": "uint128", 439 | "name": "amount1", 440 | "type": "uint128" 441 | } 442 | ], 443 | "stateMutability": "nonpayable", 444 | "type": "function" 445 | }, 446 | { 447 | "inputs": [], 448 | "name": "factory", 449 | "outputs": [ 450 | { 451 | "internalType": "address", 452 | "name": "", 453 | "type": "address" 454 | } 455 | ], 456 | "stateMutability": "view", 457 | "type": "function" 458 | }, 459 | { 460 | "inputs": [], 461 | "name": "fee", 462 | "outputs": [ 463 | { 464 | "internalType": "uint24", 465 | "name": "", 466 | "type": "uint24" 467 | } 468 | ], 469 | "stateMutability": "view", 470 | "type": "function" 471 | }, 472 | { 473 | "inputs": [], 474 | "name": "feeGrowthGlobal0X128", 475 | "outputs": [ 476 | { 477 | "internalType": "uint256", 478 | "name": "", 479 | "type": "uint256" 480 | } 481 | ], 482 | "stateMutability": "view", 483 | "type": "function" 484 | }, 485 | { 486 | "inputs": [], 487 | "name": "feeGrowthGlobal1X128", 488 | "outputs": [ 489 | { 490 | "internalType": "uint256", 491 | "name": "", 492 | "type": "uint256" 493 | } 494 | ], 495 | "stateMutability": "view", 496 | "type": "function" 497 | }, 498 | { 499 | "inputs": [ 500 | { 501 | "internalType": "address", 502 | "name": "recipient", 503 | "type": "address" 504 | }, 505 | { 506 | "internalType": "uint256", 507 | "name": "amount0", 508 | "type": "uint256" 509 | }, 510 | { 511 | "internalType": "uint256", 512 | "name": "amount1", 513 | "type": "uint256" 514 | }, 515 | { 516 | "internalType": "bytes", 517 | "name": "data", 518 | "type": "bytes" 519 | } 520 | ], 521 | "name": "flash", 522 | "outputs": [], 523 | "stateMutability": "nonpayable", 524 | "type": "function" 525 | }, 526 | { 527 | "inputs": [ 528 | { 529 | "internalType": "uint16", 530 | "name": "observationCardinalityNext", 531 | "type": "uint16" 532 | } 533 | ], 534 | "name": "increaseObservationCardinalityNext", 535 | "outputs": [], 536 | "stateMutability": "nonpayable", 537 | "type": "function" 538 | }, 539 | { 540 | "inputs": [ 541 | { 542 | "internalType": "uint160", 543 | "name": "sqrtPriceX96", 544 | "type": "uint160" 545 | } 546 | ], 547 | "name": "initialize", 548 | "outputs": [], 549 | "stateMutability": "nonpayable", 550 | "type": "function" 551 | }, 552 | { 553 | "inputs": [], 554 | "name": "liquidity", 555 | "outputs": [ 556 | { 557 | "internalType": "uint128", 558 | "name": "", 559 | "type": "uint128" 560 | } 561 | ], 562 | "stateMutability": "view", 563 | "type": "function" 564 | }, 565 | { 566 | "inputs": [], 567 | "name": "maxLiquidityPerTick", 568 | "outputs": [ 569 | { 570 | "internalType": "uint128", 571 | "name": "", 572 | "type": "uint128" 573 | } 574 | ], 575 | "stateMutability": "view", 576 | "type": "function" 577 | }, 578 | { 579 | "inputs": [ 580 | { 581 | "internalType": "address", 582 | "name": "recipient", 583 | "type": "address" 584 | }, 585 | { 586 | "internalType": "int24", 587 | "name": "tickLower", 588 | "type": "int24" 589 | }, 590 | { 591 | "internalType": "int24", 592 | "name": "tickUpper", 593 | "type": "int24" 594 | }, 595 | { 596 | "internalType": "uint128", 597 | "name": "amount", 598 | "type": "uint128" 599 | }, 600 | { 601 | "internalType": "bytes", 602 | "name": "data", 603 | "type": "bytes" 604 | } 605 | ], 606 | "name": "mint", 607 | "outputs": [ 608 | { 609 | "internalType": "uint256", 610 | "name": "amount0", 611 | "type": "uint256" 612 | }, 613 | { 614 | "internalType": "uint256", 615 | "name": "amount1", 616 | "type": "uint256" 617 | } 618 | ], 619 | "stateMutability": "nonpayable", 620 | "type": "function" 621 | }, 622 | { 623 | "inputs": [ 624 | { 625 | "internalType": "uint256", 626 | "name": "index", 627 | "type": "uint256" 628 | } 629 | ], 630 | "name": "observations", 631 | "outputs": [ 632 | { 633 | "internalType": "uint32", 634 | "name": "blockTimestamp", 635 | "type": "uint32" 636 | }, 637 | { 638 | "internalType": "int56", 639 | "name": "tickCumulative", 640 | "type": "int56" 641 | }, 642 | { 643 | "internalType": "uint160", 644 | "name": "secondsPerLiquidityCumulativeX128", 645 | "type": "uint160" 646 | }, 647 | { 648 | "internalType": "bool", 649 | "name": "initialized", 650 | "type": "bool" 651 | } 652 | ], 653 | "stateMutability": "view", 654 | "type": "function" 655 | }, 656 | { 657 | "inputs": [ 658 | { 659 | "internalType": "uint32[]", 660 | "name": "secondsAgos", 661 | "type": "uint32[]" 662 | } 663 | ], 664 | "name": "observe", 665 | "outputs": [ 666 | { 667 | "internalType": "int56[]", 668 | "name": "tickCumulatives", 669 | "type": "int56[]" 670 | }, 671 | { 672 | "internalType": "uint160[]", 673 | "name": "secondsPerLiquidityCumulativeX128s", 674 | "type": "uint160[]" 675 | } 676 | ], 677 | "stateMutability": "view", 678 | "type": "function" 679 | }, 680 | { 681 | "inputs": [ 682 | { 683 | "internalType": "bytes32", 684 | "name": "key", 685 | "type": "bytes32" 686 | } 687 | ], 688 | "name": "positions", 689 | "outputs": [ 690 | { 691 | "internalType": "uint128", 692 | "name": "_liquidity", 693 | "type": "uint128" 694 | }, 695 | { 696 | "internalType": "uint256", 697 | "name": "feeGrowthInside0LastX128", 698 | "type": "uint256" 699 | }, 700 | { 701 | "internalType": "uint256", 702 | "name": "feeGrowthInside1LastX128", 703 | "type": "uint256" 704 | }, 705 | { 706 | "internalType": "uint128", 707 | "name": "tokensOwed0", 708 | "type": "uint128" 709 | }, 710 | { 711 | "internalType": "uint128", 712 | "name": "tokensOwed1", 713 | "type": "uint128" 714 | } 715 | ], 716 | "stateMutability": "view", 717 | "type": "function" 718 | }, 719 | { 720 | "inputs": [], 721 | "name": "protocolFees", 722 | "outputs": [ 723 | { 724 | "internalType": "uint128", 725 | "name": "token0", 726 | "type": "uint128" 727 | }, 728 | { 729 | "internalType": "uint128", 730 | "name": "token1", 731 | "type": "uint128" 732 | } 733 | ], 734 | "stateMutability": "view", 735 | "type": "function" 736 | }, 737 | { 738 | "inputs": [ 739 | { 740 | "internalType": "uint8", 741 | "name": "feeProtocol0", 742 | "type": "uint8" 743 | }, 744 | { 745 | "internalType": "uint8", 746 | "name": "feeProtocol1", 747 | "type": "uint8" 748 | } 749 | ], 750 | "name": "setFeeProtocol", 751 | "outputs": [], 752 | "stateMutability": "nonpayable", 753 | "type": "function" 754 | }, 755 | { 756 | "inputs": [], 757 | "name": "slot0", 758 | "outputs": [ 759 | { 760 | "internalType": "uint160", 761 | "name": "sqrtPriceX96", 762 | "type": "uint160" 763 | }, 764 | { 765 | "internalType": "int24", 766 | "name": "tick", 767 | "type": "int24" 768 | }, 769 | { 770 | "internalType": "uint16", 771 | "name": "observationIndex", 772 | "type": "uint16" 773 | }, 774 | { 775 | "internalType": "uint16", 776 | "name": "observationCardinality", 777 | "type": "uint16" 778 | }, 779 | { 780 | "internalType": "uint16", 781 | "name": "observationCardinalityNext", 782 | "type": "uint16" 783 | }, 784 | { 785 | "internalType": "uint8", 786 | "name": "feeProtocol", 787 | "type": "uint8" 788 | }, 789 | { 790 | "internalType": "bool", 791 | "name": "unlocked", 792 | "type": "bool" 793 | } 794 | ], 795 | "stateMutability": "view", 796 | "type": "function" 797 | }, 798 | { 799 | "inputs": [ 800 | { 801 | "internalType": "int24", 802 | "name": "tickLower", 803 | "type": "int24" 804 | }, 805 | { 806 | "internalType": "int24", 807 | "name": "tickUpper", 808 | "type": "int24" 809 | } 810 | ], 811 | "name": "snapshotCumulativesInside", 812 | "outputs": [ 813 | { 814 | "internalType": "int56", 815 | "name": "tickCumulativeInside", 816 | "type": "int56" 817 | }, 818 | { 819 | "internalType": "uint160", 820 | "name": "secondsPerLiquidityInsideX128", 821 | "type": "uint160" 822 | }, 823 | { 824 | "internalType": "uint32", 825 | "name": "secondsInside", 826 | "type": "uint32" 827 | } 828 | ], 829 | "stateMutability": "view", 830 | "type": "function" 831 | }, 832 | { 833 | "inputs": [ 834 | { 835 | "internalType": "address", 836 | "name": "recipient", 837 | "type": "address" 838 | }, 839 | { 840 | "internalType": "bool", 841 | "name": "zeroForOne", 842 | "type": "bool" 843 | }, 844 | { 845 | "internalType": "int256", 846 | "name": "amountSpecified", 847 | "type": "int256" 848 | }, 849 | { 850 | "internalType": "uint160", 851 | "name": "sqrtPriceLimitX96", 852 | "type": "uint160" 853 | }, 854 | { 855 | "internalType": "bytes", 856 | "name": "data", 857 | "type": "bytes" 858 | } 859 | ], 860 | "name": "swap", 861 | "outputs": [ 862 | { 863 | "internalType": "int256", 864 | "name": "amount0", 865 | "type": "int256" 866 | }, 867 | { 868 | "internalType": "int256", 869 | "name": "amount1", 870 | "type": "int256" 871 | } 872 | ], 873 | "stateMutability": "nonpayable", 874 | "type": "function" 875 | }, 876 | { 877 | "inputs": [ 878 | { 879 | "internalType": "int16", 880 | "name": "wordPosition", 881 | "type": "int16" 882 | } 883 | ], 884 | "name": "tickBitmap", 885 | "outputs": [ 886 | { 887 | "internalType": "uint256", 888 | "name": "", 889 | "type": "uint256" 890 | } 891 | ], 892 | "stateMutability": "view", 893 | "type": "function" 894 | }, 895 | { 896 | "inputs": [], 897 | "name": "tickSpacing", 898 | "outputs": [ 899 | { 900 | "internalType": "int24", 901 | "name": "", 902 | "type": "int24" 903 | } 904 | ], 905 | "stateMutability": "view", 906 | "type": "function" 907 | }, 908 | { 909 | "inputs": [ 910 | { 911 | "internalType": "int24", 912 | "name": "tick", 913 | "type": "int24" 914 | } 915 | ], 916 | "name": "ticks", 917 | "outputs": [ 918 | { 919 | "internalType": "uint128", 920 | "name": "liquidityGross", 921 | "type": "uint128" 922 | }, 923 | { 924 | "internalType": "int128", 925 | "name": "liquidityNet", 926 | "type": "int128" 927 | }, 928 | { 929 | "internalType": "uint256", 930 | "name": "feeGrowthOutside0X128", 931 | "type": "uint256" 932 | }, 933 | { 934 | "internalType": "uint256", 935 | "name": "feeGrowthOutside1X128", 936 | "type": "uint256" 937 | }, 938 | { 939 | "internalType": "int56", 940 | "name": "tickCumulativeOutside", 941 | "type": "int56" 942 | }, 943 | { 944 | "internalType": "uint160", 945 | "name": "secondsPerLiquidityOutsideX128", 946 | "type": "uint160" 947 | }, 948 | { 949 | "internalType": "uint32", 950 | "name": "secondsOutside", 951 | "type": "uint32" 952 | }, 953 | { 954 | "internalType": "bool", 955 | "name": "initialized", 956 | "type": "bool" 957 | } 958 | ], 959 | "stateMutability": "view", 960 | "type": "function" 961 | }, 962 | { 963 | "inputs": [], 964 | "name": "token0", 965 | "outputs": [ 966 | { 967 | "internalType": "address", 968 | "name": "", 969 | "type": "address" 970 | } 971 | ], 972 | "stateMutability": "view", 973 | "type": "function" 974 | }, 975 | { 976 | "inputs": [], 977 | "name": "token1", 978 | "outputs": [ 979 | { 980 | "internalType": "address", 981 | "name": "", 982 | "type": "address" 983 | } 984 | ], 985 | "stateMutability": "view", 986 | "type": "function" 987 | } 988 | ] --------------------------------------------------------------------------------