├── .commitlintrc.json ├── .eslintignore ├── .eslintrc ├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ ├── format.yml │ ├── lint.yml │ └── tests.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── helpers ├── constants.ts └── utils.ts ├── lerna.json ├── package.json ├── subgraphs ├── aggregator │ ├── abis │ │ ├── LooksRareExchange.json │ │ └── Seaport.json │ ├── matchstick.yaml │ ├── networks.json │ ├── package.json │ ├── schema.graphql │ ├── src │ │ ├── LooksRareV1 │ │ │ └── index.ts │ │ ├── Seaport │ │ │ └── index.ts │ │ └── utils │ │ │ ├── Seaport │ │ │ ├── calculateVolume.ts │ │ │ ├── isSameConsiderationToken.ts │ │ │ └── isSameOfferToken.ts │ │ │ ├── extractOriginator.ts │ │ │ ├── findSweepEventFromLogs.ts │ │ │ ├── getOrInitializeAggregator.ts │ │ │ ├── getOrInitializeAggregatorByCurrency.ts │ │ │ ├── getOrInitializeAggregatorDailyData.ts │ │ │ ├── getOrInitializeAggregatorDailyDataByCurrency.ts │ │ │ ├── getOrInitializeCollectionByCurrency.ts │ │ │ ├── getOrInitializeCollectionDailyDataByCurrency.ts │ │ │ ├── getOrInitializeMarketplace.ts │ │ │ ├── getOrInitializeMarketplaceByCurrency.ts │ │ │ ├── getOrInitializeMarketplaceDailyData.ts │ │ │ ├── getOrInitializeMarketplaceDailyDataByCurrency.ts │ │ │ ├── getOrInitializeUserByCurrency.ts │ │ │ └── getOrInitializeUserDailyDataByCurrency.ts │ ├── subgraph.yaml │ ├── tests │ │ ├── aggregator.test.ts │ │ └── helpers │ │ │ ├── sharedTests.ts │ │ │ └── utils.ts │ └── tsconfig.json ├── airdrop │ ├── abis │ │ ├── ERC20.json │ │ ├── IUniswapV2Pair.json │ │ └── WyvernExchange.json │ ├── mappings │ │ ├── index.ts │ │ └── utils │ │ │ ├── erc20.ts │ │ │ ├── getPrice.ts │ │ │ └── index.ts │ ├── package.json │ ├── schema.graphql │ └── subgraph.yaml ├── eip1155 │ ├── abis │ │ └── EIP1155.json │ ├── mappings │ │ ├── index.ts │ │ └── utils │ │ │ └── eip1155.ts │ ├── networks.json │ ├── package.json │ ├── schema.graphql │ └── subgraph.yaml ├── eip721 │ ├── abis │ │ └── EIP721.json │ ├── mappings │ │ ├── index.ts │ │ └── utils │ │ │ └── eip721.ts │ ├── networks.json │ ├── package.json │ ├── schema.graphql │ └── subgraph.yaml ├── exchange │ ├── mappings │ │ ├── index.ts │ │ └── utils │ │ │ ├── fetchProtocolFee.ts │ │ │ └── updateDailyData.ts │ ├── matchstick.yaml │ ├── networks.json │ ├── package.json │ ├── schema.graphql │ ├── subgraph.yaml │ └── tests │ │ ├── exchange.test.ts │ │ └── helpers │ │ ├── config.ts │ │ └── utils.ts ├── looks-distribution │ ├── abis │ │ └── LightMultiRewardsDistributor.json │ ├── mappings │ │ ├── index.ts │ │ └── utils │ │ │ ├── config │ │ │ ├── addresses-goerli.ts │ │ │ ├── addresses-mainnet.ts │ │ │ └── addresses.ts │ │ │ ├── initializeDailySnapshot.ts │ │ │ ├── initializeOverview.ts │ │ │ ├── initializeUser.ts │ │ │ ├── rpc-calls │ │ │ ├── fetchShares.ts │ │ │ └── fetchTotalAmountStaked.ts │ │ │ ├── setupOverviewAndDailySnapshot.ts │ │ │ └── updateDailyData.ts │ ├── matchstick.yaml │ ├── networks.json │ ├── package.json │ ├── schema.graphql │ ├── subgraph.yaml │ └── tests │ │ ├── aggregatorFeeSharingWithUniswapV3.test.ts │ │ ├── feeSharingSystem.test.ts │ │ ├── helpers │ │ ├── aggregatorFeeSharingWithUniswapV3 │ │ │ └── utils.ts │ │ ├── feeSharingSystem │ │ │ └── utils.ts │ │ ├── multiRewardsDistributor │ │ │ └── utils.ts │ │ └── tradingRewardsDistributor │ │ │ └── utils.ts │ │ ├── multiRewardsDistributor.test.ts │ │ └── tradingRewardsDistributor.test.ts ├── raffle │ ├── abis │ │ ├── ERC20.json │ │ ├── IRaffle.json │ │ ├── IRaffleV2.json │ │ ├── Raffle.json │ │ └── RaffleV2.json │ ├── mappings │ │ ├── entities │ │ │ └── currency.ts │ │ ├── index.ts │ │ └── utils │ │ │ ├── ERC20.ts │ │ │ └── index.ts │ ├── networks.json │ ├── package.json │ ├── schema.graphql │ └── subgraph.yaml └── royalty-fee-registry │ ├── mappings │ └── index.ts │ ├── matchstick.yaml │ ├── package.json │ ├── schema.graphql │ ├── subgraph.yaml │ └── tests │ ├── helpers │ └── utils.ts │ └── royaltyFeeRegistry.test.ts ├── tsconfig.json └── yarn.lock /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"], 3 | "rules": { 4 | "subject-case": [2, "always", "sentence-case"], 5 | "type-enum": [ 6 | 2, 7 | "always", 8 | ["build", "ci", "chore", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"] 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/ 2 | generated/ 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": ["@typescript-eslint"], 4 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], 5 | "rules": { 6 | "@typescript-eslint/ban-types": "off" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence, 3 | # @global-owner1 and @global-owner2 will be requested for 4 | # review when someone opens a pull request. 5 | * @hexacroes @0xhiroshi 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | # Maintain dependencies for GitHub Actions 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "monthly" 9 | open-pull-requests-limit: 3 10 | -------------------------------------------------------------------------------- /.github/workflows/format.yml: -------------------------------------------------------------------------------- 1 | name: Format 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | format: 12 | name: Prettier 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 10 15 | 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v3 19 | 20 | - name: Setup Node 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: 18.x 24 | cache: "yarn" 25 | 26 | - name: Install dependencies 27 | run: yarn install --frozen-lockfile 28 | 29 | - name: Run Prettier 30 | run: yarn format:check 31 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | lint: 12 | name: ESLint 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 10 15 | 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v3 19 | 20 | - name: Setup Node 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: 18.x 24 | cache: "yarn" 25 | 26 | - name: Install dependencies 27 | run: yarn install --frozen-lockfile 28 | 29 | - name: Run ESLint 30 | run: yarn lint 31 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | tests: 12 | name: Matchstick 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 10 15 | container: node:18 16 | 17 | services: 18 | postgres: 19 | image: postgres:15 20 | env: 21 | POSTGRES_PASSWORD: postgres 22 | options: >- 23 | --health-cmd pg_isready 24 | --health-interval 10s 25 | --health-timeout 5s 26 | --health-retries 5 27 | 28 | steps: 29 | - name: Checkout code 30 | uses: actions/checkout@v3 31 | 32 | - name: Setup Node 33 | uses: actions/setup-node@v3 34 | with: 35 | node-version: 18.x 36 | cache: "yarn" 37 | 38 | - name: Install dependencies 39 | run: yarn install --frozen-lockfile 40 | 41 | - name: Run codegen 42 | run: yarn codegen 43 | 44 | - name: Run build 45 | run: yarn build:mainnet 46 | 47 | - name: Run tests 48 | run: yarn test 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Vercel build output 79 | .vercel 80 | 81 | # Next.js build output 82 | .next 83 | 84 | # Nuxt.js build / generate output 85 | .nuxt 86 | dist 87 | 88 | # Gatsby files 89 | .cache/ 90 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 91 | # https://nextjs.org/blog/next-9-1#public-directory-support 92 | # public 93 | 94 | # vuepress build output 95 | .vuepress/dist 96 | 97 | # Serverless directories 98 | .serverless/ 99 | 100 | # FuseBox cache 101 | .fusebox/ 102 | 103 | # DynamoDB Local files 104 | .dynamodb/ 105 | 106 | # TernJS port file 107 | .tern-port 108 | 109 | # MacOS 110 | .DS_Store 111 | 112 | # IDE's 113 | .vscode/ 114 | .idea/ 115 | 116 | # GraphQL directories 117 | build/ 118 | generated/ 119 | 120 | # Matchstick binaries 121 | .bin 122 | .latest.json 123 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint 5 | yarn format:check -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build/ 2 | generated/ 3 | node_modules/ 4 | .latest.json 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 LooksRare 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LooksRare Subgraph 2 | 3 | ## Description 4 | 5 | This mono-repository contains all subgraphs ([using The Graph](https://docs.looksrare.org/developers/looksrare-subgraph-overview#what-is-a-subgraph)) available in the LooksRare ecosystem. 6 | 7 | ## List of subgraphs 8 | 9 | | Name | Description | Status | Can run tests? | 10 | | -------------------- | -------------------------------------------------------------------------------------- | --------------------------------------------- | -------------- | 11 | | Exchange | Subgraph with trade events on the `LooksRareExchange` | Deployed (Hosted Network + The Graph network) | ✅ | 12 | | LOOKS Distribution | Subgraph with events related to the LOOKS token distribution across multiple contracts | Deployed (Hosted Network) | ✅ | 13 | | Royalty fee registry | Subgraph tracking collection royalty events from the `RoyaltyFeeRegistry` contract | Deployed (Hosted Network) | ✅ | 14 | | Airdrop | Subgraph to calculate LOOKS airdrop based on `WyvernExchange` adjusted volumes | Deprecated | ❌ | 15 | | EIP721 | Generic subgraph to track ERC-721 tokens | Informational | ❌ | 16 | | EIP1155 | Generic subgraph to track ERC-1155 tokens | Informational | ❌ | 17 | 18 | ## Documentation 19 | 20 | The documentation for **deployed subgraphs** is available [here](https://docs.looksrare.org/developers/category/subgraph-documentation). 21 | 22 | ## Setup and deployment 23 | 24 | For any of the subgraphs (referred to as `[subgraph]` on the `mainnet` network): 25 | 26 | 1. Run the `cd subgraphs/[subgraph]` command to move to the subgraph directory. 27 | 2. Run the `yarn codegen` command to prepare the TypeScript sources for the GraphQL (`./generated/*`). 28 | 3. Run the `yarn build:mainnet` command to build the subgraph, and check compilation errors before deploying. 29 | 4. Run `graph auth --product hosted-service ''` (for Hosted Service) or `graph auth --studio '` (for The Graph network). 30 | 5. Deploy via `yarn deploy:mainnet`. 31 | 32 | ## Run tests 33 | 34 | Unit tests are written using the [Matchstick framework from LimeChain](https://github.com/LimeChain/matchstick). 35 | 36 | The [Matchstick framework](https://thegraph.com/docs/en/developer/matchstick/) requires Postgres installed, [read more here.](https://github.com/LimeChain/matchstick#os-specific-release-binaries-%EF%B8%8F) 37 | 38 | For any of the subgraph supporting tests (referred to as `[subgraph]`): 39 | 40 | 1. Run the `cd subgraphs/[subgraph]` command to move to the subgraph directory. 41 | 2. Run the `yarn codegen` command to prepare the TypeScript sources for the GraphQL (`./generated/*`). 42 | 3. Run the `yarn build:mainnet` command to build the subgraph, and check compilation errors. 43 | 4. Run the `yarn test` command to execute the tests. 44 | -------------------------------------------------------------------------------- /helpers/constants.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigDecimal, BigInt, ByteArray, crypto } from "@graphprotocol/graph-ts"; 2 | 3 | export const ZERO_BI = BigInt.zero(); 4 | export const ONE_BI = BigInt.fromI32(1); 5 | export const TWO_BI = BigInt.fromI32(2); 6 | export const THREE_BI = BigInt.fromI32(3); 7 | export const FOUR_BI = BigInt.fromI32(4); 8 | export const TEN_BI = BigInt.fromI32(10); 9 | export const ONE_ETHER_IN_WEI = BigInt.fromI32(10).pow(18); 10 | export const ONE_DAY_BI = BigInt.fromI32(86400); 11 | 12 | export const ZERO_BD = BigDecimal.zero(); 13 | export const ONE_BD = BigDecimal.fromString("1"); 14 | 15 | export const ZERO_ADDRESS = Address.zero(); 16 | 17 | export const LOOKSRARE_AGGREGATOR = Address.fromString("0x00000000005228B791a99a61f36A130d50600106"); 18 | export const LOOKSRARE_AGGREGATOR_SWEEP_EVENT_TOPIC = crypto 19 | .keccak256(ByteArray.fromUTF8("Sweep(address)")) 20 | .toHexString(); 21 | -------------------------------------------------------------------------------- /helpers/utils.ts: -------------------------------------------------------------------------------- 1 | import { BigDecimal, BigInt } from "@graphprotocol/graph-ts"; 2 | import { TEN_BI } from "./constants"; 3 | 4 | /** 5 | * @param amount amount in BigInt 6 | * @param decimals number of decimal (optional) 7 | * @notice Parse the amount into a BigDecimal instance expressed in decimals 8 | */ 9 | export function toBigDecimal(amount: BigInt, decimals: i32 = 18): BigDecimal { 10 | return amount.divDecimal(TEN_BI.pow(decimals as u8).toBigDecimal()); 11 | } 12 | 13 | /** 14 | * @param amount amount in ETH (i32) 15 | * @param decimals number of decimal (optional) 16 | * @notice Parse the amount into a BigInt instance of the amount of wei. 17 | */ 18 | export function parseEther(amount: i32, decimals: u8 = 18): BigInt { 19 | const adjuster = TEN_BI.pow(decimals); 20 | return BigInt.fromI32(amount).times(adjuster); 21 | } 22 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "npmClient": "yarn", 3 | "packages": [ 4 | "subgraphs/**/*" 5 | ], 6 | "version": "independent", 7 | "$schema": "./node_modules/lerna/schemas/lerna-schema.json" 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "subgraph", 3 | "description": "LooksRare subgraphs (Graph Protocol)", 4 | "version": "1.0.0", 5 | "private": true, 6 | "repository": "git@github.com:looksrare/subgraph.git", 7 | "author": "LooksRare", 8 | "license": "MIT", 9 | "workspaces": [ 10 | "subgraphs/**/*" 11 | ], 12 | "scripts": { 13 | "build:goerli": "lerna run build:goerli", 14 | "build:mainnet": "lerna run build:mainnet", 15 | "codegen": "lerna run codegen", 16 | "format:check": "prettier --check '*/**/*.{js,ts,yaml,yml,json}'", 17 | "format:write": "prettier --write '*/**/*.{js,ts,yaml,yml,json}'", 18 | "lint": "eslint '**/**/*.{js,ts}'", 19 | "prepare": "husky install", 20 | "test": "lerna run test:lerna" 21 | }, 22 | "dependencies": { 23 | "@looksrare/sdk": "^0.12.0" 24 | }, 25 | "devDependencies": { 26 | "@commitlint/cli": "^17.6.0", 27 | "@commitlint/config-conventional": "^17.6.0", 28 | "@graphprotocol/graph-cli": "^0.53.0", 29 | "@graphprotocol/graph-ts": "^0.31.0", 30 | "@types/node": "^18", 31 | "@typescript-eslint/eslint-plugin": "^6.2.0", 32 | "@typescript-eslint/parser": "^6.2.0", 33 | "eslint": "^8.45.0", 34 | "eslint-config-prettier": "^8.9.0", 35 | "husky": "^8.0.0", 36 | "lerna": "^7.1.0", 37 | "matchstick-as": "^0.5.0", 38 | "prettier": "^3.0.0", 39 | "typescript": "^4.9.5" 40 | }, 41 | "engines": { 42 | "node": "18.x" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /subgraphs/aggregator/matchstick.yaml: -------------------------------------------------------------------------------- 1 | libsFolder: ../../node_modules/ 2 | -------------------------------------------------------------------------------- /subgraphs/aggregator/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "LooksRareV1": { 4 | "address": "0x59728544B08AB483533076417FbBB2fD0B17CE3a" 5 | }, 6 | "Seaport": { 7 | "address": "0x00000000000001ad428e4906aE43D8F9852d0dD6" 8 | } 9 | }, 10 | "goerli": { 11 | "LooksRareV1": { 12 | "address": "0xD112466471b5438C1ca2D218694200e49d81D047" 13 | }, 14 | "Seaport": { 15 | "address": "0x00000000000001ad428e4906aE43D8F9852d0dD6" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /subgraphs/aggregator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aggregator", 3 | "description": "LooksRare aggregator subgraph", 4 | "version": "1.0.0", 5 | "repository": "git@github.com:looksrare/subgraphs.git", 6 | "author": "LooksRare", 7 | "license": "MIT", 8 | "scripts": { 9 | "codegen": "graph codegen", 10 | "build:goerli": "graph build subgraph.yaml --network goerli", 11 | "build:mainnet": "graph build subgraph.yaml --network mainnet", 12 | "deploy:goerli": "graph deploy --product hosted-service 0xhiroshi/aggregator subgraph.yaml --network goerli", 13 | "deploy:mainnet": "graph deploy --product hosted-service looksrare/aggregator subgraph.yaml --network mainnet", 14 | "deploy:studio": "graph codegen subgraph.yaml && graph build subgraph.yaml && graph deploy --studio looksrare-aggregator --network mainnet", 15 | "test": "graph test -r", 16 | "test:lerna": "graph codegen subgraph.yaml && graph build subgraph.yaml && graph test -r" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /subgraphs/aggregator/schema.graphql: -------------------------------------------------------------------------------- 1 | type Aggregator @entity { 2 | " { Hard-coded to LooksRareAggregator } " 3 | id: ID! 4 | 5 | " Total number of unique users " 6 | users: BigInt! 7 | 8 | " Total number of collections traded " 9 | collections: BigInt! 10 | 11 | " Total number of transactions " 12 | transactions: BigInt! 13 | 14 | " Aggregator by currency " 15 | byCurrency: [AggregatorByCurrency!]! @derivedFrom(field: "aggregator") 16 | 17 | " Aggregator daily data " 18 | dailyData: [AggregatorDailyData!]! @derivedFrom(field: "aggregator") 19 | } 20 | 21 | type AggregatorByCurrency @entity { 22 | " { Currency } " 23 | id: ID! 24 | 25 | " Trade currency " 26 | currency: Bytes! 27 | 28 | " Total number of unique users " 29 | users: BigInt! 30 | 31 | " Total number of collections traded " 32 | collections: BigInt! 33 | 34 | " Total number of transactions " 35 | transactions: BigInt! 36 | 37 | " Total volume " 38 | volume: BigDecimal! 39 | 40 | " Pointer to Aggregator " 41 | aggregator: Aggregator! 42 | 43 | " Aggregator daily data " 44 | dailyData: [AggregatorDailyDataByCurrency!]! @derivedFrom(field: "aggregatorByCurrency") 45 | } 46 | 47 | type AggregatorDailyData @entity { 48 | " { Date } " 49 | id: ID! 50 | 51 | " Pointer to Aggregator " 52 | aggregator: Aggregator! 53 | 54 | " Date " 55 | date: BigInt! 56 | 57 | " Total number of unique users " 58 | users: BigInt! 59 | 60 | " Total number of collections traded " 61 | collections: BigInt! 62 | 63 | " Total number of transactions " 64 | transactions: BigInt! 65 | 66 | " Aggregator daily data by currency " 67 | byCurrency: [AggregatorDailyDataByCurrency!]! @derivedFrom(field: "aggregatorDailyData") 68 | } 69 | 70 | type AggregatorDailyDataByCurrency @entity { 71 | " { Currency }-{ Date } " 72 | id: ID! 73 | 74 | " Trade currency " 75 | currency: Bytes! 76 | 77 | " Pointer to AggregatorByCurrency " 78 | aggregatorByCurrency: AggregatorByCurrency! 79 | 80 | " Pointer to AggregatorDailyData " 81 | aggregatorDailyData: AggregatorDailyData! 82 | 83 | " Date " 84 | date: BigInt! 85 | 86 | " Total number of unique users " 87 | users: BigInt! 88 | 89 | " Total number of collections traded " 90 | collections: BigInt! 91 | 92 | " Transactions " 93 | transactions: [Transaction!]! @derivedFrom(field: "aggregatorDailyDataByCurrency") 94 | 95 | " Total volume " 96 | volume: BigDecimal! 97 | } 98 | 99 | type Collection @entity { 100 | " { Address } " 101 | id: ID! 102 | 103 | " Total number of transactions " 104 | transactions: BigInt! 105 | 106 | " Collection daily data " 107 | dailyData: [CollectionDailyData!]! @derivedFrom(field: "collection") 108 | } 109 | 110 | type CollectionByCurrency @entity { 111 | " { Address }-{ Currency } " 112 | id: ID! 113 | 114 | " Trade currency " 115 | currency: Bytes! 116 | 117 | " Total number of transactions " 118 | transactions: BigInt! 119 | 120 | " Total volume " 121 | volume: BigDecimal! 122 | 123 | " Collection daily data " 124 | dailyData: [CollectionDailyDataByCurrency!]! @derivedFrom(field: "collectionByCurrency") 125 | } 126 | 127 | type CollectionDailyData @entity { 128 | " { Address }-{ Date } " 129 | id: ID! 130 | 131 | " Pointer to Collection " 132 | collection: Collection! 133 | 134 | " Date " 135 | date: BigInt! 136 | 137 | " Total number of transactions " 138 | transactions: BigInt! 139 | } 140 | 141 | type CollectionDailyDataByCurrency @entity { 142 | " { Address }-{ Currency }-{ Date } " 143 | id: ID! 144 | 145 | " Pointer to CollectionByCurrency " 146 | collectionByCurrency: CollectionByCurrency! 147 | 148 | " Trade currency " 149 | currency: Bytes! 150 | 151 | " Date " 152 | date: BigInt! 153 | 154 | " Transactions " 155 | transactions: [Transaction!]! @derivedFrom(field: "collectionDailyDataByCurrency") 156 | 157 | " Total volume " 158 | volume: BigDecimal! 159 | } 160 | 161 | type Marketplace @entity { 162 | " { Marketplace } " 163 | id: ID! 164 | 165 | " Total number of unique users " 166 | users: BigInt! 167 | 168 | " Total number of collections traded " 169 | collections: BigInt! 170 | 171 | " Total number of transactions " 172 | transactions: BigInt! 173 | 174 | " Marketplace daily data " 175 | dailyData: [MarketplaceDailyData!]! @derivedFrom(field: "marketplace") 176 | 177 | " Marketplace by currency " 178 | byCurrency: [MarketplaceByCurrency!]! @derivedFrom(field: "marketplace") 179 | } 180 | 181 | type MarketplaceByCurrency @entity { 182 | " { Marketplace }-{ Currency } " 183 | id: ID! 184 | 185 | " Trade currency " 186 | currency: Bytes! 187 | 188 | " Total number of unique users " 189 | users: BigInt! 190 | 191 | " Total number of collections traded " 192 | collections: BigInt! 193 | 194 | " Total number of transactions " 195 | transactions: BigInt! 196 | 197 | " Total volume " 198 | volume: BigDecimal! 199 | 200 | " Marketplace daily data " 201 | dailyData: [MarketplaceDailyDataByCurrency!]! @derivedFrom(field: "marketplaceByCurrency") 202 | 203 | " Marketplace " 204 | marketplace: Marketplace! 205 | } 206 | 207 | type MarketplaceDailyData @entity { 208 | " { Marketplace }-{ Date } " 209 | id: ID! 210 | 211 | " Pointer to Marketplace " 212 | marketplace: Marketplace! 213 | 214 | " Date " 215 | date: BigInt! 216 | 217 | " Total number of unique users " 218 | users: BigInt! 219 | 220 | " Total number of collections traded " 221 | collections: BigInt! 222 | 223 | " Total number of transactions " 224 | transactions: BigInt! 225 | 226 | " Marketplace by currency " 227 | byCurrency: [MarketplaceDailyDataByCurrency!]! @derivedFrom(field: "marketplaceDailyData") 228 | } 229 | 230 | type MarketplaceDailyDataByCurrency @entity { 231 | " { Marketplace }-{ Currency }-{ Date } " 232 | id: ID! 233 | 234 | " Pointer to MarketplaceByCurrency " 235 | marketplaceByCurrency: MarketplaceByCurrency! 236 | 237 | " Pointer to MarketplaceDailyData " 238 | marketplaceDailyData: MarketplaceDailyData! 239 | 240 | " Trade currency " 241 | currency: Bytes! 242 | 243 | " Date " 244 | date: BigInt! 245 | 246 | " Total number of unique users " 247 | users: BigInt! 248 | 249 | " Total number of collections traded " 250 | collections: BigInt! 251 | 252 | " Transactions " 253 | transactions: [Transaction!]! @derivedFrom(field: "marketplaceDailyDataByCurrency") 254 | 255 | " Total volume " 256 | volume: BigDecimal! 257 | } 258 | 259 | type User @entity { 260 | " { Address } " 261 | id: ID! 262 | 263 | " Total number of transactions " 264 | transactions: BigInt! 265 | 266 | " User daily data " 267 | dailyData: [UserDailyData!]! @derivedFrom(field: "user") 268 | 269 | " User by currency " 270 | byCurrency: [UserByCurrency!]! @derivedFrom(field: "user") 271 | } 272 | 273 | type UserByCurrency @entity { 274 | " { User }-{ Currency } " 275 | id: ID! 276 | 277 | " Trade currency " 278 | currency: Bytes! 279 | 280 | " Total number of transactions " 281 | transactions: BigInt! 282 | 283 | " Total volume " 284 | volume: BigDecimal! 285 | 286 | " User daily data " 287 | dailyData: [UserDailyDataByCurrency!]! @derivedFrom(field: "userByCurrency") 288 | 289 | " Pointer to User " 290 | user: User! 291 | } 292 | 293 | type UserDailyData @entity { 294 | " { User }-{ Date } " 295 | id: ID! 296 | 297 | " Pointer to User " 298 | user: User! 299 | 300 | " Date " 301 | date: BigInt! 302 | 303 | " Total number of transactions " 304 | transactions: BigInt! 305 | 306 | " User daily data by currency " 307 | byCurrency: [UserDailyDataByCurrency!]! @derivedFrom(field: "userDailyData") 308 | } 309 | 310 | type UserDailyDataByCurrency @entity { 311 | " { User }-{ Currency }-{ Date } " 312 | id: ID! 313 | 314 | " Pointer to UserByCurrency " 315 | userByCurrency: UserByCurrency! 316 | 317 | " Pointer to UserDailyData " 318 | userDailyData: UserDailyData! 319 | 320 | " Trade currency " 321 | currency: Bytes! 322 | 323 | " Date " 324 | date: BigInt! 325 | 326 | " Transactions " 327 | transactions: [Transaction!]! @derivedFrom(field: "userDailyDataByCurrency") 328 | 329 | " Total volume " 330 | volume: BigDecimal! 331 | } 332 | 333 | type Transaction @entity { 334 | " { Transaction hash }-{ Log index }-{ (optional) ID within bundle } " 335 | id: ID! 336 | 337 | " Event transaction hash. " 338 | transactionHash: String! 339 | 340 | " Event log index. " 341 | logIndex: Int! 342 | 343 | " Block timestamp where the trade is executed. " 344 | timestamp: BigInt! 345 | 346 | " Block number where the trade is executed. " 347 | blockNumber: BigInt! 348 | 349 | " Whether the trade is in a bundle. " 350 | isBundle: Boolean! 351 | 352 | " Pointer to collection " 353 | collection: Collection! 354 | 355 | " Token ID of the traded NFT. " 356 | tokenId: BigInt! 357 | 358 | " The amount of token to transfer. It should be 1 except for ERC1155 batch. " 359 | amount: BigInt! 360 | 361 | " Price. If only 1 tokenId is involved, then the price is determined by the token only. If the trade is incurred by a batch purchasing, then the price is the average price in the batch. " 362 | price: BigDecimal! 363 | 364 | " Trade currency " 365 | currency: Bytes! 366 | 367 | " Buyer account (Pointer to User) " 368 | buyer: User! 369 | 370 | " Seller account address " 371 | seller: Bytes! 372 | 373 | " Pointer to AggregatorDailyDataByCurrency " 374 | aggregatorDailyDataByCurrency: AggregatorDailyDataByCurrency! 375 | 376 | " Pointer to CollectionDailyDataByCurrency " 377 | collectionDailyDataByCurrency: CollectionDailyDataByCurrency! 378 | 379 | " Pointer to MarketplaceDailyDataByCurrency " 380 | marketplaceDailyDataByCurrency: MarketplaceDailyDataByCurrency! 381 | 382 | " Pointer to UserDailyDataByCurrency " 383 | userDailyDataByCurrency: UserDailyDataByCurrency! 384 | } 385 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/LooksRareV1/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-non-null-assertion */ 2 | import { ONE_BI, ONE_DAY_BI, ZERO_BI } from "../../../../helpers/constants"; 3 | import { TakerBid } from "../../generated/LooksRareV1/LooksRareExchange"; 4 | import { Collection, CollectionDailyData, Transaction, User, UserDailyData } from "../../generated/schema"; 5 | import { extractOriginator } from "../utils/extractOriginator"; 6 | import { findSweepEventFromLogs } from "../utils/findSweepEventFromLogs"; 7 | import { getOrInitializeAggregator } from "../utils/getOrInitializeAggregator"; 8 | import { getOrInitializeAggregatorByCurrency } from "../utils/getOrInitializeAggregatorByCurrency"; 9 | import { getOrInitializeAggregatorDailyData } from "../utils/getOrInitializeAggregatorDailyData"; 10 | import { getOrInitializeAggregatorDailyDataByCurrency } from "../utils/getOrInitializeAggregatorDailyDataByCurrency"; 11 | import { getOrInitializeCollectionByCurrency } from "../utils/getOrInitializeCollectionByCurrency"; 12 | import { getOrInitializeCollectionDailyDataByCurrency } from "../utils/getOrInitializeCollectionDailyDataByCurrency"; 13 | import { getOrInitializeMarketplace } from "../utils/getOrInitializeMarketplace"; 14 | import { getOrInitializeMarketplaceByCurrency } from "../utils/getOrInitializeMarketplaceByCurrency"; 15 | import { getOrInitializeMarketplaceDailyData } from "../utils/getOrInitializeMarketplaceDailyData"; 16 | import { getOrInitializeMarketplaceDailyDataByCurrency } from "../utils/getOrInitializeMarketplaceDailyDataByCurrency"; 17 | import { getOrInitializeUserByCurrency } from "../utils/getOrInitializeUserByCurrency"; 18 | import { getOrInitializeUserDailyDataByCurrency } from "../utils/getOrInitializeUserDailyDataByCurrency"; 19 | 20 | export function handleTakerBid(event: TakerBid): void { 21 | const logs = event.receipt!.logs; 22 | 23 | const sweepEvent = findSweepEventFromLogs(logs); 24 | if (!sweepEvent) return; 25 | if (sweepEvent.transactionLogIndex >= event.transactionLogIndex) return; 26 | 27 | const currency = event.params.currency; 28 | const price = event.params.price.toBigDecimal(); 29 | const collectionAddress = event.params.collection.toHexString(); 30 | const dayID = event.block.timestamp.div(ONE_DAY_BI); 31 | 32 | // 1. Aggregator 33 | const aggregator = getOrInitializeAggregator(); 34 | aggregator.transactions = aggregator.transactions.plus(ONE_BI); 35 | 36 | // 2. Aggregator by currency 37 | const aggregatorByCurrency = getOrInitializeAggregatorByCurrency(aggregator, currency); 38 | aggregatorByCurrency.volume = aggregatorByCurrency.volume.plus(price); 39 | aggregatorByCurrency.transactions = aggregatorByCurrency.transactions.plus(ONE_BI); 40 | 41 | // 3. Aggregator daily data 42 | const aggregatorDailyData = getOrInitializeAggregatorDailyData(dayID, aggregator); 43 | aggregatorDailyData.transactions = aggregatorDailyData.transactions.plus(ONE_BI); 44 | 45 | // 4. Aggregator daily data by currency 46 | const aggregatorDailyDataByCurrency = getOrInitializeAggregatorDailyDataByCurrency( 47 | aggregatorByCurrency, 48 | aggregatorDailyData, 49 | dayID, 50 | ); 51 | aggregatorDailyDataByCurrency.volume = aggregatorDailyDataByCurrency.volume.plus(price); 52 | 53 | // 5. Marketplace 54 | const marketplace = getOrInitializeMarketplace("LooksRareV1"); 55 | marketplace.transactions = marketplace.transactions.plus(ONE_BI); 56 | 57 | // 6. Marketplace by currency 58 | const marketplaceByCurrency = getOrInitializeMarketplaceByCurrency(marketplace, currency); 59 | marketplaceByCurrency.volume = marketplaceByCurrency.volume.plus(price); 60 | marketplaceByCurrency.transactions = marketplaceByCurrency.transactions.plus(ONE_BI); 61 | 62 | // 7. Marketplace daily data 63 | const marketplaceDailyData = getOrInitializeMarketplaceDailyData(currency, marketplace, dayID); 64 | marketplaceDailyData.transactions = marketplaceDailyData.transactions.plus(ONE_BI); 65 | 66 | // 8. Marketplace daily data by currency 67 | const marketplaceDailyDataByCurrency = getOrInitializeMarketplaceDailyDataByCurrency( 68 | marketplaceDailyData, 69 | marketplaceByCurrency, 70 | dayID, 71 | ); 72 | marketplaceDailyDataByCurrency.volume = marketplaceDailyDataByCurrency.volume.plus(price); 73 | 74 | // 9. User 75 | const originator = extractOriginator(sweepEvent); 76 | const userID = originator.toHexString(); 77 | let user = User.load(userID); 78 | if (!user) { 79 | user = new User(userID); 80 | user.transactions = ZERO_BI; 81 | 82 | // New aggregator/marketplace user 83 | aggregator.users = aggregator.users.plus(ONE_BI); 84 | aggregatorByCurrency.users = aggregatorByCurrency.users.plus(ONE_BI); 85 | marketplace.users = marketplace.users.plus(ONE_BI); 86 | marketplaceByCurrency.users = marketplaceByCurrency.users.plus(ONE_BI); 87 | } 88 | user.transactions = user.transactions.plus(ONE_BI); 89 | 90 | // 10. User by currency 91 | const userByCurrency = getOrInitializeUserByCurrency(user, currency); 92 | userByCurrency.volume = userByCurrency.volume.plus(price); 93 | userByCurrency.transactions = userByCurrency.transactions.plus(ONE_BI); 94 | 95 | // 11. User daily data 96 | const userDailyDataID = `${userID}-${dayID.toString()}`; 97 | let userDailyData = UserDailyData.load(userDailyDataID); 98 | if (!userDailyData) { 99 | userDailyData = new UserDailyData(userDailyDataID); 100 | const dayStartTimestamp = dayID.times(ONE_DAY_BI); 101 | userDailyData.date = dayStartTimestamp; 102 | userDailyData.transactions = ZERO_BI; 103 | userDailyData.user = userID; 104 | 105 | // New aggregator/marketplace user for the day 106 | aggregatorDailyData.users = aggregatorDailyData.users.plus(ONE_BI); 107 | aggregatorDailyDataByCurrency.users = aggregatorDailyDataByCurrency.users.plus(ONE_BI); 108 | marketplaceDailyData.users = marketplaceDailyData.users.plus(ONE_BI); 109 | marketplaceDailyDataByCurrency.users = marketplaceDailyDataByCurrency.users.plus(ONE_BI); 110 | } 111 | userDailyData.transactions = userDailyData.transactions.plus(ONE_BI); 112 | 113 | // 12. User daily data by currency 114 | const userDailyDataByCurrency = getOrInitializeUserDailyDataByCurrency(userDailyData, userByCurrency, dayID); 115 | userDailyDataByCurrency.volume = userDailyDataByCurrency.volume.plus(price); 116 | 117 | // 13. Collection 118 | let collection = Collection.load(collectionAddress); 119 | if (!collection) { 120 | collection = new Collection(collectionAddress); 121 | collection.transactions = ZERO_BI; 122 | 123 | // New aggregator/marketplace user 124 | aggregator.collections = aggregator.collections.plus(ONE_BI); 125 | aggregatorByCurrency.collections = aggregatorByCurrency.collections.plus(ONE_BI); 126 | marketplace.collections = marketplace.collections.plus(ONE_BI); 127 | marketplaceByCurrency.collections = marketplaceByCurrency.collections.plus(ONE_BI); 128 | } 129 | collection.transactions = collection.transactions.plus(ONE_BI); 130 | 131 | // 14. Collection by currency 132 | const collectionByCurrency = getOrInitializeCollectionByCurrency(event.params.collection, currency); 133 | collectionByCurrency.volume = collectionByCurrency.volume.plus(price); 134 | collectionByCurrency.transactions = collectionByCurrency.transactions.plus(ONE_BI); 135 | 136 | // 15. Collection daily data 137 | const collectionDailyDataID = `${collection.id}-${dayID.toString()}`; 138 | let collectionDailyData = CollectionDailyData.load(collectionDailyDataID); 139 | if (!collectionDailyData) { 140 | collectionDailyData = new CollectionDailyData(collectionDailyDataID); 141 | const dayStartTimestamp = dayID.times(ONE_DAY_BI); 142 | collectionDailyData.date = dayStartTimestamp; 143 | collectionDailyData.transactions = ZERO_BI; 144 | collectionDailyData.collection = collection.id; 145 | 146 | // New aggregator/marketplace collection for the day 147 | marketplaceDailyData.collections = marketplaceDailyData.collections.plus(ONE_BI); 148 | marketplaceDailyDataByCurrency.collections = marketplaceDailyDataByCurrency.collections.plus(ONE_BI); 149 | aggregatorDailyData.collections = aggregatorDailyData.collections.plus(ONE_BI); 150 | aggregatorDailyDataByCurrency.collections = aggregatorDailyDataByCurrency.collections.plus(ONE_BI); 151 | } 152 | collectionDailyData.transactions = collectionDailyData.transactions.plus(ONE_BI); 153 | 154 | // 16. Collection daily data by currency 155 | const collectionDailyDataByCurrency = getOrInitializeCollectionDailyDataByCurrency(collectionByCurrency, dayID); 156 | collectionDailyDataByCurrency.volume = collectionDailyDataByCurrency.volume.plus(price); 157 | 158 | const transactionHash = event.transaction.hash.toHexString(); 159 | const logIndex = event.logIndex; 160 | const block = event.block; 161 | const transactionID = `${transactionHash}-${logIndex.toString()}`; 162 | 163 | // 17. Transaction 164 | const transaction = new Transaction(transactionID); 165 | transaction.transactionHash = transactionHash; 166 | transaction.logIndex = logIndex.toI32(); 167 | transaction.timestamp = block.timestamp; 168 | transaction.blockNumber = block.number; 169 | transaction.isBundle = false; 170 | transaction.collection = collection.id; 171 | transaction.tokenId = event.params.tokenId; 172 | transaction.price = price; 173 | transaction.currency = currency; 174 | transaction.amount = ONE_BI; 175 | transaction.buyer = user.id; 176 | transaction.seller = event.params.maker; 177 | transaction.aggregatorDailyDataByCurrency = aggregatorDailyDataByCurrency.id; 178 | transaction.collectionDailyDataByCurrency = collectionDailyDataByCurrency.id; 179 | transaction.marketplaceDailyDataByCurrency = marketplaceDailyDataByCurrency.id; 180 | transaction.userDailyDataByCurrency = userDailyDataByCurrency.id; 181 | 182 | aggregator.save(); 183 | aggregatorByCurrency.save(); 184 | aggregatorDailyData.save(); 185 | aggregatorDailyDataByCurrency.save(); 186 | marketplace.save(); 187 | marketplaceByCurrency.save(); 188 | marketplaceDailyData.save(); 189 | marketplaceDailyDataByCurrency.save(); 190 | user.save(); 191 | userByCurrency.save(); 192 | userDailyData.save(); 193 | userDailyDataByCurrency.save(); 194 | collection.save(); 195 | collectionByCurrency.save(); 196 | collectionDailyData.save(); 197 | collectionDailyDataByCurrency.save(); 198 | transaction.save(); 199 | } 200 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/Seaport/calculateVolume.ts: -------------------------------------------------------------------------------- 1 | import { BigDecimal } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BD } from "../../../../../helpers/constants"; 3 | import { OrderFulfilledConsiderationStruct } from "../../../generated/Seaport/Seaport"; 4 | 5 | export function calculateVolume(consideration: OrderFulfilledConsiderationStruct[]): BigDecimal { 6 | let volume = ZERO_BD; 7 | for (let i = 0; i < consideration.length; i++) { 8 | volume = volume.plus(consideration[i].amount.toBigDecimal()); 9 | } 10 | return volume; 11 | } 12 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/Seaport/isSameConsiderationToken.ts: -------------------------------------------------------------------------------- 1 | import { OrderFulfilledConsiderationStruct } from "../../../generated/Seaport/Seaport"; 2 | 3 | export function isSameConsiderationToken(consideration: OrderFulfilledConsiderationStruct[]): boolean { 4 | const currency = consideration[0].token; 5 | const itemType = consideration[0].itemType; 6 | for (let i = 0; i < consideration.length; i++) { 7 | const receivedItem = consideration[i]; 8 | if (receivedItem.token != currency || receivedItem.itemType !== itemType) { 9 | return false; 10 | } 11 | } 12 | 13 | return true; 14 | } 15 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/Seaport/isSameOfferToken.ts: -------------------------------------------------------------------------------- 1 | import { OrderFulfilledOfferStruct } from "../../../generated/Seaport/Seaport"; 2 | 3 | export function isSameOfferToken(offer: OrderFulfilledOfferStruct[]): boolean { 4 | const offerToken = offer[0].token; 5 | for (let i = 0; i < offer.length; i++) { 6 | if (offer[i].token != offerToken) { 7 | return false; 8 | } 9 | } 10 | 11 | return true; 12 | } 13 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/extractOriginator.ts: -------------------------------------------------------------------------------- 1 | import { Address, Bytes, ethereum } from "@graphprotocol/graph-ts"; 2 | 3 | export function extractOriginator(sweepEvent: ethereum.Log): Bytes { 4 | return Address.fromHexString(`0x${sweepEvent.topics[1].toHexString().substring(26)}`); 5 | } 6 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/findSweepEventFromLogs.ts: -------------------------------------------------------------------------------- 1 | import { Bytes, ethereum } from "@graphprotocol/graph-ts"; 2 | import { LOOKSRARE_AGGREGATOR, LOOKSRARE_AGGREGATOR_SWEEP_EVENT_TOPIC } from "../../../../helpers/constants"; 3 | 4 | export function findSweepEventFromLogs(logs: ethereum.Log[]): ethereum.Log | null { 5 | const sweepEventIndex = logs.findIndex((log) => { 6 | return ( 7 | log.address == LOOKSRARE_AGGREGATOR && 8 | log.topics[0] == Bytes.fromHexString(LOOKSRARE_AGGREGATOR_SWEEP_EVENT_TOPIC) 9 | ); 10 | }); 11 | if (sweepEventIndex === -1) return null; 12 | return logs[sweepEventIndex]; 13 | } 14 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeAggregator.ts: -------------------------------------------------------------------------------- 1 | import { ZERO_BI } from "../../../../helpers/constants"; 2 | import { Aggregator } from "../../generated/schema"; 3 | 4 | export function getOrInitializeAggregator(): Aggregator { 5 | const aggregatorID = "LooksRareAggregator"; 6 | let aggregator = Aggregator.load(aggregatorID); 7 | if (!aggregator) { 8 | aggregator = new Aggregator(aggregatorID); 9 | aggregator.collections = ZERO_BI; 10 | aggregator.transactions = ZERO_BI; 11 | aggregator.users = ZERO_BI; 12 | } 13 | return aggregator; 14 | } 15 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeAggregatorByCurrency.ts: -------------------------------------------------------------------------------- 1 | import { Bytes } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BD, ZERO_BI } from "../../../../helpers/constants"; 3 | import { Aggregator, AggregatorByCurrency } from "../../generated/schema"; 4 | 5 | export function getOrInitializeAggregatorByCurrency(aggregator: Aggregator, currency: Bytes): AggregatorByCurrency { 6 | const aggregatorByCurrencyID = currency.toHexString(); 7 | let aggregatorByCurrency = AggregatorByCurrency.load(aggregatorByCurrencyID); 8 | if (!aggregatorByCurrency) { 9 | aggregatorByCurrency = new AggregatorByCurrency(aggregatorByCurrencyID); 10 | aggregatorByCurrency.aggregator = aggregator.id; 11 | aggregatorByCurrency.currency = currency; 12 | aggregatorByCurrency.volume = ZERO_BD; 13 | aggregatorByCurrency.collections = ZERO_BI; 14 | aggregatorByCurrency.transactions = ZERO_BI; 15 | aggregatorByCurrency.users = ZERO_BI; 16 | } 17 | return aggregatorByCurrency; 18 | } 19 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeAggregatorDailyData.ts: -------------------------------------------------------------------------------- 1 | import { BigInt } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BI, ONE_DAY_BI } from "../../../../helpers/constants"; 3 | import { Aggregator, AggregatorDailyData } from "../../generated/schema"; 4 | 5 | export function getOrInitializeAggregatorDailyData(dayID: BigInt, aggregator: Aggregator): AggregatorDailyData { 6 | const aggregatorDailyDataID = dayID.toString(); 7 | let aggregatorDailyData = AggregatorDailyData.load(aggregatorDailyDataID); 8 | if (!aggregatorDailyData) { 9 | aggregatorDailyData = new AggregatorDailyData(aggregatorDailyDataID); 10 | const dayStartTimestamp = dayID.times(ONE_DAY_BI); 11 | aggregatorDailyData.date = dayStartTimestamp; 12 | aggregatorDailyData.collections = ZERO_BI; 13 | aggregatorDailyData.transactions = ZERO_BI; 14 | aggregatorDailyData.users = ZERO_BI; 15 | aggregatorDailyData.aggregator = aggregator.id; 16 | } 17 | return aggregatorDailyData; 18 | } 19 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeAggregatorDailyDataByCurrency.ts: -------------------------------------------------------------------------------- 1 | import { BigInt } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BD, ZERO_BI, ONE_DAY_BI } from "../../../../helpers/constants"; 3 | import { AggregatorByCurrency, AggregatorDailyData, AggregatorDailyDataByCurrency } from "../../generated/schema"; 4 | 5 | export function getOrInitializeAggregatorDailyDataByCurrency( 6 | aggregatorByCurrency: AggregatorByCurrency, 7 | aggregatorDailyData: AggregatorDailyData, 8 | dayID: BigInt, 9 | ): AggregatorDailyDataByCurrency { 10 | const aggregatorDailyDataByCurrencyID = `${aggregatorByCurrency.id}-${dayID.toString()}`; 11 | let aggregatorDailyDataByCurrency = AggregatorDailyDataByCurrency.load(aggregatorDailyDataByCurrencyID); 12 | if (!aggregatorDailyDataByCurrency) { 13 | aggregatorDailyDataByCurrency = new AggregatorDailyDataByCurrency(aggregatorDailyDataByCurrencyID); 14 | aggregatorDailyDataByCurrency.currency = aggregatorByCurrency.currency; 15 | const dayStartTimestamp = dayID.times(ONE_DAY_BI); 16 | aggregatorDailyDataByCurrency.date = dayStartTimestamp; 17 | aggregatorDailyDataByCurrency.volume = ZERO_BD; 18 | aggregatorDailyDataByCurrency.collections = ZERO_BI; 19 | aggregatorDailyDataByCurrency.users = ZERO_BI; 20 | aggregatorDailyDataByCurrency.aggregatorByCurrency = aggregatorByCurrency.id; 21 | aggregatorDailyDataByCurrency.aggregatorDailyData = aggregatorDailyData.id; 22 | } 23 | return aggregatorDailyDataByCurrency; 24 | } 25 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeCollectionByCurrency.ts: -------------------------------------------------------------------------------- 1 | import { Bytes } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BD, ZERO_BI } from "../../../../helpers/constants"; 3 | import { CollectionByCurrency } from "../../generated/schema"; 4 | 5 | export function getOrInitializeCollectionByCurrency(collection: Bytes, currency: Bytes): CollectionByCurrency { 6 | const collectionByCurrencyID = `${collection.toHexString()}-${currency.toHexString()}`; 7 | let collectionByCurrency = CollectionByCurrency.load(collectionByCurrencyID); 8 | if (!collectionByCurrency) { 9 | collectionByCurrency = new CollectionByCurrency(collectionByCurrencyID); 10 | collectionByCurrency.currency = currency; 11 | collectionByCurrency.volume = ZERO_BD; 12 | collectionByCurrency.transactions = ZERO_BI; 13 | } 14 | return collectionByCurrency; 15 | } 16 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeCollectionDailyDataByCurrency.ts: -------------------------------------------------------------------------------- 1 | import { BigInt } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BD, ONE_DAY_BI } from "../../../../helpers/constants"; 3 | import { CollectionByCurrency, CollectionDailyDataByCurrency } from "../../generated/schema"; 4 | 5 | export function getOrInitializeCollectionDailyDataByCurrency( 6 | collectionByCurrency: CollectionByCurrency, 7 | dayID: BigInt, 8 | ): CollectionDailyDataByCurrency { 9 | const collectionDailyDataByCurrencyID = `${collectionByCurrency.id}-${dayID.toString()}`; 10 | let collectionDailyDataByCurrency = CollectionDailyDataByCurrency.load(collectionDailyDataByCurrencyID); 11 | if (!collectionDailyDataByCurrency) { 12 | collectionDailyDataByCurrency = new CollectionDailyDataByCurrency(collectionDailyDataByCurrencyID); 13 | const dayStartTimestamp = dayID.times(ONE_DAY_BI); 14 | collectionDailyDataByCurrency.date = dayStartTimestamp; 15 | collectionDailyDataByCurrency.currency = collectionByCurrency.currency; 16 | collectionDailyDataByCurrency.volume = ZERO_BD; 17 | collectionDailyDataByCurrency.collectionByCurrency = collectionByCurrency.id; 18 | } 19 | return collectionDailyDataByCurrency; 20 | } 21 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeMarketplace.ts: -------------------------------------------------------------------------------- 1 | import { ZERO_BI } from "../../../../helpers/constants"; 2 | import { Marketplace } from "../../generated/schema"; 3 | 4 | export function getOrInitializeMarketplace(id: string): Marketplace { 5 | let marketplace = Marketplace.load(id); 6 | if (!marketplace) { 7 | marketplace = new Marketplace(id); 8 | marketplace.collections = ZERO_BI; 9 | marketplace.transactions = ZERO_BI; 10 | marketplace.users = ZERO_BI; 11 | } 12 | return marketplace; 13 | } 14 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeMarketplaceByCurrency.ts: -------------------------------------------------------------------------------- 1 | import { Bytes } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BD, ZERO_BI } from "../../../../helpers/constants"; 3 | import { Marketplace, MarketplaceByCurrency } from "../../generated/schema"; 4 | 5 | export function getOrInitializeMarketplaceByCurrency(marketplace: Marketplace, currency: Bytes): MarketplaceByCurrency { 6 | const ID = `${marketplace.id}-${currency.toHexString()}`; 7 | let marketplaceByCurrency = MarketplaceByCurrency.load(ID); 8 | if (!marketplaceByCurrency) { 9 | marketplaceByCurrency = new MarketplaceByCurrency(ID); 10 | marketplaceByCurrency.currency = currency; 11 | marketplaceByCurrency.volume = ZERO_BD; 12 | marketplaceByCurrency.collections = ZERO_BI; 13 | marketplaceByCurrency.transactions = ZERO_BI; 14 | marketplaceByCurrency.users = ZERO_BI; 15 | marketplaceByCurrency.marketplace = marketplace.id; 16 | } 17 | return marketplaceByCurrency; 18 | } 19 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeMarketplaceDailyData.ts: -------------------------------------------------------------------------------- 1 | import { BigInt, Bytes } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BI, ONE_DAY_BI } from "../../../../helpers/constants"; 3 | import { Marketplace, MarketplaceDailyData } from "../../generated/schema"; 4 | 5 | export function getOrInitializeMarketplaceDailyData( 6 | currency: Bytes, 7 | marketplace: Marketplace, 8 | dayID: BigInt, 9 | ): MarketplaceDailyData { 10 | const marketplaceDailyDataID = `${marketplace.id}-${dayID.toString()}`; 11 | let marketplaceDailyData = MarketplaceDailyData.load(marketplaceDailyDataID); 12 | if (!marketplaceDailyData) { 13 | marketplaceDailyData = new MarketplaceDailyData(marketplaceDailyDataID); 14 | const dayStartTimestamp = dayID.times(ONE_DAY_BI); 15 | marketplaceDailyData.date = dayStartTimestamp; 16 | marketplaceDailyData.marketplace = marketplace.id; 17 | marketplaceDailyData.collections = ZERO_BI; 18 | marketplaceDailyData.transactions = ZERO_BI; 19 | marketplaceDailyData.users = ZERO_BI; 20 | } 21 | return marketplaceDailyData; 22 | } 23 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeMarketplaceDailyDataByCurrency.ts: -------------------------------------------------------------------------------- 1 | import { BigInt } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BD, ZERO_BI, ONE_DAY_BI } from "../../../../helpers/constants"; 3 | import { MarketplaceByCurrency, MarketplaceDailyData, MarketplaceDailyDataByCurrency } from "../../generated/schema"; 4 | 5 | export function getOrInitializeMarketplaceDailyDataByCurrency( 6 | marketplaceDailyData: MarketplaceDailyData, 7 | marketplaceByCurrency: MarketplaceByCurrency, 8 | dayID: BigInt, 9 | ): MarketplaceDailyDataByCurrency { 10 | const ID = `${marketplaceByCurrency.id}-${dayID.toString()}`; 11 | let marketplaceDailyDataByCurrency = MarketplaceDailyDataByCurrency.load(ID); 12 | if (!marketplaceDailyDataByCurrency) { 13 | marketplaceDailyDataByCurrency = new MarketplaceDailyDataByCurrency(ID); 14 | marketplaceDailyDataByCurrency.currency = marketplaceByCurrency.currency; 15 | const dayStartTimestamp = dayID.times(ONE_DAY_BI); 16 | marketplaceDailyDataByCurrency.date = dayStartTimestamp; 17 | marketplaceDailyDataByCurrency.marketplaceByCurrency = marketplaceByCurrency.id; 18 | marketplaceDailyDataByCurrency.volume = ZERO_BD; 19 | marketplaceDailyDataByCurrency.collections = ZERO_BI; 20 | marketplaceDailyDataByCurrency.users = ZERO_BI; 21 | marketplaceDailyDataByCurrency.marketplaceDailyData = marketplaceDailyData.id; 22 | } 23 | return marketplaceDailyDataByCurrency; 24 | } 25 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeUserByCurrency.ts: -------------------------------------------------------------------------------- 1 | import { Bytes } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BD, ZERO_BI } from "../../../../helpers/constants"; 3 | import { User, UserByCurrency } from "../../generated/schema"; 4 | 5 | export function getOrInitializeUserByCurrency(user: User, currency: Bytes): UserByCurrency { 6 | const userByCurrencyID = `${user.id}-${currency.toHexString()}`; 7 | let userByCurrency = UserByCurrency.load(userByCurrencyID); 8 | if (!userByCurrency) { 9 | userByCurrency = new UserByCurrency(userByCurrencyID); 10 | userByCurrency.currency = currency; 11 | userByCurrency.volume = ZERO_BD; 12 | userByCurrency.transactions = ZERO_BI; 13 | userByCurrency.user = user.id; 14 | } 15 | return userByCurrency; 16 | } 17 | -------------------------------------------------------------------------------- /subgraphs/aggregator/src/utils/getOrInitializeUserDailyDataByCurrency.ts: -------------------------------------------------------------------------------- 1 | import { BigInt } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BD, ONE_DAY_BI } from "../../../../helpers/constants"; 3 | import { UserByCurrency, UserDailyData, UserDailyDataByCurrency } from "../../generated/schema"; 4 | 5 | export function getOrInitializeUserDailyDataByCurrency( 6 | userDailyData: UserDailyData, 7 | userByCurrency: UserByCurrency, 8 | dayID: BigInt, 9 | ): UserDailyDataByCurrency { 10 | const userDailyDataByCurrencyID = `${userByCurrency.id}-${dayID.toString()}`; 11 | let userDailyDataByCurrency = UserDailyDataByCurrency.load(userDailyDataByCurrencyID); 12 | if (!userDailyDataByCurrency) { 13 | userDailyDataByCurrency = new UserDailyDataByCurrency(userDailyDataByCurrencyID); 14 | const dayStartTimestamp = dayID.times(ONE_DAY_BI); 15 | userDailyDataByCurrency.date = dayStartTimestamp; 16 | userDailyDataByCurrency.currency = userByCurrency.currency; 17 | userDailyDataByCurrency.volume = ZERO_BD; 18 | userDailyDataByCurrency.userByCurrency = userByCurrency.id; 19 | userDailyDataByCurrency.userDailyData = userDailyData.id; 20 | } 21 | return userDailyDataByCurrency; 22 | } 23 | -------------------------------------------------------------------------------- /subgraphs/aggregator/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | schema: 3 | file: ./schema.graphql 4 | dataSources: 5 | - kind: ethereum 6 | name: Seaport 7 | network: mainnet 8 | source: 9 | abi: Seaport 10 | address: "0x00000000000001ad428e4906aE43D8F9852d0dD6" 11 | mapping: 12 | kind: ethereum/events 13 | apiVersion: 0.0.7 14 | language: wasm/assemblyscript 15 | entities: 16 | - OrderFulfilled 17 | abis: 18 | - name: Seaport 19 | file: ./abis/Seaport.json 20 | eventHandlers: 21 | - event: OrderFulfilled(bytes32,indexed address,indexed 22 | address,address,(uint8,address,uint256,uint256)[],(uint8,address,uint256,uint256,address)[]) 23 | handler: handleOrderFulfilled 24 | file: ./src/Seaport/index.ts 25 | - kind: ethereum 26 | name: LooksRareV1 27 | network: mainnet 28 | source: 29 | abi: LooksRareExchange 30 | address: "0x59728544B08AB483533076417FbBB2fD0B17CE3a" 31 | mapping: 32 | kind: ethereum/events 33 | apiVersion: 0.0.7 34 | language: wasm/assemblyscript 35 | entities: 36 | - TakerBid 37 | abis: 38 | - name: LooksRareExchange 39 | file: ./abis/LooksRareExchange.json 40 | eventHandlers: 41 | - event: TakerBid(bytes32,uint256,indexed address,indexed address,indexed 42 | address,address,address,uint256,uint256,uint256) 43 | handler: handleTakerBid 44 | file: ./src/LooksRareV1/index.ts 45 | -------------------------------------------------------------------------------- /subgraphs/aggregator/tests/helpers/utils.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt, Bytes, ethereum, Wrapped } from "@graphprotocol/graph-ts"; 2 | import { newMockEvent } from "matchstick-as"; 3 | import { TakerBid } from "../../generated/LooksRareV1/LooksRareExchange"; 4 | import { OrderFulfilled } from "../../generated/Seaport/Seaport"; 5 | 6 | const stringEventParam = (key: string, value: string): ethereum.EventParam => 7 | new ethereum.EventParam(key, ethereum.Value.fromString(value)); 8 | 9 | const addressEventParam = (key: string, value: string): ethereum.EventParam => 10 | new ethereum.EventParam(key, ethereum.Value.fromAddress(Address.fromString(value))); 11 | 12 | const uintEventParam = (key: string, value: i32): ethereum.EventParam => 13 | new ethereum.EventParam(key, ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(value))); 14 | 15 | export function createOrderFulfilledEvent( 16 | orderHash: string, 17 | offerer: string, 18 | zone: string, 19 | recipient: string, 20 | offerItemTypes: Array, 21 | offerTokens: Array, 22 | offerIdentifiers: Array, 23 | offerAmounts: Array, 24 | considerationItemTypes: Array, 25 | considerationTokens: Array, 26 | considerationIdentifiers: Array, 27 | considerationAmounts: Array, 28 | considerationRecipients: Array, 29 | timestamp: i32, 30 | ): OrderFulfilled { 31 | const mockEvent = newMockEvent(); 32 | const orderFulfilledEvent = new OrderFulfilled( 33 | mockEvent.address, 34 | mockEvent.logIndex, 35 | mockEvent.transactionLogIndex, 36 | mockEvent.logType, 37 | mockEvent.block, 38 | mockEvent.transaction, 39 | [], 40 | mockEvent.receipt, 41 | ); 42 | 43 | orderFulfilledEvent.block.timestamp = BigInt.fromI32(timestamp); 44 | 45 | const orderHashParam = stringEventParam("orderHash", orderHash); 46 | const offererParam = addressEventParam("offerer", offerer); 47 | const zoneParam = addressEventParam("zone", zone); 48 | const recipientParam = addressEventParam("recipient", recipient); 49 | 50 | const offerTupleArray: Array = []; 51 | 52 | for (let i = 0; i < offerAmounts.length; i++) { 53 | const offer: Array = [ 54 | ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(offerItemTypes[i])), 55 | ethereum.Value.fromAddress(Address.fromString(offerTokens[i])), 56 | ethereum.Value.fromI32(offerIdentifiers[i]), 57 | ethereum.Value.fromI32(offerAmounts[i]), 58 | ]; 59 | 60 | const offerTuple = changetype(offer); 61 | offerTupleArray.push(offerTuple); 62 | } 63 | 64 | const offerParam = new ethereum.EventParam("offer", ethereum.Value.fromTupleArray(offerTupleArray)); 65 | 66 | const considerationTupleArray: Array = []; 67 | 68 | for (let i = 0; i < considerationAmounts.length; i++) { 69 | const consideration: Array = [ 70 | ethereum.Value.fromI32(considerationItemTypes[i]), 71 | ethereum.Value.fromAddress(Address.fromString(considerationTokens[i])), 72 | ethereum.Value.fromI32(considerationIdentifiers[i]), 73 | ethereum.Value.fromUnsignedBigInt(BigInt.fromString(considerationAmounts[i])), 74 | ethereum.Value.fromAddress(Address.fromString(considerationRecipients[i])), 75 | ]; 76 | 77 | const considerationTuple = changetype(consideration); 78 | considerationTupleArray.push(considerationTuple); 79 | } 80 | 81 | const considerationParam = new ethereum.EventParam( 82 | "consideration", 83 | ethereum.Value.fromTupleArray(considerationTupleArray), 84 | ); 85 | 86 | orderFulfilledEvent.parameters.push(orderHashParam); 87 | orderFulfilledEvent.parameters.push(offererParam); 88 | orderFulfilledEvent.parameters.push(zoneParam); 89 | orderFulfilledEvent.parameters.push(recipientParam); 90 | orderFulfilledEvent.parameters.push(offerParam); 91 | orderFulfilledEvent.parameters.push(considerationParam); 92 | 93 | return orderFulfilledEvent; 94 | } 95 | 96 | export function createTakerBidEvent( 97 | orderHash: string, 98 | orderNonce: i32, 99 | taker: string, 100 | maker: string, 101 | strategy: string, 102 | currency: string, 103 | collection: string, 104 | tokenId: i32, 105 | price: i64, 106 | timestamp: i32, 107 | ): TakerBid { 108 | const mockEvent = newMockEvent(); 109 | const takerBidEvent = new TakerBid( 110 | mockEvent.address, 111 | mockEvent.logIndex, 112 | mockEvent.transactionLogIndex, 113 | mockEvent.logType, 114 | mockEvent.block, 115 | mockEvent.transaction, 116 | [], 117 | mockEvent.receipt, 118 | ); 119 | 120 | takerBidEvent.block.timestamp = BigInt.fromI32(timestamp); 121 | 122 | const orderHashParam = stringEventParam("orderHash", orderHash); 123 | const orderNonceParam = uintEventParam("orderNonce", orderNonce); 124 | const takerParam = addressEventParam("taker", taker); 125 | const makerParam = addressEventParam("maker", maker); 126 | const strategyParam = addressEventParam("strategy", strategy); 127 | const currencyParam = addressEventParam("currency", currency); 128 | const collectionParam = addressEventParam("collection", collection); 129 | const tokenIdParam = uintEventParam("tokenId", tokenId); 130 | const amountParam = uintEventParam("amount", 1); 131 | const priceParam = new ethereum.EventParam("price", ethereum.Value.fromUnsignedBigInt(BigInt.fromI64(price))); 132 | 133 | takerBidEvent.parameters.push(orderHashParam); 134 | takerBidEvent.parameters.push(orderNonceParam); 135 | takerBidEvent.parameters.push(takerParam); 136 | takerBidEvent.parameters.push(makerParam); 137 | takerBidEvent.parameters.push(strategyParam); 138 | takerBidEvent.parameters.push(currencyParam); 139 | takerBidEvent.parameters.push(collectionParam); 140 | takerBidEvent.parameters.push(tokenIdParam); 141 | takerBidEvent.parameters.push(amountParam); 142 | takerBidEvent.parameters.push(priceParam); 143 | 144 | return takerBidEvent; 145 | } 146 | 147 | export function newLog(address: Address, topics: Array, transactionLogIndex: BigInt): ethereum.Log { 148 | // Copied from https://github.com/LimeChain/matchstick-as/blob/886a3ff95bc760ccacec06d23c577d332ae03e4d/assembly/defaults.ts#L35 149 | const defaultAddress = Address.fromString("0xA16081F360e3847006dB660bae1c6d1b2e17eC2A"); 150 | const defaultAddressBytes = defaultAddress as Bytes; 151 | const defaultBigInt = BigInt.fromI32(1); 152 | const defaultIntBytes = Bytes.fromI32(1); 153 | const defaultEventDataLogType = "default_log_type"; 154 | 155 | return new ethereum.Log( 156 | address, 157 | topics, 158 | defaultAddressBytes, // data 159 | defaultAddressBytes, // blockHash 160 | defaultIntBytes, // blockNumber 161 | defaultAddressBytes, // transactionHash 162 | defaultBigInt, // transactionIndex 163 | defaultBigInt, // logIndex 164 | transactionLogIndex, 165 | defaultEventDataLogType, // logType 166 | new Wrapped(false), // removed 167 | ); 168 | } 169 | -------------------------------------------------------------------------------- /subgraphs/aggregator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /subgraphs/airdrop/abis/ERC20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "name", 6 | "outputs": [ 7 | { 8 | "internalType": "string", 9 | "name": "", 10 | "type": "string" 11 | } 12 | ], 13 | "payable": false, 14 | "stateMutability": "view", 15 | "type": "function" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "symbol", 21 | "outputs": [ 22 | { 23 | "internalType": "string", 24 | "name": "", 25 | "type": "string" 26 | } 27 | ], 28 | "payable": false, 29 | "stateMutability": "view", 30 | "type": "function" 31 | }, 32 | { 33 | "inputs": [], 34 | "name": "decimals", 35 | "outputs": [ 36 | { 37 | "internalType": "uint8", 38 | "name": "", 39 | "type": "uint8" 40 | } 41 | ], 42 | "stateMutability": "view", 43 | "type": "function" 44 | } 45 | ] 46 | -------------------------------------------------------------------------------- /subgraphs/airdrop/mappings/index.ts: -------------------------------------------------------------------------------- 1 | import { BigDecimal, BigInt } from "@graphprotocol/graph-ts"; 2 | import { Balance, Currency, User } from "../generated/schema"; 3 | import { AtomicMatch_Call } from "../generated/WyvernExchange/WyvernExchange"; 4 | import { toBigDecimal } from "../../../helpers/utils"; 5 | import { ZERO_BD, ZERO_BI, ONE_BI, ONE_BD } from "../../../helpers/constants"; 6 | 7 | import { currencies, etherAddresses } from "./utils"; 8 | import { fetchDecimals, fetchName, fetchSymbol } from "./utils/erc20"; 9 | import { getPrice } from "./utils/getPrice"; 10 | 11 | const END_BLOCK = BigInt.fromString("13812868"); 12 | 13 | function initializeUser(user: string, currency: string, timestamp: BigInt): User { 14 | const newUser = new User(user); 15 | newUser.totalBuyTransactions = ZERO_BI; 16 | newUser.totalSellTransactions = ZERO_BI; 17 | newUser.totalVolumeInETH = ZERO_BD; 18 | newUser.firstTradeAt = timestamp; 19 | newUser.firstTradeWith = currency; 20 | newUser.lastTradeAt = ZERO_BI; 21 | newUser.lastTradeWith = ""; 22 | return newUser; 23 | } 24 | 25 | function initializeUserBalance(user: string, currency: string): Balance { 26 | const balance = new Balance(user + "-" + currency); 27 | balance.currency = currency; 28 | balance.user = user; 29 | balance.volume = ZERO_BD; 30 | balance.updatedAt = ZERO_BI; 31 | return balance; 32 | } 33 | 34 | export function handleAtomicMatch(call: AtomicMatch_Call): void { 35 | // Stop indexing after end block 36 | if (call.block.number > END_BLOCK) { 37 | return; 38 | } 39 | 40 | // If the currency used for the trade isn't in the whitelist, skip. 41 | if (!currencies.includes(call.inputs.addrs[6].toHex())) { 42 | return; 43 | } 44 | 45 | // If the order value is equal zero, skip. 46 | if (call.inputs.uints[4].equals(BigInt.zero())) { 47 | return; 48 | } 49 | 50 | // 1. Currency 51 | let currency = Currency.load(call.inputs.addrs[6].toHex()); 52 | if (currency === null) { 53 | currency = new Currency(call.inputs.addrs[6].toHex()); 54 | currency.name = fetchName(call.inputs.addrs[6]); 55 | currency.symbol = fetchSymbol(call.inputs.addrs[6]); 56 | currency.decimals = fetchDecimals(call.inputs.addrs[6]); 57 | currency.totalTrades = ZERO_BI; 58 | currency.volume = ZERO_BD; 59 | currency.volumeInETH = ZERO_BD; 60 | currency.priceOfOneETH = ONE_BD; 61 | currency.updatedAt = ZERO_BI; 62 | if (!etherAddresses.includes(currency.id)) { 63 | currency.minPriceOfOneETH = BigDecimal.fromString("9999999999"); // Arbitrary large number 64 | currency.maxPriceOfOneETH = ZERO_BD; 65 | } else { 66 | currency.minPriceOfOneETH = ONE_BD; 67 | currency.maxPriceOfOneETH = ONE_BD; 68 | } 69 | } 70 | 71 | currency.totalTrades = currency.totalTrades.plus(ONE_BI); 72 | currency.volume = currency.volume.plus(toBigDecimal(call.inputs.uints[4], currency.decimals.toI32())); 73 | 74 | let priceOfOneETH = currency.priceOfOneETH; 75 | let adjustedCurrencyVolume = toBigDecimal(call.inputs.uints[4], currency.decimals.toI32()); 76 | 77 | // Exclude if traded currency is WETH/ETH 78 | if (!etherAddresses.includes(currency.id)) { 79 | // Refresh only if not updated for 1 hour 80 | if (currency.updatedAt.plus(BigInt.fromString("3600")) < call.block.timestamp) { 81 | priceOfOneETH = getPrice(currency.id, currency.decimals.toI32()); 82 | 83 | currency.priceOfOneETH = priceOfOneETH; 84 | 85 | if (priceOfOneETH > currency.maxPriceOfOneETH) { 86 | currency.maxPriceOfOneETH = priceOfOneETH; 87 | } 88 | 89 | if (priceOfOneETH < currency.minPriceOfOneETH) { 90 | currency.minPriceOfOneETH = priceOfOneETH; 91 | } 92 | 93 | currency.updatedAt = call.block.timestamp; 94 | } 95 | adjustedCurrencyVolume = adjustedCurrencyVolume.div(priceOfOneETH).truncate(18); 96 | } 97 | 98 | currency.volumeInETH = currency.volumeInETH.plus(adjustedCurrencyVolume); 99 | currency.save(); 100 | 101 | // 2. Buyer 102 | // 2.1 Buyer 103 | let buyer = User.load(call.inputs.addrs[1].toHex()); 104 | if (buyer === null) { 105 | buyer = initializeUser(call.inputs.addrs[1].toHex(), currency.id, call.block.timestamp); 106 | } 107 | 108 | buyer.totalBuyTransactions = buyer.totalBuyTransactions.plus(ONE_BI); 109 | buyer.lastTradeAt = call.block.timestamp; 110 | buyer.lastTradeWith = currency.id; 111 | buyer.totalVolumeInETH = buyer.totalVolumeInETH.plus(adjustedCurrencyVolume); 112 | buyer.save(); 113 | 114 | // 2.2 Buyer's balance in the currency 115 | let buyerBalance = Balance.load(call.inputs.addrs[1].toHex() + "-" + call.inputs.addrs[6].toHex()); 116 | 117 | if (buyerBalance === null) { 118 | buyerBalance = initializeUserBalance(call.inputs.addrs[1].toHex(), call.inputs.addrs[6].toHex()); 119 | } 120 | 121 | buyerBalance.volume = buyerBalance.volume.plus(toBigDecimal(call.inputs.uints[4], currency.decimals.toI32())); 122 | buyerBalance.updatedAt = call.block.timestamp; 123 | buyerBalance.save(); 124 | 125 | // 3. Seller 126 | // 3.1 Seller 127 | let seller = User.load(call.inputs.addrs[8].toHex()); 128 | if (seller === null) { 129 | seller = initializeUser(call.inputs.addrs[8].toHex(), currency.id, call.block.timestamp); 130 | } 131 | 132 | seller.totalSellTransactions = seller.totalSellTransactions.plus(ONE_BI); 133 | seller.totalVolumeInETH = seller.totalVolumeInETH.plus(adjustedCurrencyVolume); 134 | seller.lastTradeAt = call.block.timestamp; 135 | seller.lastTradeWith = currency.id; 136 | seller.save(); 137 | 138 | // 3.2 Seller's balance 139 | let sellerBalance = Balance.load(call.inputs.addrs[8].toHex() + "-" + call.inputs.addrs[6].toHex()); 140 | if (sellerBalance === null) { 141 | sellerBalance = initializeUserBalance(call.inputs.addrs[8].toHex(), call.inputs.addrs[6].toHex()); 142 | sellerBalance.save(); 143 | } 144 | 145 | sellerBalance.volume = sellerBalance.volume.plus(toBigDecimal(call.inputs.uints[4], currency.decimals.toI32())); 146 | sellerBalance.updatedAt = call.block.timestamp; 147 | sellerBalance.save(); 148 | } 149 | -------------------------------------------------------------------------------- /subgraphs/airdrop/mappings/utils/erc20.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt } from "@graphprotocol/graph-ts"; 2 | import { ERC20 } from "../../generated/WyvernExchange/ERC20"; 3 | 4 | export function fetchName(address: Address): string { 5 | const contract = ERC20.bind(address); 6 | 7 | const nameResult = contract.try_name(); 8 | if (!nameResult.reverted) { 9 | return nameResult.value; 10 | } 11 | 12 | return "Ethereum"; 13 | } 14 | 15 | export function fetchSymbol(address: Address): string { 16 | const contract = ERC20.bind(address); 17 | 18 | const symbolResult = contract.try_symbol(); 19 | if (!symbolResult.reverted) { 20 | return symbolResult.value; 21 | } 22 | 23 | return "ETH"; 24 | } 25 | 26 | export function fetchDecimals(address: Address): BigInt { 27 | const contract = ERC20.bind(address); 28 | 29 | const decimalResult = contract.try_decimals(); 30 | if (!decimalResult.reverted) { 31 | return BigInt.fromI32(decimalResult.value); 32 | } 33 | 34 | return BigInt.fromI32(18); 35 | } 36 | -------------------------------------------------------------------------------- /subgraphs/airdrop/mappings/utils/getPrice.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigDecimal } from "@graphprotocol/graph-ts"; 2 | import { toBigDecimal } from "../../../../helpers/utils"; 3 | import { ZERO_BD } from "../../../../helpers/constants"; 4 | import { IUniswapV2Pair } from "../../generated/WyvernExchange/IUniswapV2Pair"; 5 | 6 | // Switch/case is not supported for strings 7 | // https://www.assemblyscript.org/examples/snippets.html#switch-case 8 | function getLpAddress(currency: string): string { 9 | if (currency == "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") { 10 | // USDC 11 | return "0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc"; 12 | } else if (currency == "0x6b175474e89094c44da98b954eedeac495271d0f") { 13 | // DAI 14 | return "0xa478c2975ab1ea89e8196811f51a7b7ade33eb11"; 15 | } else if (currency == "0x3845badade8e6dff049820680d1f14bd3903a5d0") { 16 | // SAND 17 | return "0x3dd49f67e9d5bc4c5e6634b3f70bfd9dc1b6bd74"; 18 | } else if (currency == "0x0f5d2fb29fb7d3cfee444a200298f468908cc942") { 19 | // MANA 20 | return "0x11b1f53204d03e5529f09eb3091939e4fd8c9cf3"; 21 | } else if (currency == "0x15d4c048f83bd7e37d49ea4c83a07267ec4203da") { 22 | // GALA 23 | return "0xbe19c32b4cd202407e8eeb73e4e2949438461ae3"; 24 | } else { 25 | return "0x0000000000000000000000000000000000000000"; 26 | } 27 | } 28 | 29 | export function getPrice(currency: string, decimals: i32): BigDecimal { 30 | const lpAddress = getLpAddress(currency); 31 | 32 | if (lpAddress == "0x0000000000000000000000000000000000000000") { 33 | return ZERO_BD; 34 | } 35 | 36 | const uniswapPair = IUniswapV2Pair.bind(Address.fromString(lpAddress)); 37 | 38 | const reserves = uniswapPair.try_getReserves(); 39 | if (!reserves.reverted) { 40 | const reserve0 = toBigDecimal(reserves.value.value0, decimals); 41 | const reserve1 = toBigDecimal(reserves.value.value1); 42 | return reserve0.div(reserve1); 43 | } else { 44 | return ZERO_BD; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /subgraphs/airdrop/mappings/utils/index.ts: -------------------------------------------------------------------------------- 1 | export const currencies: string[] = [ 2 | "0x0000000000000000000000000000000000000000", // ETH 3 | "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH 4 | "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC 5 | "0x6b175474e89094c44da98b954eedeac495271d0f", // DAI, 6 | "0x3845badade8e6dff049820680d1f14bd3903a5d0", // SAND 7 | "0x0f5d2fb29fb7d3cfee444a200298f468908cc942", // MANA 8 | "0x15d4c048f83bd7e37d49ea4c83a07267ec4203da", // GALA 9 | ]; 10 | 11 | export const etherAddresses: string[] = [ 12 | "0x0000000000000000000000000000000000000000", // ETH 13 | "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH 14 | ]; 15 | -------------------------------------------------------------------------------- /subgraphs/airdrop/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "airdrop", 3 | "description": "LooksRare Subgraph", 4 | "version": "1.0.0", 5 | "repository": "git@github.com:looksrare/subgraph.git", 6 | "author": "LooksRare", 7 | "license": "MIT", 8 | "scripts": { 9 | "codegen": "graph codegen subgraph.yaml", 10 | "build:mainnet": "graph build subgraph.yaml", 11 | "deploy:mainnet": "graph deploy --product hosted-service looksrare/airdrop subgraph-mainnet.yaml" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /subgraphs/airdrop/schema.graphql: -------------------------------------------------------------------------------- 1 | """ 2 | Currencies 3 | """ 4 | type Currency @entity { 5 | "ID" 6 | id: ID! 7 | 8 | "Name" 9 | name: String! 10 | 11 | "Symbol" 12 | symbol: String! 13 | 14 | "Decimals" 15 | decimals: BigInt! 16 | 17 | "Total of Trades" 18 | totalTrades: BigInt! 19 | 20 | "Volume" 21 | volume: BigDecimal! 22 | 23 | "Volume in ETH" 24 | volumeInETH: BigDecimal! 25 | 26 | "Price of 1 ETH in the currency" 27 | priceOfOneETH: BigDecimal! 28 | 29 | "Minimum price of 1 ETH" 30 | minPriceOfOneETH: BigDecimal! 31 | 32 | "Maximum price of 1 ETH" 33 | maxPriceOfOneETH: BigDecimal! 34 | 35 | "Last update for exchange rate of 1 ETH" 36 | updatedAt: BigInt! 37 | 38 | "List of Balances" 39 | balances: [Balance!]! @derivedFrom(field: "currency") 40 | } 41 | 42 | """ 43 | User 44 | """ 45 | type User @entity { 46 | "ID" 47 | id: ID! 48 | 49 | "Total of Buy transactions" 50 | totalBuyTransactions: BigInt! 51 | 52 | "Total of Sell transactions" 53 | totalSellTransactions: BigInt! 54 | 55 | "Total volume in ETH" 56 | totalVolumeInETH: BigDecimal! 57 | 58 | "First Trade At" 59 | firstTradeAt: BigInt! 60 | 61 | "First Trade With" 62 | firstTradeWith: Currency! 63 | 64 | "Last Trade At" 65 | lastTradeAt: BigInt! 66 | 67 | "Last Trade With" 68 | lastTradeWith: Currency! 69 | 70 | "List of Balances" 71 | balances: [Balance!]! @derivedFrom(field: "user") 72 | } 73 | 74 | """ 75 | Balance 76 | """ 77 | type Balance @entity { 78 | "ID" 79 | id: ID! 80 | 81 | "Currency" 82 | currency: Currency! 83 | 84 | "User" 85 | user: User! 86 | 87 | "Volume" 88 | volume: BigDecimal! 89 | 90 | "Updated At" 91 | updatedAt: BigInt! 92 | } 93 | -------------------------------------------------------------------------------- /subgraphs/airdrop/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | description: OpenSea indexer for LooksRare airdrop 3 | repository: https://github.com/looksrare 4 | schema: 5 | file: ./schema.graphql 6 | dataSources: 7 | - kind: ethereum/contract 8 | name: WyvernExchange 9 | network: mainnet 10 | source: 11 | abi: WyvernExchange 12 | address: "0x7Be8076f4EA4A4AD08075C2508e481d6C946D12b" 13 | startBlock: 12642194 14 | mapping: 15 | kind: ethereum/events 16 | apiVersion: 0.0.7 17 | language: wasm/assemblyscript 18 | file: ./mappings/index.ts 19 | entities: 20 | - User 21 | abis: 22 | - name: WyvernExchange 23 | file: ./abis/WyvernExchange.json 24 | - name: ERC20 25 | file: ./abis/ERC20.json 26 | - name: IUniswapV2Pair 27 | file: ./abis/IUniswapV2Pair.json 28 | callHandlers: 29 | - function: atomicMatch_(address[14],uint256[18],uint8[8],bytes,bytes,bytes,bytes,bytes,bytes,uint8[2],bytes32[5]) 30 | handler: handleAtomicMatch 31 | -------------------------------------------------------------------------------- /subgraphs/eip1155/abis/EIP1155.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "_operator", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "_from", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": true, 19 | "internalType": "address", 20 | "name": "_to", 21 | "type": "address" 22 | }, 23 | { 24 | "indexed": false, 25 | "internalType": "uint256[]", 26 | "name": "_ids", 27 | "type": "uint256[]" 28 | }, 29 | { 30 | "indexed": false, 31 | "internalType": "uint256[]", 32 | "name": "_values", 33 | "type": "uint256[]" 34 | } 35 | ], 36 | "name": "TransferBatch", 37 | "type": "event" 38 | }, 39 | { 40 | "anonymous": false, 41 | "inputs": [ 42 | { 43 | "indexed": true, 44 | "internalType": "address", 45 | "name": "_operator", 46 | "type": "address" 47 | }, 48 | { 49 | "indexed": true, 50 | "internalType": "address", 51 | "name": "_from", 52 | "type": "address" 53 | }, 54 | { 55 | "indexed": true, 56 | "internalType": "address", 57 | "name": "_to", 58 | "type": "address" 59 | }, 60 | { 61 | "indexed": false, 62 | "internalType": "uint256", 63 | "name": "_id", 64 | "type": "uint256" 65 | }, 66 | { 67 | "indexed": false, 68 | "internalType": "uint256", 69 | "name": "_value", 70 | "type": "uint256" 71 | } 72 | ], 73 | "name": "TransferSingle", 74 | "type": "event" 75 | }, 76 | { 77 | "anonymous": false, 78 | "inputs": [ 79 | { 80 | "indexed": false, 81 | "internalType": "string", 82 | "name": "_value", 83 | "type": "string" 84 | }, 85 | { 86 | "indexed": true, 87 | "internalType": "uint256", 88 | "name": "_id", 89 | "type": "uint256" 90 | } 91 | ], 92 | "name": "URI", 93 | "type": "event" 94 | }, 95 | { 96 | "constant": true, 97 | "inputs": [], 98 | "name": "name", 99 | "outputs": [ 100 | { 101 | "internalType": "string", 102 | "name": "", 103 | "type": "string" 104 | } 105 | ], 106 | "payable": false, 107 | "stateMutability": "view", 108 | "type": "function" 109 | }, 110 | { 111 | "constant": true, 112 | "inputs": [], 113 | "name": "symbol", 114 | "outputs": [ 115 | { 116 | "internalType": "string", 117 | "name": "", 118 | "type": "string" 119 | } 120 | ], 121 | "payable": false, 122 | "stateMutability": "view", 123 | "type": "function" 124 | }, 125 | { 126 | "constant": true, 127 | "inputs": [ 128 | { 129 | "internalType": "uint256", 130 | "name": "_id", 131 | "type": "uint256" 132 | } 133 | ], 134 | "name": "uri", 135 | "outputs": [ 136 | { 137 | "internalType": "string", 138 | "name": "", 139 | "type": "string" 140 | } 141 | ], 142 | "payable": false, 143 | "stateMutability": "view", 144 | "type": "function" 145 | } 146 | ] 147 | -------------------------------------------------------------------------------- /subgraphs/eip1155/mappings/utils/eip1155.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt } from "@graphprotocol/graph-ts"; 2 | import { EIP1155 } from "../../generated/EIP1155/EIP1155"; 3 | 4 | export function fetchName(address: Address): string { 5 | const contract = EIP1155.bind(address); 6 | 7 | const nameResult = contract.try_name(); 8 | if (!nameResult.reverted) { 9 | return nameResult.value; 10 | } 11 | 12 | return "unknown"; 13 | } 14 | 15 | export function fetchSymbol(address: Address): string { 16 | const contract = EIP1155.bind(address); 17 | 18 | const symbolResult = contract.try_symbol(); 19 | if (!symbolResult.reverted) { 20 | return symbolResult.value; 21 | } 22 | 23 | return "unknown"; 24 | } 25 | 26 | export function fetchURI(address: Address, tokenID: BigInt): string | null { 27 | const contract = EIP1155.bind(address); 28 | 29 | const uriResult = contract.try_uri(tokenID); 30 | if (!uriResult.reverted) { 31 | return uriResult.value; 32 | } 33 | 34 | return null; 35 | } 36 | -------------------------------------------------------------------------------- /subgraphs/eip1155/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "EIP1155": { 4 | "startBlock": 5801844 5 | } 6 | }, 7 | "goerli": { 8 | "EIP1155": { 9 | "startBlock": 0 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /subgraphs/eip1155/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eip1155", 3 | "description": "LooksRare Subgraph", 4 | "version": "1.0.0", 5 | "repository": "git@github.com:looksrare/subgraph.git", 6 | "author": "LooksRare", 7 | "license": "MIT", 8 | "scripts": { 9 | "codegen": "graph codegen subgraph.yaml", 10 | "build:goerli": "graph build subgraph.yaml --network goerli", 11 | "build:mainnet": "graph build subgraph.yaml --network mainnet", 12 | "deploy:goerli": "graph deploy --product hosted-service 0xjurassicpunk/eip1155 subgraph.yaml --network goerli", 13 | "deploy:mainnet": "graph deploy --product hosted-service looksrare/eip1155 subgraph.yaml --network mainnet" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /subgraphs/eip1155/schema.graphql: -------------------------------------------------------------------------------- 1 | """ 2 | Blockchain 3 | """ 4 | type Blockchain @entity { 5 | "ID" 6 | id: ID! 7 | 8 | "Total of Collections" 9 | totalCollections: BigInt! 10 | 11 | "Total of Tokens" 12 | totalTokens: BigInt! 13 | 14 | "Total of Transactions" 15 | totalTransactions: BigInt! 16 | } 17 | 18 | """ 19 | EIP-1155 Collection(s) 20 | """ 21 | type Collection @entity { 22 | "ID" 23 | id: ID! 24 | 25 | "Name" 26 | name: String! 27 | 28 | "Symbol" 29 | symbol: String! 30 | 31 | "List of Tokens" 32 | tokens: [Token!]! @derivedFrom(field: "collection") 33 | "Total of Tokens" 34 | totalTokens: BigInt! 35 | 36 | "List of Transactions" 37 | transactions: [Transaction!]! @derivedFrom(field: "collection") 38 | "Total of Transactions" 39 | totalTransactions: BigInt! 40 | 41 | "Block number" 42 | block: BigInt! 43 | "Created At" 44 | createdAt: BigInt! 45 | "Updated At" 46 | updatedAt: BigInt! 47 | } 48 | 49 | """ 50 | EIP-1155 Owner(s) 51 | """ 52 | type Owner @entity { 53 | "ID" 54 | id: ID! 55 | 56 | "List of Tokens" 57 | tokens: [Token!]! @derivedFrom(field: "owner") 58 | "Total of Tokens" 59 | totalTokens: BigInt! 60 | "Total of Tokens Minted" 61 | totalTokensMinted: BigInt! 62 | 63 | "List of Transactions (as Sender)" 64 | fromTransactions: [Transaction!]! @derivedFrom(field: "from") 65 | "List of Transactions (as Receiver)" 66 | toTransactions: [Transaction!]! @derivedFrom(field: "to") 67 | "Total of Transactions" 68 | totalTransactions: BigInt! 69 | 70 | "Block number" 71 | block: BigInt! 72 | "Created At" 73 | createdAt: BigInt! 74 | "Updated At" 75 | updatedAt: BigInt! 76 | } 77 | 78 | """ 79 | EIP-1155 Token(s) 80 | """ 81 | type Token @entity { 82 | "ID" 83 | id: ID! 84 | 85 | "Collection" 86 | collection: Collection! 87 | 88 | "Token ID" 89 | tokenID: BigInt! 90 | 91 | "Token URI" 92 | tokenURI: String 93 | 94 | "Minter" 95 | minter: Owner! 96 | 97 | "Owner" 98 | owner: Owner! 99 | 100 | "Burned" 101 | burned: Boolean! 102 | 103 | "List of Transactions" 104 | transactions: [Transaction!]! @derivedFrom(field: "token") 105 | "Total of Transactions" 106 | totalTransactions: BigInt! 107 | 108 | "Block number" 109 | block: BigInt! 110 | "Created At" 111 | createdAt: BigInt! 112 | "Updated At" 113 | updatedAt: BigInt! 114 | } 115 | 116 | """ 117 | Transaction Type 118 | """ 119 | enum TransactionType { 120 | Single 121 | Batch 122 | } 123 | 124 | """ 125 | EIP-1155 Transaction(s) 126 | """ 127 | type Transaction @entity { 128 | "ID (hash)" 129 | id: ID! 130 | 131 | "Hash" 132 | hash: Bytes! 133 | 134 | "Sender" 135 | from: Owner! 136 | "Receiver" 137 | to: Owner! 138 | 139 | "Collection" 140 | collection: Collection! 141 | "Token" 142 | token: Token! 143 | 144 | "Type" 145 | type: TransactionType! 146 | 147 | "Gas Limit" 148 | gasLimit: BigInt! 149 | "Gas Price (in wei)" 150 | gasPrice: BigDecimal! 151 | 152 | "Block number" 153 | block: BigInt! 154 | "Block timestamp" 155 | timestamp: BigInt! 156 | } 157 | -------------------------------------------------------------------------------- /subgraphs/eip1155/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | description: Ethereum EIP-1155 indexer 3 | repository: https://github.com/looksrare 4 | schema: 5 | file: ./schema.graphql 6 | dataSources: 7 | - kind: ethereum/contract 8 | name: EIP1155 9 | network: mainnet 10 | source: 11 | abi: EIP1155 12 | startBlock: 5801844 13 | mapping: 14 | kind: ethereum/events 15 | apiVersion: 0.0.7 16 | language: wasm/assemblyscript 17 | file: ./mappings/index.ts 18 | entities: 19 | - Blockchain 20 | - Collection 21 | - Owner 22 | - Token 23 | - Transaction 24 | abis: 25 | - name: EIP1155 26 | file: ./abis/EIP1155.json 27 | eventHandlers: 28 | - event: TransferBatch(indexed address,indexed address,indexed 29 | address,uint256[],uint256[]) 30 | handler: handleTransferBatch 31 | - event: TransferSingle(indexed address,indexed address,indexed 32 | address,uint256,uint256) 33 | handler: handleTransferSingle 34 | - event: URI(string,indexed uint256) 35 | handler: handleURI 36 | -------------------------------------------------------------------------------- /subgraphs/eip721/abis/EIP721.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "name", 5 | "outputs": [ 6 | { 7 | "internalType": "string", 8 | "name": "", 9 | "type": "string" 10 | } 11 | ], 12 | "stateMutability": "view", 13 | "type": "function" 14 | }, 15 | { 16 | "inputs": [], 17 | "name": "symbol", 18 | "outputs": [ 19 | { 20 | "internalType": "string", 21 | "name": "", 22 | "type": "string" 23 | } 24 | ], 25 | "stateMutability": "view", 26 | "type": "function" 27 | }, 28 | { 29 | "inputs": [ 30 | { 31 | "internalType": "uint256", 32 | "name": "tokenId", 33 | "type": "uint256" 34 | } 35 | ], 36 | "name": "tokenURI", 37 | "outputs": [ 38 | { 39 | "internalType": "string", 40 | "name": "", 41 | "type": "string" 42 | } 43 | ], 44 | "stateMutability": "view", 45 | "type": "function" 46 | }, 47 | { 48 | "anonymous": false, 49 | "inputs": [ 50 | { 51 | "indexed": true, 52 | "internalType": "address", 53 | "name": "from", 54 | "type": "address" 55 | }, 56 | { 57 | "indexed": true, 58 | "internalType": "address", 59 | "name": "to", 60 | "type": "address" 61 | }, 62 | { 63 | "indexed": true, 64 | "internalType": "uint256", 65 | "name": "tokenId", 66 | "type": "uint256" 67 | } 68 | ], 69 | "name": "Transfer", 70 | "type": "event" 71 | } 72 | ] 73 | -------------------------------------------------------------------------------- /subgraphs/eip721/mappings/index.ts: -------------------------------------------------------------------------------- 1 | import { BigInt } from "@graphprotocol/graph-ts"; 2 | import { ONE_BI, ZERO_ADDRESS } from "../../../helpers/constants"; 3 | import { toBigDecimal } from "../../../helpers/utils"; 4 | import { Transfer } from "../generated/EIP721/EIP721"; 5 | import { Blockchain, Collection, Owner, Token, Transaction } from "../generated/schema"; 6 | import { fetchName, fetchSymbol, fetchTokenURI } from "./utils/eip721"; 7 | 8 | export function handleTransfer(event: Transfer): void { 9 | let blockchain = Blockchain.load("ETH"); 10 | if (blockchain === null) { 11 | // Blockchain 12 | blockchain = new Blockchain("ETH"); 13 | blockchain.totalCollections = BigInt.zero(); 14 | blockchain.totalTokens = BigInt.zero(); 15 | blockchain.totalTransactions = BigInt.zero(); 16 | blockchain.save(); 17 | } 18 | blockchain.totalTransactions = blockchain.totalTransactions.plus(ONE_BI); 19 | blockchain.save(); 20 | 21 | let collection = Collection.load(event.address.toHex()); 22 | if (collection === null) { 23 | // Collection 24 | collection = new Collection(event.address.toHex()); 25 | collection.name = fetchName(event.address); 26 | collection.symbol = fetchSymbol(event.address); 27 | collection.totalTokens = BigInt.zero(); 28 | collection.totalTransactions = BigInt.zero(); 29 | collection.block = event.block.number; 30 | collection.createdAt = event.block.timestamp; 31 | collection.updatedAt = event.block.timestamp; 32 | collection.save(); 33 | 34 | // Blockchain 35 | blockchain.totalCollections = blockchain.totalCollections.plus(ONE_BI); 36 | blockchain.save(); 37 | } 38 | collection.totalTransactions = collection.totalTransactions.plus(ONE_BI); 39 | collection.updatedAt = event.block.timestamp; 40 | collection.save(); 41 | 42 | let from = Owner.load(event.params.from.toHex()); 43 | if (from === null) { 44 | // Owner - as Sender 45 | from = new Owner(event.params.from.toHex()); 46 | from.totalTokens = BigInt.zero(); 47 | from.totalTokensMinted = BigInt.zero(); 48 | from.totalTransactions = BigInt.zero(); 49 | from.block = event.block.number; 50 | from.createdAt = event.block.timestamp; 51 | from.updatedAt = event.block.timestamp; 52 | from.save(); 53 | } 54 | from.totalTokens = event.params.from.equals(ZERO_ADDRESS) ? from.totalTokens : from.totalTokens.minus(ONE_BI); 55 | from.totalTransactions = from.totalTransactions.plus(ONE_BI); 56 | from.updatedAt = event.block.timestamp; 57 | from.save(); 58 | 59 | let to = Owner.load(event.params.to.toHex()); 60 | if (to === null) { 61 | // Owner - as Receiver 62 | to = new Owner(event.params.to.toHex()); 63 | to.totalTokens = BigInt.zero(); 64 | to.totalTokensMinted = BigInt.zero(); 65 | to.totalTransactions = BigInt.zero(); 66 | to.block = event.block.number; 67 | to.createdAt = event.block.timestamp; 68 | to.updatedAt = event.block.timestamp; 69 | to.save(); 70 | } 71 | to.totalTokens = to.totalTokens.plus(ONE_BI); 72 | to.totalTransactions = to.totalTransactions.plus(ONE_BI); 73 | to.updatedAt = event.block.timestamp; 74 | to.save(); 75 | 76 | let token = Token.load(event.address.toHex() + "-" + event.params.tokenId.toString()); 77 | if (token === null) { 78 | // Token 79 | token = new Token(event.address.toHex() + "-" + event.params.tokenId.toString()); 80 | token.collection = collection.id; 81 | token.tokenID = event.params.tokenId; 82 | token.tokenURI = fetchTokenURI(event.address, event.params.tokenId); 83 | token.minter = to.id; 84 | token.owner = to.id; 85 | token.burned = false; 86 | token.totalTransactions = BigInt.zero(); 87 | token.block = event.block.number; 88 | token.createdAt = event.block.timestamp; 89 | token.updatedAt = event.block.timestamp; 90 | token.save(); 91 | 92 | // Owner - as Receiver 93 | to.totalTokensMinted = to.totalTokensMinted.plus(ONE_BI); 94 | to.save(); 95 | 96 | // Collection 97 | collection.totalTokens = collection.totalTokens.plus(ONE_BI); 98 | collection.save(); 99 | 100 | // Blockchain 101 | blockchain.totalTokens = blockchain.totalTokens.plus(ONE_BI); 102 | blockchain.save(); 103 | } 104 | token.owner = to.id; 105 | token.burned = event.params.to.equals(ZERO_ADDRESS); 106 | token.totalTransactions = token.totalTransactions.plus(ONE_BI); 107 | token.updatedAt = event.block.timestamp; 108 | token.save(); 109 | 110 | // Transaction 111 | const transaction = new Transaction(event.transaction.hash.toHex()); 112 | transaction.hash = event.transaction.hash; 113 | transaction.from = from.id; 114 | transaction.to = to.id; 115 | transaction.collection = collection.id; 116 | transaction.token = token.id; 117 | transaction.gasLimit = event.transaction.gasLimit; 118 | transaction.gasPrice = toBigDecimal(event.transaction.gasPrice, 9); 119 | transaction.block = event.block.number; 120 | transaction.timestamp = event.block.timestamp; 121 | transaction.save(); 122 | } 123 | -------------------------------------------------------------------------------- /subgraphs/eip721/mappings/utils/eip721.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt } from "@graphprotocol/graph-ts"; 2 | import { EIP721 } from "../../generated/EIP721/EIP721"; 3 | 4 | export function fetchName(address: Address): string { 5 | const contract = EIP721.bind(address); 6 | 7 | const nameResult = contract.try_name(); 8 | if (!nameResult.reverted) { 9 | return nameResult.value; 10 | } 11 | 12 | return "unknown"; 13 | } 14 | 15 | export function fetchSymbol(address: Address): string { 16 | const contract = EIP721.bind(address); 17 | 18 | const symbolResult = contract.try_symbol(); 19 | if (!symbolResult.reverted) { 20 | return symbolResult.value; 21 | } 22 | 23 | return "unknown"; 24 | } 25 | 26 | export function fetchTokenURI(address: Address, tokenID: BigInt): string | null { 27 | const contract = EIP721.bind(address); 28 | 29 | const uriResult = contract.try_tokenURI(tokenID); 30 | if (!uriResult.reverted) { 31 | return uriResult.value; 32 | } 33 | 34 | return null; 35 | } 36 | -------------------------------------------------------------------------------- /subgraphs/eip721/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "EIP721": { 4 | "startBlock": 4300082 5 | } 6 | }, 7 | "goerli": { 8 | "EIP721": { 9 | "startBlock": 0 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /subgraphs/eip721/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eip721", 3 | "description": "LooksRare Subgraph", 4 | "version": "1.0.0", 5 | "repository": "git@github.com:looksrare/subgraph.git", 6 | "author": "LooksRare", 7 | "license": "MIT", 8 | "scripts": { 9 | "codegen": "graph codegen subgraph.yaml", 10 | "build:goerli": "graph build subgraph.yaml --network goerli", 11 | "build:mainnet": "graph build subgraph.yaml --network mainnet", 12 | "deploy:goerli": "graph deploy --product hosted-service 0xjurassicpunk/eip721 subgraph.yaml --network goerli", 13 | "deploy:mainnet": "graph deploy --product hosted-service looksrare/eip721 subgraph.yaml --network mainnet" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /subgraphs/eip721/schema.graphql: -------------------------------------------------------------------------------- 1 | """ 2 | Blockchain 3 | """ 4 | type Blockchain @entity { 5 | "ID" 6 | id: ID! 7 | 8 | "Total of Collections" 9 | totalCollections: BigInt! 10 | 11 | "Total of Tokens" 12 | totalTokens: BigInt! 13 | 14 | "Total of Transactions" 15 | totalTransactions: BigInt! 16 | } 17 | 18 | """ 19 | EIP-721 Collection(s) 20 | """ 21 | type Collection @entity { 22 | "ID" 23 | id: ID! 24 | 25 | "Name" 26 | name: String! 27 | 28 | "Symbol" 29 | symbol: String! 30 | 31 | "List of Tokens" 32 | tokens: [Token!]! @derivedFrom(field: "collection") 33 | "Total of Tokens" 34 | totalTokens: BigInt! 35 | 36 | "List of Transactions" 37 | transactions: [Transaction!]! @derivedFrom(field: "collection") 38 | "Total of Transactions" 39 | totalTransactions: BigInt! 40 | 41 | "Block number" 42 | block: BigInt! 43 | "Created At" 44 | createdAt: BigInt! 45 | "Updated At" 46 | updatedAt: BigInt! 47 | } 48 | 49 | """ 50 | EIP-721 Owner(s) 51 | """ 52 | type Owner @entity { 53 | "ID" 54 | id: ID! 55 | 56 | "List of Tokens" 57 | tokens: [Token!]! @derivedFrom(field: "owner") 58 | "Total of Tokens" 59 | totalTokens: BigInt! 60 | "Total of Tokens Minted" 61 | totalTokensMinted: BigInt! 62 | 63 | "List of Transactions (as Sender)" 64 | fromTransactions: [Transaction!]! @derivedFrom(field: "from") 65 | "List of Transactions (as Receiver)" 66 | toTransactions: [Transaction!]! @derivedFrom(field: "to") 67 | "Total of Transactions" 68 | totalTransactions: BigInt! 69 | 70 | "Block number" 71 | block: BigInt! 72 | "Created At" 73 | createdAt: BigInt! 74 | "Updated At" 75 | updatedAt: BigInt! 76 | } 77 | 78 | """ 79 | EIP-721 Token(s) 80 | """ 81 | type Token @entity { 82 | "ID" 83 | id: ID! 84 | 85 | "Collection" 86 | collection: Collection! 87 | 88 | "Token ID" 89 | tokenID: BigInt! 90 | 91 | "Token URI" 92 | tokenURI: String 93 | 94 | "Minter" 95 | minter: Owner! 96 | 97 | "Owner" 98 | owner: Owner! 99 | 100 | "Burned" 101 | burned: Boolean! 102 | 103 | "List of Transactions" 104 | transactions: [Transaction!]! @derivedFrom(field: "token") 105 | "Total of Transactions" 106 | totalTransactions: BigInt! 107 | 108 | "Block number" 109 | block: BigInt! 110 | "Created At" 111 | createdAt: BigInt! 112 | "Updated At" 113 | updatedAt: BigInt! 114 | } 115 | 116 | """ 117 | EIP-721 Transaction(s) 118 | """ 119 | type Transaction @entity { 120 | "ID (hash)" 121 | id: ID! 122 | 123 | "Hash" 124 | hash: Bytes! 125 | 126 | "Sender" 127 | from: Owner! 128 | "Receiver" 129 | to: Owner! 130 | 131 | "Collection" 132 | collection: Collection! 133 | "Token" 134 | token: Token! 135 | 136 | "Gas Limit" 137 | gasLimit: BigInt! 138 | "Gas Price (in wei)" 139 | gasPrice: BigDecimal! 140 | 141 | "Block number" 142 | block: BigInt! 143 | "Block timestamp" 144 | timestamp: BigInt! 145 | } -------------------------------------------------------------------------------- /subgraphs/eip721/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | description: Ethereum EIP-721 indexer 3 | repository: https://github.com/looksrare 4 | schema: 5 | file: ./schema.graphql 6 | dataSources: 7 | - kind: ethereum/contract 8 | name: EIP721 9 | network: mainnet 10 | source: 11 | abi: EIP721 12 | startBlock: 4300082 13 | mapping: 14 | kind: ethereum/events 15 | apiVersion: 0.0.7 16 | language: wasm/assemblyscript 17 | file: ./mappings/index.ts 18 | entities: 19 | - Blockchain 20 | - Collection 21 | - Owner 22 | - Token 23 | - Transaction 24 | abis: 25 | - name: EIP721 26 | file: ./abis/EIP721.json 27 | eventHandlers: 28 | - event: Transfer(indexed address,indexed address,indexed uint256) 29 | handler: handleTransfer 30 | -------------------------------------------------------------------------------- /subgraphs/exchange/mappings/utils/fetchProtocolFee.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt } from "@graphprotocol/graph-ts"; 2 | import { IExecutionStrategy } from "../../generated/LooksRareExchange/IExecutionStrategy"; 3 | 4 | export function fetchProtocolFee(strategy: Address): BigInt { 5 | const executionStrategy = IExecutionStrategy.bind(strategy); 6 | 7 | const protocolFee = executionStrategy.try_viewProtocolFee(); 8 | if (!protocolFee.reverted) { 9 | return protocolFee.value; 10 | } 11 | 12 | return BigInt.zero(); 13 | } 14 | -------------------------------------------------------------------------------- /subgraphs/exchange/mappings/utils/updateDailyData.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigDecimal, BigInt } from "@graphprotocol/graph-ts"; 2 | import { ONE_BI, ONE_DAY_BI, ZERO_BD, ZERO_BI } from "../../../../helpers/constants"; 3 | import { 4 | CollectionDailyData, 5 | ExchangeDailyData, 6 | ExecutionStrategy, 7 | ExecutionStrategyDailyData, 8 | UserDailyData, 9 | } from "../../generated/schema"; 10 | 11 | export function updateCollectionDailyData( 12 | collection: Address, 13 | strategy: Address, 14 | volume: BigDecimal, 15 | timestamp: BigInt, 16 | isTakerAsk: boolean, 17 | ): void { 18 | const dailyTimestampBigInt = ONE_DAY_BI; 19 | const dayID = timestamp.div(dailyTimestampBigInt); 20 | const dayStartTimestamp = dayID.times(dailyTimestampBigInt); 21 | const ID = dayID.toString() + "-" + collection.toHex(); 22 | 23 | let collectionDailyData = CollectionDailyData.load(ID); 24 | if (collectionDailyData === null) { 25 | collectionDailyData = new CollectionDailyData(ID); 26 | collectionDailyData.date = dayStartTimestamp; 27 | collectionDailyData.collection = collection.toHex(); 28 | collectionDailyData.dailyTransactions = ZERO_BI; 29 | collectionDailyData.dailyTakerBidTransactions = ZERO_BI; 30 | collectionDailyData.dailyTakerAskTransactions = ZERO_BI; 31 | collectionDailyData.dailyVolume = ZERO_BD; 32 | collectionDailyData.dailyTakerBidVolume = ZERO_BD; 33 | collectionDailyData.dailyTakerAskVolume = ZERO_BD; 34 | collectionDailyData.dailyVolumeExcludingZeroFee = ZERO_BD; 35 | collectionDailyData.dailyRoyalty = ZERO_BD; 36 | 37 | // Increment number of unique daily collections if it didn't exist 38 | const exchangeDailyData = ExchangeDailyData.load(dayID.toString()); 39 | if (exchangeDailyData !== null) { 40 | exchangeDailyData.dailyCollections = exchangeDailyData.dailyCollections.plus(ONE_BI); 41 | exchangeDailyData.save(); 42 | } 43 | } 44 | collectionDailyData.dailyVolume = collectionDailyData.dailyVolume.plus(volume); 45 | collectionDailyData.dailyTransactions = collectionDailyData.dailyTransactions.plus(ONE_BI); 46 | 47 | if (isTakerAsk === true) { 48 | collectionDailyData.dailyTakerAskTransactions = collectionDailyData.dailyTakerAskTransactions.plus(ONE_BI); 49 | collectionDailyData.dailyTakerAskVolume = collectionDailyData.dailyTakerAskVolume.plus(volume); 50 | } else { 51 | collectionDailyData.dailyTakerBidTransactions = collectionDailyData.dailyTakerBidTransactions.plus(ONE_BI); 52 | collectionDailyData.dailyTakerBidVolume = collectionDailyData.dailyTakerBidVolume.plus(volume); 53 | } 54 | 55 | const executionStrategy = ExecutionStrategy.load(strategy.toHex()); 56 | if (executionStrategy !== null) { 57 | if (executionStrategy.protocolFee !== ZERO_BI) { 58 | collectionDailyData.dailyVolumeExcludingZeroFee = collectionDailyData.dailyVolumeExcludingZeroFee.plus(volume); 59 | } 60 | } 61 | 62 | collectionDailyData.save(); 63 | } 64 | 65 | export function updateRoyaltyForCollectionDailyData(collection: Address, royalty: BigDecimal, timestamp: BigInt): void { 66 | const dailyTimestampBigInt = ONE_DAY_BI; 67 | const dayID = timestamp.div(dailyTimestampBigInt); 68 | const dayStartTimestamp = dayID.times(dailyTimestampBigInt); 69 | const ID = dayID.toString() + "-" + collection.toHex(); 70 | 71 | let collectionDailyData = CollectionDailyData.load(ID); 72 | if (collectionDailyData === null) { 73 | collectionDailyData = new CollectionDailyData(ID); 74 | collectionDailyData.date = dayStartTimestamp; 75 | collectionDailyData.collection = collection.toHex(); 76 | collectionDailyData.dailyTransactions = ZERO_BI; 77 | collectionDailyData.dailyTakerBidTransactions = ZERO_BI; 78 | collectionDailyData.dailyTakerAskTransactions = ZERO_BI; 79 | collectionDailyData.dailyVolume = ZERO_BD; 80 | collectionDailyData.dailyTakerBidVolume = ZERO_BD; 81 | collectionDailyData.dailyTakerAskVolume = ZERO_BD; 82 | collectionDailyData.dailyVolumeExcludingZeroFee = ZERO_BD; 83 | collectionDailyData.dailyRoyalty = ZERO_BD; 84 | } 85 | 86 | collectionDailyData.dailyRoyalty = collectionDailyData.dailyRoyalty.plus(royalty); 87 | collectionDailyData.save(); 88 | } 89 | 90 | export function updateExchangeDailyData( 91 | strategy: Address, 92 | volume: BigDecimal, 93 | timestamp: BigInt, 94 | isTakerAsk: boolean, 95 | ): void { 96 | const dailyTimestampBigInt = ONE_DAY_BI; 97 | const dayID = timestamp.div(dailyTimestampBigInt); 98 | const dayStartTimestamp = dayID.times(dailyTimestampBigInt); 99 | const ID = dayID.toString(); 100 | 101 | let exchangeDailyData = ExchangeDailyData.load(ID); 102 | if (exchangeDailyData === null) { 103 | exchangeDailyData = new ExchangeDailyData(ID); 104 | exchangeDailyData.date = dayStartTimestamp; 105 | exchangeDailyData.dailyUsers = ZERO_BI; 106 | exchangeDailyData.dailyCollections = ZERO_BI; 107 | exchangeDailyData.dailyTransactions = ZERO_BI; 108 | exchangeDailyData.dailyTakerBidTransactions = ZERO_BI; 109 | exchangeDailyData.dailyTakerAskTransactions = ZERO_BI; 110 | exchangeDailyData.dailyVolume = ZERO_BD; 111 | exchangeDailyData.dailyTakerBidVolume = ZERO_BD; 112 | exchangeDailyData.dailyTakerAskVolume = ZERO_BD; 113 | exchangeDailyData.dailyVolumeExcludingZeroFee = ZERO_BD; 114 | } 115 | exchangeDailyData.dailyTransactions = exchangeDailyData.dailyTransactions.plus(ONE_BI); 116 | exchangeDailyData.dailyVolume = exchangeDailyData.dailyVolume.plus(volume); 117 | 118 | if (isTakerAsk === true) { 119 | exchangeDailyData.dailyTakerAskTransactions = exchangeDailyData.dailyTakerAskTransactions.plus(ONE_BI); 120 | exchangeDailyData.dailyTakerAskVolume = exchangeDailyData.dailyTakerAskVolume.plus(volume); 121 | } else { 122 | exchangeDailyData.dailyTakerBidTransactions = exchangeDailyData.dailyTakerBidTransactions.plus(ONE_BI); 123 | exchangeDailyData.dailyTakerBidVolume = exchangeDailyData.dailyTakerBidVolume.plus(volume); 124 | } 125 | 126 | const executionStrategy = ExecutionStrategy.load(strategy.toHex()); 127 | if (executionStrategy !== null) { 128 | if (executionStrategy.protocolFee !== ZERO_BI) { 129 | exchangeDailyData.dailyVolumeExcludingZeroFee = exchangeDailyData.dailyVolumeExcludingZeroFee.plus(volume); 130 | } 131 | } 132 | 133 | exchangeDailyData.save(); 134 | } 135 | 136 | export function updateExecutionStrategyDailyData( 137 | strategy: Address, 138 | volume: BigDecimal, 139 | timestamp: BigInt, 140 | isTakerAsk: boolean, 141 | ): void { 142 | const dailyTimestampBigInt = ONE_DAY_BI; 143 | const dayID = timestamp.div(dailyTimestampBigInt); 144 | const dayStartTimestamp = dayID.times(dailyTimestampBigInt); 145 | const ID = dayID.toString() + "-" + strategy.toHex(); 146 | 147 | let strategyDailyData = ExecutionStrategyDailyData.load(ID); 148 | if (strategyDailyData === null) { 149 | strategyDailyData = new ExecutionStrategyDailyData(ID); 150 | strategyDailyData.date = dayStartTimestamp; 151 | strategyDailyData.strategy = strategy.toHex(); 152 | strategyDailyData.dailyTransactions = ZERO_BI; 153 | strategyDailyData.dailyTakerBidTransactions = ZERO_BI; 154 | strategyDailyData.dailyTakerAskTransactions = ZERO_BI; 155 | strategyDailyData.dailyVolume = ZERO_BD; 156 | strategyDailyData.dailyTakerBidVolume = ZERO_BD; 157 | strategyDailyData.dailyTakerAskVolume = ZERO_BD; 158 | } 159 | strategyDailyData.dailyTransactions = strategyDailyData.dailyTransactions.plus(ONE_BI); 160 | strategyDailyData.dailyVolume = strategyDailyData.dailyVolume.plus(volume); 161 | 162 | if (isTakerAsk === true) { 163 | strategyDailyData.dailyTakerAskTransactions = strategyDailyData.dailyTakerAskTransactions.plus(ONE_BI); 164 | strategyDailyData.dailyTakerAskVolume = strategyDailyData.dailyTakerAskVolume.plus(volume); 165 | } else { 166 | strategyDailyData.dailyTakerBidTransactions = strategyDailyData.dailyTakerBidTransactions.plus(ONE_BI); 167 | strategyDailyData.dailyTakerBidVolume = strategyDailyData.dailyTakerBidVolume.plus(volume); 168 | } 169 | 170 | strategyDailyData.save(); 171 | } 172 | 173 | export function updateUserDailyData(user: Address, strategy: Address, volume: BigDecimal, timestamp: BigInt): void { 174 | const dailyTimestampBigInt = ONE_DAY_BI; 175 | const dayID = timestamp.div(dailyTimestampBigInt); 176 | const dayStartTimestamp = dayID.times(dailyTimestampBigInt); 177 | const ID = dayID.toString() + "-" + user.toHex(); 178 | 179 | let userDailyData = UserDailyData.load(ID); 180 | 181 | if (userDailyData === null) { 182 | userDailyData = new UserDailyData(ID); 183 | userDailyData.date = dayStartTimestamp; 184 | userDailyData.user = user.toHex(); 185 | userDailyData.dailyTransactions = ZERO_BI; 186 | userDailyData.dailyVolume = ZERO_BD; 187 | userDailyData.dailyVolumeExcludingZeroFee = ZERO_BD; 188 | 189 | // Increment number of unique daily users if it didn't exist 190 | const exchangeDailyData = ExchangeDailyData.load(dayID.toString()); 191 | if (exchangeDailyData !== null) { 192 | exchangeDailyData.dailyUsers = exchangeDailyData.dailyUsers.plus(ONE_BI); 193 | exchangeDailyData.save(); 194 | } 195 | } 196 | 197 | userDailyData.dailyTransactions = userDailyData.dailyTransactions.plus(ONE_BI); 198 | userDailyData.dailyVolume = userDailyData.dailyVolume.plus(volume); 199 | 200 | const executionStrategy = ExecutionStrategy.load(strategy.toHex()); 201 | if (executionStrategy !== null) { 202 | if (executionStrategy.protocolFee !== ZERO_BI) { 203 | userDailyData.dailyVolumeExcludingZeroFee = userDailyData.dailyVolumeExcludingZeroFee.plus(volume); 204 | } 205 | } 206 | 207 | userDailyData.save(); 208 | } 209 | -------------------------------------------------------------------------------- /subgraphs/exchange/matchstick.yaml: -------------------------------------------------------------------------------- 1 | libsFolder: ../../node_modules/ 2 | -------------------------------------------------------------------------------- /subgraphs/exchange/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "LooksRareExchange": { 4 | "address": "0x59728544b08ab483533076417fbbb2fd0b17ce3a", 5 | "startBlock": 13885625 6 | } 7 | }, 8 | "goerli": { 9 | "LooksRareExchange": { 10 | "address": "0xd112466471b5438c1ca2d218694200e49d81d047", 11 | "startBlock": 7247379 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /subgraphs/exchange/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exchange", 3 | "description": "LooksRare exchange subgraph", 4 | "version": "1.0.0", 5 | "repository": "git@github.com:looksrare/subgraphs.git", 6 | "author": "LooksRare", 7 | "license": "MIT", 8 | "scripts": { 9 | "codegen": "graph codegen", 10 | "build:goerli": "graph build subgraph.yaml --network goerli", 11 | "build:mainnet": "graph build subgraph.yaml --network mainnet", 12 | "deploy:goerli": "graph deploy --product hosted-service 0xjurassicpunk/exchange subgraph.yaml --network goerli", 13 | "deploy:mainnet": "graph deploy --product hosted-service looksrare/exchange subgraph.yaml --network mainnet", 14 | "deploy:studio": "graph codegen subgraph.yaml && graph build subgraph.yaml && graph deploy --studio looksrare-exchange-v1 --network mainnet", 15 | "test": "graph test -r", 16 | "test:lerna": "graph codegen subgraph.yaml && graph build subgraph.yaml && graph test -r" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /subgraphs/exchange/schema.graphql: -------------------------------------------------------------------------------- 1 | """ 2 | Collection 3 | """ 4 | type Collection @entity { 5 | "ID (collection address)" 6 | id: ID! 7 | 8 | "Total number of transactions" 9 | totalTransactions: BigInt! 10 | 11 | "Total number of taker bid transactions" 12 | totalTakerBidTransactions: BigInt! 13 | 14 | "Total number of taker ask transactions" 15 | totalTakerAskTransactions: BigInt! 16 | 17 | "Total volume of collection (in ETH)" 18 | totalVolume: BigDecimal! 19 | 20 | "Total taker bid volume (in ETH)" 21 | totalTakerBidVolume: BigDecimal! 22 | 23 | "Total taker ask volume (in ETH)" 24 | totalTakerAskVolume: BigDecimal! 25 | 26 | "Total royalty paid (in ETH)" 27 | totalRoyaltyPaid: BigDecimal! 28 | 29 | "Daily history" 30 | dayData: [CollectionDailyData!]! @derivedFrom(field: "collection") 31 | 32 | "Royalty transfers" 33 | royaltyTransfers: [RoyaltyTransfer!]! @derivedFrom(field: "collection") 34 | 35 | "Transactions" 36 | transactions: [Transaction!]! @derivedFrom(field: "collection") 37 | } 38 | 39 | """ 40 | ExecutionStrategy 41 | """ 42 | type ExecutionStrategy @entity { 43 | "ID (execution strategy address)" 44 | id: ID! 45 | 46 | "Protocol fee (e.g., 500 --> 5%)" 47 | protocolFee: BigInt! 48 | 49 | "Total number of transactions" 50 | totalTransactions: BigInt! 51 | 52 | "Total number of taker bid transactions" 53 | totalTakerBidTransactions: BigInt! 54 | 55 | "Total number of taker ask transactions" 56 | totalTakerAskTransactions: BigInt! 57 | 58 | "Total volume (in ETH)" 59 | totalVolume: BigDecimal! 60 | 61 | "Total taker bid volume (in ETH)" 62 | totalTakerBidVolume: BigDecimal! 63 | 64 | "Total taker ask volume (in ETH)" 65 | totalTakerAskVolume: BigDecimal! 66 | 67 | "Daily history" 68 | dayData: [ExecutionStrategyDailyData!]! @derivedFrom(field: "strategy") 69 | 70 | "Transactions" 71 | transactions: [Transaction!]! @derivedFrom(field: "strategy") 72 | } 73 | 74 | """ 75 | User 76 | """ 77 | type User @entity { 78 | "ID (user address)" 79 | id: ID! 80 | 81 | "Total number of transactions" 82 | totalTransactions: BigInt! 83 | 84 | "Total ask volume (in ETH)" 85 | totalAskVolume: BigDecimal! 86 | 87 | "Total bid volume (in ETH)" 88 | totalBidVolume: BigDecimal! 89 | 90 | "Total maker volume (in ETH)" 91 | totalMakerVolume: BigDecimal! 92 | 93 | "Total taker volume (in ETH)" 94 | totalTakerVolume: BigDecimal! 95 | 96 | "Total volume (in ETH)" 97 | totalVolume: BigDecimal! 98 | 99 | "Total royalty collected (in ETH)" 100 | totalRoyaltyCollected: BigDecimal! 101 | 102 | "Daily history" 103 | dayData: [UserDailyData!]! @derivedFrom(field: "user") 104 | 105 | "Transactions where user is the maker side" 106 | makerTransactions: [Transaction!]! @derivedFrom(field: "maker") 107 | 108 | "Transactions where user is the taker side" 109 | takerTransactions: [Transaction!]! @derivedFrom(field: "taker") 110 | 111 | "Royalty payments collected by the user (as recipient)" 112 | royaltyPayments: [RoyaltyTransfer!]! @derivedFrom(field: "user") 113 | } 114 | 115 | """ 116 | CollectionDailyData 117 | """ 118 | type CollectionDailyData @entity { 119 | "ID" 120 | id: ID! 121 | 122 | "Start date (timestamp)" 123 | date: BigInt! 124 | 125 | "Collection" 126 | collection: Collection! 127 | 128 | "Daily unique transactions" 129 | dailyTransactions: BigInt! 130 | 131 | "Daily unique taker bid transactions" 132 | dailyTakerBidTransactions: BigInt! 133 | 134 | "Daily unique taker ask transactions" 135 | dailyTakerAskTransactions: BigInt! 136 | 137 | "Daily volume (in ETH)" 138 | dailyVolume: BigDecimal! 139 | 140 | "Daily taker bid volume (in ETH)" 141 | dailyTakerBidVolume: BigDecimal! 142 | 143 | "Daily taker ask volume (in ETH)" 144 | dailyTakerAskVolume: BigDecimal! 145 | 146 | "Daily volume (in ETH) excluding zero-fee strategy" 147 | dailyVolumeExcludingZeroFee: BigDecimal! 148 | 149 | "Royalty payment (in ETH)" 150 | dailyRoyalty: BigDecimal! 151 | } 152 | 153 | """ 154 | ExchangeDailyData 155 | """ 156 | type ExchangeDailyData @entity { 157 | "ID" 158 | id: ID! 159 | 160 | "Start date (timestamp)" 161 | date: BigInt! 162 | 163 | "Daily unique users" 164 | dailyUsers: BigInt! 165 | 166 | "Daily unique collections traded" 167 | dailyCollections: BigInt! 168 | 169 | "Daily unique transactions" 170 | dailyTransactions: BigInt! 171 | 172 | "Daily taker ask transactions" 173 | dailyTakerBidTransactions: BigInt! 174 | 175 | "Daily taker ask transactions" 176 | dailyTakerAskTransactions: BigInt! 177 | 178 | "Daily volume (in ETH)" 179 | dailyVolume: BigDecimal! 180 | 181 | "Daily taker bid volume (in ETH)" 182 | dailyTakerBidVolume: BigDecimal! 183 | 184 | "Daily taker ask volume (in ETH)" 185 | dailyTakerAskVolume: BigDecimal! 186 | 187 | "Daily volume (in ETH) excluding zero-fee strategy" 188 | dailyVolumeExcludingZeroFee: BigDecimal! 189 | } 190 | 191 | """ 192 | ExecutionStrategyDailyData 193 | """ 194 | type ExecutionStrategyDailyData @entity { 195 | "ID" 196 | id: ID! 197 | 198 | "Execution strategy" 199 | strategy: ExecutionStrategy! 200 | 201 | "Start date (timestamp)" 202 | date: BigInt! 203 | 204 | "Daily unique transactions" 205 | dailyTransactions: BigInt! 206 | 207 | "Daily unique taker bid transactions" 208 | dailyTakerBidTransactions: BigInt! 209 | 210 | "Daily unique taker bid transactions" 211 | dailyTakerAskTransactions: BigInt! 212 | 213 | "Daily volume (in ETH)" 214 | dailyVolume: BigDecimal! 215 | 216 | "Daily taker bid volume (in ETH)" 217 | dailyTakerBidVolume: BigDecimal! 218 | 219 | "Daily taker ask volume (in ETH)" 220 | dailyTakerAskVolume: BigDecimal! 221 | } 222 | 223 | """ 224 | UserDailyData 225 | """ 226 | type UserDailyData @entity { 227 | "ID" 228 | id: ID! 229 | 230 | "Start date (timestamp)" 231 | date: BigInt! 232 | 233 | "User" 234 | user: User! 235 | 236 | "Daily unique transactions" 237 | dailyTransactions: BigInt! 238 | 239 | "Daily volume (in ETH)" 240 | dailyVolume: BigDecimal! 241 | 242 | "Daily volume (in ETH) excluding zero-fee strategy" 243 | dailyVolumeExcludingZeroFee: BigDecimal! 244 | } 245 | 246 | """ 247 | RoyaltyTransfer 248 | """ 249 | type RoyaltyTransfer @entity { 250 | "ID" 251 | id: ID! 252 | 253 | "Date (timestamp)" 254 | date: BigInt! 255 | 256 | "Block number" 257 | block: BigInt! 258 | 259 | "Collection" 260 | collection: Collection! 261 | 262 | "TokenId" 263 | tokenId: BigInt! 264 | 265 | "User" 266 | user: User! 267 | 268 | "Amount received (in ETH)" 269 | amount: BigDecimal! 270 | } 271 | 272 | """ 273 | Transaction 274 | """ 275 | type Transaction @entity { 276 | "ID" 277 | id: ID! 278 | 279 | "Date (timestamp)" 280 | date: BigInt! 281 | 282 | "Block number" 283 | block: BigInt! 284 | 285 | "Collection" 286 | collection: Collection! 287 | 288 | "Whether the transaction is a takerAsk" 289 | isTakerAsk: Boolean! 290 | 291 | "Execution strategy" 292 | strategy: ExecutionStrategy! 293 | 294 | "TokenId" 295 | tokenId: BigInt! 296 | 297 | "Price (in ETH)" 298 | price: BigDecimal! 299 | 300 | "Maker user" 301 | maker: User! 302 | 303 | "Taker user" 304 | taker: User! 305 | } 306 | -------------------------------------------------------------------------------- /subgraphs/exchange/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | description: Volumes & royalties of users, creators, and collections. 3 | repository: https://github.com/looksrare 4 | schema: 5 | file: ./schema.graphql 6 | dataSources: 7 | - kind: ethereum/contract 8 | name: LooksRareExchange 9 | network: mainnet 10 | source: 11 | abi: LooksRareExchange 12 | address: "0x59728544b08ab483533076417fbbb2fd0b17ce3a" 13 | startBlock: 13885625 14 | mapping: 15 | kind: ethereum/events 16 | apiVersion: 0.0.7 17 | language: wasm/assemblyscript 18 | file: ./mappings/index.ts 19 | entities: 20 | - Collection 21 | - ExecutionStrategy 22 | - User 23 | - CollectionDailyData 24 | - ExchangeDailyData 25 | - ExecutionStrategyDailyData 26 | - UserDailyData 27 | - RoyaltyPayment 28 | - Trade 29 | abis: 30 | - name: LooksRareExchange 31 | file: ../../node_modules/@looksrare/sdk/dist/LooksRareExchangeAbi.json 32 | - name: IExecutionStrategy 33 | file: ../../node_modules/@looksrare/sdk/dist/IExecutionStrategyAbi.json 34 | eventHandlers: 35 | - event: RoyaltyPayment(indexed address,indexed uint256,indexed 36 | address,address,uint256) 37 | handler: handleRoyaltyPayment 38 | - event: TakerAsk(bytes32,uint256,indexed address,indexed address,indexed 39 | address,address,address,uint256,uint256,uint256) 40 | handler: handleTakerAsk 41 | - event: TakerBid(bytes32,uint256,indexed address,indexed address,indexed 42 | address,address,address,uint256,uint256,uint256) 43 | handler: handleTakerBid 44 | -------------------------------------------------------------------------------- /subgraphs/exchange/tests/helpers/config.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "@graphprotocol/graph-ts"; 2 | 3 | export const WETH = Address.fromString("0xc778417e063141139fce010982780140aa0cd5ab"); 4 | export const STRATEGY = Address.fromString("0x732319A3590E4fA838C111826f9584a9A2fDEa1a"); 5 | export const COLLECTION = Address.fromString("0xF7c68B84A8ad29A61AF42FC31cEF1964dd80f8Ea"); 6 | -------------------------------------------------------------------------------- /subgraphs/exchange/tests/helpers/utils.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt, Bytes, ethereum } from "@graphprotocol/graph-ts"; 2 | import { newMockEvent } from "matchstick-as"; 3 | import { RoyaltyPayment, TakerAsk, TakerBid } from "../../generated/LooksRareExchange/LooksRareExchange"; 4 | 5 | /** 6 | * @param orderHash 7 | * @param orderNonce 8 | * @param taker 9 | * @param maker 10 | * @param strategy 11 | * @param currency 12 | * @param collection 13 | * @param tokenId 14 | * @param amount 15 | * @param price 16 | * @returns TakerAsk Event 17 | */ 18 | export function createTakerAskEvent( 19 | orderHash: Bytes, 20 | orderNonce: BigInt, 21 | taker: Address, 22 | maker: Address, 23 | strategy: Address, 24 | currency: Address, 25 | collection: Address, 26 | tokenId: BigInt, 27 | amount: BigInt, 28 | price: BigInt, 29 | ): TakerAsk { 30 | const mockEvent = newMockEvent(); 31 | const newTakerAskEvent = new TakerAsk( 32 | mockEvent.address, 33 | mockEvent.logIndex, 34 | mockEvent.transactionLogIndex, 35 | mockEvent.logType, 36 | mockEvent.block, 37 | mockEvent.transaction, 38 | mockEvent.parameters, 39 | mockEvent.receipt, 40 | ); 41 | 42 | newTakerAskEvent.parameters = []; 43 | 44 | const orderHashParam = new ethereum.EventParam("orderHash", ethereum.Value.fromBytes(orderHash)); 45 | const orderNonceParam = new ethereum.EventParam("orderNonce", ethereum.Value.fromSignedBigInt(orderNonce)); 46 | const takerParam = new ethereum.EventParam("taker", ethereum.Value.fromAddress(taker)); 47 | const makerParam = new ethereum.EventParam("maker", ethereum.Value.fromAddress(maker)); 48 | const strategyParam = new ethereum.EventParam("strategy", ethereum.Value.fromAddress(strategy)); 49 | const currencyParam = new ethereum.EventParam("currency", ethereum.Value.fromAddress(currency)); 50 | const collectionParam = new ethereum.EventParam("collection", ethereum.Value.fromAddress(collection)); 51 | const tokenIdParam = new ethereum.EventParam("tokenId", ethereum.Value.fromSignedBigInt(tokenId)); 52 | const amountParam = new ethereum.EventParam("amount", ethereum.Value.fromSignedBigInt(amount)); 53 | const priceParam = new ethereum.EventParam("price", ethereum.Value.fromSignedBigInt(price)); 54 | 55 | newTakerAskEvent.parameters.push(orderHashParam); 56 | newTakerAskEvent.parameters.push(orderNonceParam); 57 | newTakerAskEvent.parameters.push(takerParam); 58 | newTakerAskEvent.parameters.push(makerParam); 59 | newTakerAskEvent.parameters.push(strategyParam); 60 | newTakerAskEvent.parameters.push(currencyParam); 61 | newTakerAskEvent.parameters.push(collectionParam); 62 | newTakerAskEvent.parameters.push(tokenIdParam); 63 | newTakerAskEvent.parameters.push(amountParam); 64 | newTakerAskEvent.parameters.push(priceParam); 65 | 66 | return newTakerAskEvent; 67 | } 68 | 69 | /** 70 | * @param orderHash 71 | * @param orderNonce 72 | * @param taker 73 | * @param maker 74 | * @param strategy 75 | * @param currency 76 | * @param collection 77 | * @param tokenId 78 | * @param amount 79 | * @param price 80 | * @returns TakerBid Event 81 | */ 82 | export function createTakerBidEvent( 83 | orderHash: Bytes, 84 | orderNonce: BigInt, 85 | taker: Address, 86 | maker: Address, 87 | strategy: Address, 88 | currency: Address, 89 | collection: Address, 90 | tokenId: BigInt, 91 | amount: BigInt, 92 | price: BigInt, 93 | ): TakerBid { 94 | const mockEvent = newMockEvent(); 95 | const newTakerBidEvent = new TakerBid( 96 | mockEvent.address, 97 | mockEvent.logIndex, 98 | mockEvent.transactionLogIndex, 99 | mockEvent.logType, 100 | mockEvent.block, 101 | mockEvent.transaction, 102 | mockEvent.parameters, 103 | mockEvent.receipt, 104 | ); 105 | 106 | newTakerBidEvent.parameters = []; 107 | 108 | const orderHashParam = new ethereum.EventParam("orderHash", ethereum.Value.fromBytes(orderHash)); 109 | const orderNonceParam = new ethereum.EventParam("orderNonce", ethereum.Value.fromSignedBigInt(orderNonce)); 110 | const takerParam = new ethereum.EventParam("taker", ethereum.Value.fromAddress(taker)); 111 | const makerParam = new ethereum.EventParam("maker", ethereum.Value.fromAddress(maker)); 112 | const strategyParam = new ethereum.EventParam("strategy", ethereum.Value.fromAddress(strategy)); 113 | const currencyParam = new ethereum.EventParam("currency", ethereum.Value.fromAddress(currency)); 114 | const collectionParam = new ethereum.EventParam("collection", ethereum.Value.fromAddress(collection)); 115 | const tokenIdParam = new ethereum.EventParam("tokenId", ethereum.Value.fromSignedBigInt(tokenId)); 116 | const amountParam = new ethereum.EventParam("amount", ethereum.Value.fromSignedBigInt(amount)); 117 | const priceParam = new ethereum.EventParam("price", ethereum.Value.fromSignedBigInt(price)); 118 | 119 | newTakerBidEvent.parameters.push(orderHashParam); 120 | newTakerBidEvent.parameters.push(orderNonceParam); 121 | newTakerBidEvent.parameters.push(takerParam); 122 | newTakerBidEvent.parameters.push(makerParam); 123 | newTakerBidEvent.parameters.push(strategyParam); 124 | newTakerBidEvent.parameters.push(currencyParam); 125 | newTakerBidEvent.parameters.push(collectionParam); 126 | newTakerBidEvent.parameters.push(tokenIdParam); 127 | newTakerBidEvent.parameters.push(amountParam); 128 | newTakerBidEvent.parameters.push(priceParam); 129 | 130 | return newTakerBidEvent; 131 | } 132 | 133 | /** 134 | * @param collection 135 | * @param tokenId 136 | * @param royaltyRecipient 137 | * @param currency 138 | * @param amount 139 | * @returns RoyaltyPayment Event 140 | */ 141 | export function createRoyaltyPaymentEvent( 142 | collection: Address, 143 | tokenId: BigInt, 144 | royaltyRecipient: Address, 145 | currency: Address, 146 | amount: BigInt, 147 | ): RoyaltyPayment { 148 | const mockEvent = newMockEvent(); 149 | const newRoyaltyPaymentEvent = new RoyaltyPayment( 150 | mockEvent.address, 151 | mockEvent.logIndex, 152 | mockEvent.transactionLogIndex, 153 | mockEvent.logType, 154 | mockEvent.block, 155 | mockEvent.transaction, 156 | mockEvent.parameters, 157 | mockEvent.receipt, 158 | ); 159 | 160 | newRoyaltyPaymentEvent.parameters = []; 161 | 162 | const collectionParam = new ethereum.EventParam("collection", ethereum.Value.fromAddress(collection)); 163 | const tokenIdParam = new ethereum.EventParam("tokenId", ethereum.Value.fromSignedBigInt(tokenId)); 164 | const royaltyRecipientParam = new ethereum.EventParam( 165 | "royaltyRecipient", 166 | ethereum.Value.fromAddress(royaltyRecipient), 167 | ); 168 | const currencyParam = new ethereum.EventParam("currency", ethereum.Value.fromAddress(currency)); 169 | const amountParam = new ethereum.EventParam("amount", ethereum.Value.fromSignedBigInt(amount)); 170 | 171 | newRoyaltyPaymentEvent.parameters.push(collectionParam); 172 | newRoyaltyPaymentEvent.parameters.push(tokenIdParam); 173 | newRoyaltyPaymentEvent.parameters.push(royaltyRecipientParam); 174 | newRoyaltyPaymentEvent.parameters.push(currencyParam); 175 | newRoyaltyPaymentEvent.parameters.push(amountParam); 176 | 177 | return newRoyaltyPaymentEvent; 178 | } 179 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/abis/LightMultiRewardsDistributor.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": false, 7 | "internalType": "address", 8 | "name": "user", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": false, 13 | "internalType": "uint256", 14 | "name": "rewardRound", 15 | "type": "uint256" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "totalAmount", 21 | "type": "uint256" 22 | }, 23 | { 24 | "indexed": false, 25 | "internalType": "uint8[]", 26 | "name": "treeIds", 27 | "type": "uint8[]" 28 | }, 29 | { 30 | "indexed": false, 31 | "internalType": "uint256[]", 32 | "name": "amounts", 33 | "type": "uint256[]" 34 | } 35 | ], 36 | "name": "Claim", 37 | "type": "event" 38 | } 39 | ] 40 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/mappings/utils/config/addresses-goerli.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "@graphprotocol/graph-ts"; 2 | 3 | export const AGGREGATOR_ADDRESS = Address.fromString("0x63c38b3be3ef075a00a5edaec36f499088c7334c"); 4 | export const FEE_SHARING_ADDRESS = Address.fromString("0x8a29e7b241a32158fd7f267d484199aad8dd2e31"); 5 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/mappings/utils/config/addresses-mainnet.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "@graphprotocol/graph-ts"; 2 | 3 | export const AGGREGATOR_ADDRESS = Address.fromString("0x3ab16af1315dc6c95f83cbf522fecf98d00fd9ba"); 4 | export const FEE_SHARING_ADDRESS = Address.fromString("0xbcd7254a1d759efa08ec7c3291b2e85c5dcc12ce"); 5 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/mappings/utils/config/addresses.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "@graphprotocol/graph-ts"; 2 | 3 | export const AGGREGATOR_ADDRESS = Address.fromString("0x0000000000000000000000000000000000000000"); 4 | export const FEE_SHARING_ADDRESS = Address.fromString("0x0000000000000000000000000000000000000000"); 5 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/mappings/utils/initializeDailySnapshot.ts: -------------------------------------------------------------------------------- 1 | import { BigInt } from "@graphprotocol/graph-ts"; 2 | import { ZERO_BD, ZERO_BI } from "../../../../helpers/constants"; 3 | import { DailySnapshot } from "../../generated/schema"; 4 | 5 | export function initializeDailySnapshot(ID: string, dayStartTimestamp: BigInt): DailySnapshot { 6 | const dailySnapshot = new DailySnapshot(ID); 7 | dailySnapshot.date = dayStartTimestamp; 8 | dailySnapshot.activeStakers = ZERO_BI; 9 | dailySnapshot.newStakers = ZERO_BI; 10 | dailySnapshot.removedStakers = ZERO_BI; 11 | dailySnapshot.aggregatorActiveUsers = ZERO_BI; 12 | dailySnapshot.aggregatorNewUsers = ZERO_BI; 13 | dailySnapshot.aggregatorRemovedUsers = ZERO_BI; 14 | dailySnapshot.aggregatorDailyInflowLOOKS = ZERO_BD; 15 | dailySnapshot.aggregatorDailyOutflowLOOKS = ZERO_BD; 16 | dailySnapshot.aggregatorTotalStakedLOOKS = ZERO_BD; 17 | dailySnapshot.aggregatorTotalWETHSold = ZERO_BD; 18 | dailySnapshot.aggregatorTotalLOOKSReceived = ZERO_BD; 19 | dailySnapshot.aggregatorConversionCount = ZERO_BI; 20 | dailySnapshot.feeSharingActiveUsers = ZERO_BI; 21 | dailySnapshot.feeSharingNewUsers = ZERO_BI; 22 | dailySnapshot.feeSharingRemovedUsers = ZERO_BI; 23 | dailySnapshot.feeSharingDailyInflowLOOKS = ZERO_BD; 24 | dailySnapshot.feeSharingDailyOutflowLOOKS = ZERO_BD; 25 | dailySnapshot.feeSharingTotalStakedLOOKS = ZERO_BD; 26 | return dailySnapshot; 27 | } 28 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/mappings/utils/initializeOverview.ts: -------------------------------------------------------------------------------- 1 | import { ONE_BI, ZERO_BD, ZERO_BI } from "../../../../helpers/constants"; 2 | import { Overview } from "../../generated/schema"; 3 | 4 | export function initializeOverview(): Overview { 5 | const overview = new Overview(ONE_BI.toHex()); 6 | overview.aggregatorActiveUsers = ZERO_BI; 7 | overview.aggregatorTotalStakedLOOKS = ZERO_BD; 8 | overview.feeSharingActiveUsers = ZERO_BI; 9 | overview.feeSharingTotalStakedLOOKS = ZERO_BD; 10 | overview.activeStakers = ZERO_BI; 11 | return overview; 12 | } 13 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/mappings/utils/initializeUser.ts: -------------------------------------------------------------------------------- 1 | import { User } from "../../generated/schema"; 2 | import { ZERO_BI, ZERO_BD } from "../../../../helpers/constants"; 3 | 4 | export function initializeUser(userID: string): User { 5 | const user = new User(userID); 6 | user.airdropAmount = ZERO_BD; 7 | user.airdropClaimDate = ZERO_BI; 8 | user.aggregatorAdjustedDepositAmount = ZERO_BD; 9 | user.aggregatorTotalCollectedLOOKS = ZERO_BD; 10 | user.aggregatorLastDepositDate = ZERO_BI; 11 | user.aggregatorLastWithdrawDate = ZERO_BI; 12 | user.aggregatorIsActive = false; 13 | user.feeSharingAdjustedDepositAmount = ZERO_BD; 14 | user.feeSharingTotalCollectedLOOKS = ZERO_BD; 15 | user.feeSharingTotalCollectedWETH = ZERO_BD; 16 | user.feeSharingLastDepositDate = ZERO_BI; 17 | user.feeSharingLastWithdrawDate = ZERO_BI; 18 | user.feeSharingLastHarvestDate = ZERO_BI; 19 | user.feeSharingIsActive = false; 20 | user.listingRewardsAmount = ZERO_BD; 21 | user.listingRewardsLastClaimDate = ZERO_BI; 22 | user.stakingPoolUniswapV2TotalCollectedLOOKS = ZERO_BD; 23 | user.stakingPoolUniswapV2LastDepositDate = ZERO_BI; 24 | user.stakingPoolUniswapV2LastHarvestDate = ZERO_BI; 25 | user.stakingPoolUniswapV2LastWithdrawDate = ZERO_BI; 26 | user.tradingRewardsAmount = ZERO_BD; 27 | user.tradingRewardsLastClaimDate = ZERO_BI; 28 | return user; 29 | } 30 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/mappings/utils/rpc-calls/fetchShares.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt } from "@graphprotocol/graph-ts"; 2 | import { AGGREGATOR_ADDRESS, FEE_SHARING_ADDRESS } from "../config/addresses"; 3 | import { AggregatorFeeSharingWithUniswapV3 } from "../../../generated/AggregatorFeeSharingWithUniswapV3/AggregatorFeeSharingWithUniswapV3"; 4 | import { FeeSharingSystem } from "../../../generated/FeeSharingSystem/FeeSharingSystem"; 5 | 6 | export function fetchSharesAggregator(user: Address): BigInt { 7 | const aggregator = AggregatorFeeSharingWithUniswapV3.bind(AGGREGATOR_ADDRESS); 8 | const userInfo = aggregator.try_userInfo(user); 9 | if (!userInfo.reverted) { 10 | return userInfo.value; 11 | } 12 | return BigInt.zero(); 13 | } 14 | 15 | export function fetchSharesFeeSharingSystem(user: Address): BigInt { 16 | const feeSharingSystem = FeeSharingSystem.bind(FEE_SHARING_ADDRESS); 17 | const userInfo = feeSharingSystem.try_userInfo(user); 18 | if (!userInfo.reverted) { 19 | return userInfo.value.value0; 20 | } 21 | return BigInt.zero(); 22 | } 23 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/mappings/utils/rpc-calls/fetchTotalAmountStaked.ts: -------------------------------------------------------------------------------- 1 | import { BigDecimal } from "@graphprotocol/graph-ts"; 2 | import { AGGREGATOR_ADDRESS, FEE_SHARING_ADDRESS } from "../config/addresses"; 3 | import { AggregatorFeeSharingWithUniswapV3 } from "../../../generated/AggregatorFeeSharingWithUniswapV3/AggregatorFeeSharingWithUniswapV3"; 4 | import { FeeSharingSystem } from "../../../generated/FeeSharingSystem/FeeSharingSystem"; 5 | import { toBigDecimal } from "../../../../../helpers/utils"; 6 | import { ZERO_BD } from "../../../../../helpers/constants"; 7 | 8 | export function fetchTotalAmountStakedAggregator(): BigDecimal { 9 | const aggregator = AggregatorFeeSharingWithUniswapV3.bind(AGGREGATOR_ADDRESS); 10 | const totalShares = aggregator.try_totalShares(); 11 | const pricePerShareInLOOKS = aggregator.try_calculateSharePriceInLOOKS(); 12 | if (!totalShares.reverted && !pricePerShareInLOOKS.reverted) { 13 | return toBigDecimal(totalShares.value).times(toBigDecimal(pricePerShareInLOOKS.value)); 14 | } 15 | return ZERO_BD; 16 | } 17 | 18 | export function fetchTotalAmountStakedFeeSharing(): BigDecimal { 19 | const feeSharingSystem = FeeSharingSystem.bind(FEE_SHARING_ADDRESS); 20 | const totalShares = feeSharingSystem.try_totalShares(); 21 | const pricePerShareInLOOKS = feeSharingSystem.try_calculateSharePriceInLOOKS(); 22 | if (!totalShares.reverted && !pricePerShareInLOOKS.reverted) { 23 | return toBigDecimal(totalShares.value).times(toBigDecimal(pricePerShareInLOOKS.value)); 24 | } 25 | return ZERO_BD; 26 | } 27 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/mappings/utils/setupOverviewAndDailySnapshot.ts: -------------------------------------------------------------------------------- 1 | import { BigInt } from "@graphprotocol/graph-ts"; 2 | import { ONE_BI, ONE_DAY_BI, ZERO_BI } from "../../../../helpers/constants"; 3 | import { DailySnapshot, Overview } from "../../generated/schema"; 4 | import { initializeDailySnapshot } from "./initializeDailySnapshot"; 5 | import { initializeOverview } from "./initializeOverview"; 6 | import { fetchTotalAmountStakedAggregator, fetchTotalAmountStakedFeeSharing } from "./rpc-calls/fetchTotalAmountStaked"; 7 | 8 | export function setupOverviewAndDailySnapshot(timestamp: BigInt): DailySnapshot { 9 | const dailyTimestampBigInt = ONE_DAY_BI; 10 | const dayID = timestamp.div(dailyTimestampBigInt); 11 | const dayStartTimestamp = dayID.times(dailyTimestampBigInt); 12 | const ID = dayID.toString(); 13 | 14 | let dailySnapshot = DailySnapshot.load(ID); 15 | if (dailySnapshot === null) { 16 | let overview = Overview.load(ONE_BI.toHex()); 17 | if (overview === null) { 18 | overview = initializeOverview(); 19 | } 20 | 21 | dailySnapshot = initializeDailySnapshot(ID, dayStartTimestamp); 22 | dailySnapshot.activeStakers = overview.activeStakers; 23 | dailySnapshot.aggregatorActiveUsers = overview.aggregatorActiveUsers; 24 | dailySnapshot.feeSharingActiveUsers = overview.feeSharingActiveUsers; 25 | dailySnapshot.aggregatorTotalStakedLOOKS = overview.aggregatorTotalStakedLOOKS; 26 | dailySnapshot.feeSharingTotalStakedLOOKS = overview.feeSharingTotalStakedLOOKS; 27 | 28 | if (overview.aggregatorActiveUsers.gt(ZERO_BI)) { 29 | overview.aggregatorTotalStakedLOOKS = fetchTotalAmountStakedAggregator(); 30 | } 31 | 32 | if (overview.feeSharingActiveUsers.gt(ZERO_BI)) { 33 | overview.feeSharingTotalStakedLOOKS = fetchTotalAmountStakedFeeSharing().minus( 34 | overview.aggregatorTotalStakedLOOKS, 35 | ); 36 | } 37 | overview.save(); 38 | } 39 | return dailySnapshot; 40 | } 41 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/mappings/utils/updateDailyData.ts: -------------------------------------------------------------------------------- 1 | import { BigInt, BigDecimal } from "@graphprotocol/graph-ts"; 2 | import { Overview } from "../../generated/schema"; 3 | import { ONE_BI } from "../../../../helpers/constants"; 4 | import { setupOverviewAndDailySnapshot } from "./setupOverviewAndDailySnapshot"; 5 | 6 | export function updateDailySnapshotDepositFeeSharing(timestamp: BigInt, amount: BigDecimal): void { 7 | const dailySnapshot = setupOverviewAndDailySnapshot(timestamp); 8 | dailySnapshot.feeSharingDailyInflowLOOKS = dailySnapshot.feeSharingDailyInflowLOOKS.plus(amount); 9 | dailySnapshot.save(); 10 | } 11 | 12 | export function updateDailySnapshotWithdrawFeeSharing(timestamp: BigInt, amount: BigDecimal): void { 13 | const dailySnapshot = setupOverviewAndDailySnapshot(timestamp); 14 | dailySnapshot.feeSharingDailyOutflowLOOKS = dailySnapshot.feeSharingDailyOutflowLOOKS.plus(amount); 15 | dailySnapshot.save(); 16 | } 17 | 18 | export function updateNumberUsersFeeSharing(timestamp: BigInt, isIncrease: boolean, isStakingElsewhere: boolean): void { 19 | const dailySnapshot = setupOverviewAndDailySnapshot(timestamp); 20 | const overview = Overview.load(ONE_BI.toHex()); 21 | if (overview === null) { 22 | // This should be impossible since it is initialized before 23 | return; 24 | } 25 | 26 | if (isIncrease) { 27 | dailySnapshot.feeSharingNewUsers = dailySnapshot.feeSharingNewUsers.plus(ONE_BI); 28 | dailySnapshot.feeSharingActiveUsers = dailySnapshot.feeSharingActiveUsers.plus(ONE_BI); 29 | overview.feeSharingActiveUsers = overview.feeSharingActiveUsers.plus(ONE_BI); 30 | if (!isStakingElsewhere) { 31 | overview.activeStakers = overview.activeStakers.plus(ONE_BI); 32 | dailySnapshot.activeStakers = dailySnapshot.activeStakers.plus(ONE_BI); 33 | dailySnapshot.newStakers = dailySnapshot.newStakers.plus(ONE_BI); 34 | } 35 | } else { 36 | dailySnapshot.feeSharingRemovedUsers = dailySnapshot.feeSharingRemovedUsers.plus(ONE_BI); 37 | dailySnapshot.feeSharingActiveUsers = dailySnapshot.feeSharingActiveUsers.minus(ONE_BI); 38 | overview.feeSharingActiveUsers = overview.feeSharingActiveUsers.minus(ONE_BI); 39 | if (!isStakingElsewhere) { 40 | overview.activeStakers = overview.activeStakers.minus(ONE_BI); 41 | dailySnapshot.activeStakers = dailySnapshot.activeStakers.minus(ONE_BI); 42 | dailySnapshot.removedStakers = dailySnapshot.removedStakers.plus(ONE_BI); 43 | } 44 | } 45 | 46 | dailySnapshot.save(); 47 | overview.save(); 48 | } 49 | 50 | export function updateDailySnapshotDepositAggregator(timestamp: BigInt, amount: BigDecimal): void { 51 | const dailySnapshot = setupOverviewAndDailySnapshot(timestamp); 52 | dailySnapshot.aggregatorDailyInflowLOOKS = dailySnapshot.aggregatorDailyInflowLOOKS.plus(amount); 53 | dailySnapshot.save(); 54 | } 55 | 56 | export function updateDailySnapshotWithdrawAggregator(timestamp: BigInt, amount: BigDecimal): void { 57 | const dailySnapshot = setupOverviewAndDailySnapshot(timestamp); 58 | dailySnapshot.aggregatorDailyOutflowLOOKS = dailySnapshot.aggregatorDailyOutflowLOOKS.plus(amount); 59 | dailySnapshot.save(); 60 | } 61 | 62 | export function updateNumberUsersAggregator(timestamp: BigInt, isIncrease: boolean, isStakingElsewhere: boolean): void { 63 | const dailySnapshot = setupOverviewAndDailySnapshot(timestamp); 64 | const overview = Overview.load(ONE_BI.toHex()); 65 | if (overview === null) { 66 | // This should be impossible since it is initialized before 67 | return; 68 | } 69 | if (isIncrease) { 70 | dailySnapshot.aggregatorNewUsers = dailySnapshot.aggregatorNewUsers.plus(ONE_BI); 71 | dailySnapshot.aggregatorActiveUsers = dailySnapshot.aggregatorActiveUsers.plus(ONE_BI); 72 | overview.aggregatorActiveUsers = overview.aggregatorActiveUsers.plus(ONE_BI); 73 | if (!isStakingElsewhere) { 74 | overview.activeStakers = overview.activeStakers.plus(ONE_BI); 75 | dailySnapshot.activeStakers = dailySnapshot.activeStakers.plus(ONE_BI); 76 | dailySnapshot.newStakers = dailySnapshot.newStakers.plus(ONE_BI); 77 | } 78 | } else { 79 | dailySnapshot.aggregatorRemovedUsers = dailySnapshot.aggregatorRemovedUsers.plus(ONE_BI); 80 | dailySnapshot.aggregatorActiveUsers = dailySnapshot.aggregatorActiveUsers.minus(ONE_BI); 81 | overview.aggregatorActiveUsers = overview.aggregatorActiveUsers.minus(ONE_BI); 82 | if (!isStakingElsewhere) { 83 | overview.activeStakers = overview.activeStakers.minus(ONE_BI); 84 | dailySnapshot.activeStakers = dailySnapshot.activeStakers.minus(ONE_BI); 85 | dailySnapshot.removedStakers = dailySnapshot.removedStakers.plus(ONE_BI); 86 | } 87 | } 88 | 89 | dailySnapshot.save(); 90 | overview.save(); 91 | } 92 | 93 | export function updateDailySnapshotConversion( 94 | timestamp: BigInt, 95 | amountSold: BigDecimal, 96 | amountReceived: BigDecimal, 97 | ): void { 98 | const dailySnapshot = setupOverviewAndDailySnapshot(timestamp); 99 | dailySnapshot.aggregatorTotalStakedLOOKS = dailySnapshot.aggregatorTotalStakedLOOKS.plus(amountSold); 100 | dailySnapshot.aggregatorTotalLOOKSReceived = dailySnapshot.aggregatorTotalLOOKSReceived.plus(amountReceived); 101 | dailySnapshot.aggregatorConversionCount = dailySnapshot.aggregatorConversionCount.plus(ONE_BI); 102 | dailySnapshot.save(); 103 | } 104 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/matchstick.yaml: -------------------------------------------------------------------------------- 1 | libsFolder: ../../node_modules/ 2 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "FeeSharingSystem": { 4 | "address": "0xbcd7254a1d759efa08ec7c3291b2e85c5dcc12ce", 5 | "startBlock": 13975044 6 | }, 7 | "StakingPoolForUniswapV2Tokens": { 8 | "address": "0x2a70e7f51f6cd40c3e9956aa964137668cbfadc5", 9 | "startBlock": 13975044 10 | }, 11 | "LooksRareAirdrop": { 12 | "address": "0xa35dce3e0e6ceb67a30b8d7f4aee721c949b5970", 13 | "startBlock": 13975044 14 | }, 15 | "TradingRewardsDistributor": { 16 | "address": "0x453c1208b400fe47acf275315f14e8f9f9fbc3cd", 17 | "startBlock": 13975044 18 | }, 19 | "AggregatorFeeSharingWithUniswapV3": { 20 | "address": "0x3ab16af1315dc6c95f83cbf522fecf98d00fd9ba", 21 | "startBlock": 14477871 22 | }, 23 | "MultiRewardsDistributor": { 24 | "address": "0x0554f068365ed43dcc98dcd7fd7a8208a5638c72", 25 | "startBlock": 14619105 26 | } 27 | }, 28 | "goerli": { 29 | "FeeSharingSystem": { 30 | "address": "0x8a29e7b241a32158fd7f267d484199aad8dd2e31", 31 | "startBlock": 7532819 32 | }, 33 | "StakingPoolForUniswapV2Tokens": { 34 | "address": "0x0000000000000000000000000000000000000000", 35 | "startBlock": 99999999999999999999999 36 | }, 37 | "LooksRareAirdrop": { 38 | "address": "0x0000000000000000000000000000000000000000", 39 | "startBlock": 99999999999999999999999 40 | }, 41 | "TradingRewardsDistributor": { 42 | "address": "0x0000000000000000000000000000000000000000", 43 | "startBlock": 99999999999999999999999 44 | }, 45 | "AggregatorFeeSharingWithUniswapV3": { 46 | "address": "0x63c38b3be3ef075a00a5edaec36f499088c7334c", 47 | "startBlock": 7532819 48 | }, 49 | "MultiRewardsDistributor": { 50 | "address": "0xdc6dc8d1b784890ba2c38947218f89e963ec2673", 51 | "startBlock": 7532819 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "looks-distribution", 3 | "description": "LooksRare LOOKS distribution system", 4 | "version": "1.0.0", 5 | "repository": "git@github.com:looksrare/subgraph.git", 6 | "author": "LooksRare", 7 | "license": "MIT", 8 | "scripts": { 9 | "codegen": "graph codegen subgraph.yaml", 10 | "build:goerli": "graph build subgraph.yaml --network goerli", 11 | "build:mainnet": "graph build subgraph.yaml --network mainnet", 12 | "deploy:goerli": "cp ./mappings/utils/config/addresses.ts ./mappings/utils/config/addresses-copy.ts && cp ./mappings/utils/config/addresses-goerli.ts ./mappings/utils/config/addresses.ts && graph deploy --product hosted-service 0xjurassicpunk/looks-distribution subgraph.yaml --network goerli && cp ./mappings/utils/config/addresses-copy.ts ./mappings/utils/config/addresses.ts && rm ./mappings/utils/config/addresses-copy.ts", 13 | "deploy:mainnet": "cp ./mappings/utils/config/addresses.ts ./mappings/utils/config/addresses-copy.ts && cp ./mappings/utils/config/addresses-mainnet.ts ./mappings/utils/config/addresses.ts && graph deploy --product hosted-service looksrare/looks-distribution subgraph.yaml --network mainnet && cp ./mappings/utils/config/addresses-copy.ts ./mappings/utils/config/addresses.ts && rm ./mappings/utils/config/addresses-copy.ts", 14 | "deploy:studio": "cp ./mappings/utils/config/addresses.ts ./mappings/utils/config/addresses-copy.ts && cp ./mappings/utils/config/addresses-mainnet.ts ./mappings/utils/config/addresses.ts && graph codegen subgraph.yaml && graph build subgraph.yaml && graph deploy --studio looks-distribution --network mainnet && cp ./mappings/utils/config/addresses-copy.ts ./mappings/utils/config/addresses.ts && rm ./mappings/utils/config/addresses-copy.ts", 15 | "test": "graph test -r", 16 | "test:lerna": "graph codegen subgraph.yaml && graph build subgraph.yaml && graph test -r" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/schema.graphql: -------------------------------------------------------------------------------- 1 | """ 2 | User 3 | """ 4 | type User @entity { 5 | "ID" 6 | id: ID! 7 | 8 | "Airdrop - Amount claimed (LOOKS)" 9 | airdropAmount: BigDecimal! 10 | 11 | "Airdrop - Claim date" 12 | airdropClaimDate: BigInt! 13 | 14 | "Aggregator - Adjusted deposit amount (LOOKS)" 15 | aggregatorAdjustedDepositAmount: BigDecimal! 16 | 17 | "Aggregator - Total LOOKS collected" 18 | aggregatorTotalCollectedLOOKS: BigDecimal! 19 | 20 | "Aggregator - Last deposit date" 21 | aggregatorLastDepositDate: BigInt! 22 | 23 | "Aggregator - Last withdraw date" 24 | aggregatorLastWithdrawDate: BigInt! 25 | 26 | "Aggregator - Is active" 27 | aggregatorIsActive: Boolean! 28 | 29 | "Fee Sharing - Adjusted deposit amount (LOOKS)" 30 | feeSharingAdjustedDepositAmount: BigDecimal! 31 | 32 | "Fee Sharing - Total LOOKS collected" 33 | feeSharingTotalCollectedLOOKS: BigDecimal! 34 | 35 | "Fee Sharing - Total WETH collected" 36 | feeSharingTotalCollectedWETH: BigDecimal! 37 | 38 | "Fee Sharing - Last deposit date" 39 | feeSharingLastDepositDate: BigInt! 40 | 41 | "Fee Sharing - Last harvest date" 42 | feeSharingLastHarvestDate: BigInt! 43 | 44 | "Fee Sharing - Last withdraw date" 45 | feeSharingLastWithdrawDate: BigInt! 46 | 47 | "Fee Sharing - Is active" 48 | feeSharingIsActive: Boolean! 49 | 50 | "Listing Rewards - Total LOOKS collected" 51 | listingRewardsAmount: BigDecimal! 52 | 53 | "Listing Rewards - Last claim date" 54 | listingRewardsLastClaimDate: BigInt! 55 | 56 | "Trading Rewards - Total LOOKS collected" 57 | tradingRewardsAmount: BigDecimal! 58 | 59 | "Trading Rewards - Last claim date" 60 | tradingRewardsLastClaimDate: BigInt! 61 | 62 | "UniswapV2 Staking - Total LOOKS collected" 63 | stakingPoolUniswapV2TotalCollectedLOOKS: BigDecimal! 64 | 65 | "UniswapV2 Staking - Last deposit date" 66 | stakingPoolUniswapV2LastDepositDate: BigInt! 67 | 68 | "UniswapV2 Staking - Last harvest date" 69 | stakingPoolUniswapV2LastHarvestDate: BigInt! 70 | 71 | "UniswapV2 Staking - Last withdraw date" 72 | stakingPoolUniswapV2LastWithdrawDate: BigInt! 73 | } 74 | 75 | """ 76 | DailySnapshot 77 | """ 78 | type DailySnapshot @entity { 79 | "ID" 80 | id: ID! 81 | 82 | "Date" 83 | date: BigInt! 84 | 85 | "Number unique active stakers (Aggregator + Fee Sharing)" 86 | activeStakers: BigInt! 87 | 88 | "Daily new stakers (Aggregator + Fee Sharing)" 89 | newStakers: BigInt! 90 | 91 | "Daily removed stakers (Aggregator + Fee Sharing)" 92 | removedStakers: BigInt! 93 | 94 | "Aggregator - Number active users" 95 | aggregatorActiveUsers: BigInt! 96 | 97 | "Aggregator - Daily new users" 98 | aggregatorNewUsers: BigInt! 99 | 100 | "Aggregator - Daily removed users" 101 | aggregatorRemovedUsers: BigInt! 102 | 103 | "Aggregator - Daily inflow (LOOKS)" 104 | aggregatorDailyInflowLOOKS: BigDecimal! 105 | 106 | "Aggregator - Daily outflow (LOOKS)" 107 | aggregatorDailyOutflowLOOKS: BigDecimal! 108 | 109 | "Aggregator - Total LOOKS staked" 110 | aggregatorTotalStakedLOOKS: BigDecimal! 111 | 112 | "Aggregator - Total WETH sold" 113 | aggregatorTotalWETHSold: BigDecimal! 114 | 115 | "Aggregator - Total LOOKS received from sales" 116 | aggregatorTotalLOOKSReceived: BigDecimal! 117 | 118 | "Aggregator - Number of conversions" 119 | aggregatorConversionCount: BigInt! 120 | 121 | "Fee Sharing - Number active users" 122 | feeSharingActiveUsers: BigInt! 123 | 124 | "Fee Sharing - Daily new users" 125 | feeSharingNewUsers: BigInt! 126 | 127 | "Fee Sharing - Daily removed users" 128 | feeSharingRemovedUsers: BigInt! 129 | 130 | "Fee Sharing - Daily inflow (LOOKS)" 131 | feeSharingDailyInflowLOOKS: BigDecimal! 132 | 133 | "Fee Sharing - Daily outflow (LOOKS)" 134 | feeSharingDailyOutflowLOOKS: BigDecimal! 135 | 136 | "Fee Sharing - Total LOOKS staked" 137 | feeSharingTotalStakedLOOKS: BigDecimal! 138 | } 139 | 140 | """ 141 | Overview 142 | """ 143 | type Overview @entity { 144 | "ID" 145 | id: ID! 146 | 147 | "Number unique active stakers (Aggregator + Fee Sharing)" 148 | activeStakers: BigInt! 149 | 150 | "Aggregator - Number active users" 151 | aggregatorActiveUsers: BigInt! 152 | 153 | "Aggregator - Total LOOKS staked" 154 | aggregatorTotalStakedLOOKS: BigDecimal! 155 | 156 | "Fee Sharing - Number active users" 157 | feeSharingActiveUsers: BigInt! 158 | 159 | "Fee Sharing - Total LOOKS staked" 160 | feeSharingTotalStakedLOOKS: BigDecimal! 161 | } 162 | 163 | """ 164 | RewardPeriod 165 | """ 166 | type RewardPeriod @entity { 167 | "ID (transaction hash)" 168 | id: ID! 169 | 170 | "Block number" 171 | block: BigInt! 172 | 173 | "Date (timestamp)" 174 | date: BigInt! 175 | 176 | "Period length (in blocks)" 177 | numberBlocks: BigInt! 178 | 179 | "Reward amount for the period (in WETH)" 180 | reward: BigDecimal! 181 | 182 | "Reward per block for the period (in WETH)" 183 | rewardPerBlock: BigDecimal! 184 | } 185 | 186 | """ 187 | AggregatorConversion 188 | """ 189 | type AggregatorConversion @entity { 190 | "ID (transaction hash)" 191 | id: ID! 192 | 193 | "Block number" 194 | block: BigInt! 195 | 196 | "Date (timestamp)" 197 | date: BigInt! 198 | 199 | "Reward sold (in WETH)" 200 | amountSold: BigDecimal! 201 | 202 | "Reward received (in LOOKS)" 203 | amountReceived: BigDecimal! 204 | 205 | "Price of 1 WETH in LOOKS" 206 | priceOfETHInLOOKS: BigDecimal! 207 | } 208 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | description: LooksRare LOOKS distribution contracts 3 | repository: https://github.com/looksrare 4 | schema: 5 | file: ./schema.graphql 6 | dataSources: 7 | - kind: ethereum/contract 8 | name: FeeSharingSystem 9 | network: mainnet 10 | source: 11 | abi: FeeSharingSystem 12 | address: "0xbcd7254a1d759efa08ec7c3291b2e85c5dcc12ce" 13 | startBlock: 13975044 14 | mapping: 15 | kind: ethereum/events 16 | apiVersion: 0.0.7 17 | language: wasm/assemblyscript 18 | file: ./mappings/index.ts 19 | entities: 20 | - DailySnapshot 21 | - Overview 22 | - RewardPeriod 23 | - User 24 | abis: 25 | - name: AggregatorFeeSharingWithUniswapV3 26 | file: ../../node_modules/@looksrare/sdk/dist/AggregatorFeeSharingWithUniswapV3Abi.json 27 | - name: FeeSharingSystem 28 | file: ../../node_modules/@looksrare/sdk/dist/FeeSharingSystemAbi.json 29 | callHandlers: 30 | - function: withdraw(uint256,bool) 31 | handler: handleCallWithdrawFeeSharing 32 | - function: withdrawAll(bool) 33 | handler: handleCallWithdrawAllFeeSharing 34 | eventHandlers: 35 | - event: Deposit(indexed address,uint256,uint256) 36 | handler: handleDepositFeeSharing 37 | - event: Harvest(indexed address,uint256) 38 | handler: handleHarvestFeeSharing 39 | - event: Withdraw(indexed address,uint256,uint256) 40 | handler: handleWithdrawFeeSharing 41 | - event: NewRewardPeriod(uint256,uint256,uint256) 42 | handler: handleNewRewardPeriod 43 | - kind: ethereum/contract 44 | name: StakingPoolForUniswapV2Tokens 45 | network: mainnet 46 | source: 47 | abi: StakingPoolForUniswapV2Tokens 48 | address: "0x2a70e7f51f6cd40c3e9956aa964137668cbfadc5" 49 | startBlock: 13975044 50 | mapping: 51 | kind: ethereum/events 52 | apiVersion: 0.0.7 53 | language: wasm/assemblyscript 54 | file: ./mappings/index.ts 55 | entities: 56 | - User 57 | abis: 58 | - name: StakingPoolForUniswapV2Tokens 59 | file: ../../node_modules/@looksrare/sdk/dist/StakingPoolForUniswapV2TokensAbi.json 60 | eventHandlers: 61 | - event: Deposit(indexed address,uint256,uint256) 62 | handler: handleDepositStakingV2 63 | - event: Harvest(indexed address,uint256) 64 | handler: handleHarvestStakingV2 65 | - event: Withdraw(indexed address,uint256,uint256) 66 | handler: handleWithdrawStakingV2 67 | - kind: ethereum/contract 68 | name: LooksRareAirdrop 69 | network: mainnet 70 | source: 71 | abi: LooksRareAirdrop 72 | address: "0xa35dce3e0e6ceb67a30b8d7f4aee721c949b5970" 73 | startBlock: 13975044 74 | mapping: 75 | kind: ethereum/events 76 | apiVersion: 0.0.7 77 | language: wasm/assemblyscript 78 | file: ./mappings/index.ts 79 | entities: 80 | - User 81 | abis: 82 | - name: LooksRareAirdrop 83 | file: ../../node_modules/@looksrare/sdk/dist/LooksRareAirdropAbi.json 84 | eventHandlers: 85 | - event: AirdropRewardsClaim(indexed address,uint256) 86 | handler: handleAirdropClaim 87 | - kind: ethereum/contract 88 | name: TradingRewardsDistributor 89 | network: mainnet 90 | source: 91 | abi: TradingRewardsDistributor 92 | address: "0x453c1208b400fe47acf275315f14e8f9f9fbc3cd" 93 | startBlock: 13975044 94 | mapping: 95 | kind: ethereum/events 96 | apiVersion: 0.0.7 97 | language: wasm/assemblyscript 98 | file: ./mappings/index.ts 99 | entities: 100 | - User 101 | abis: 102 | - name: TradingRewardsDistributor 103 | file: ../../node_modules/@looksrare/sdk/dist/TradingRewardsDistributorAbi.json 104 | eventHandlers: 105 | - event: RewardsClaim(indexed address,indexed uint256,uint256) 106 | handler: handleTradingRewardsClaim 107 | - kind: ethereum/contract 108 | name: AggregatorFeeSharingWithUniswapV3 109 | network: mainnet 110 | source: 111 | abi: AggregatorFeeSharingWithUniswapV3 112 | address: "0x3ab16af1315dc6c95f83cbf522fecf98d00fd9ba" 113 | startBlock: 14477871 114 | mapping: 115 | kind: ethereum/events 116 | apiVersion: 0.0.7 117 | language: wasm/assemblyscript 118 | file: ./mappings/index.ts 119 | entities: 120 | - DailySnapshot 121 | - Overview 122 | - AggregatorConversion 123 | - User 124 | abis: 125 | - name: AggregatorFeeSharingWithUniswapV3 126 | file: ../../node_modules/@looksrare/sdk/dist/AggregatorFeeSharingWithUniswapV3Abi.json 127 | - name: FeeSharingSystem 128 | file: ../../node_modules/@looksrare/sdk/dist/FeeSharingSystemAbi.json 129 | callHandlers: 130 | - function: withdraw(uint256) 131 | handler: handleCallWithdrawAggregatorUniswapV3 132 | - function: withdrawAll() 133 | handler: handleCallWithdrawAggregatorUniswapV3 134 | eventHandlers: 135 | - event: Deposit(indexed address,uint256) 136 | handler: handleDepositAggregatorUniswapV3 137 | - event: ConversionToLOOKS(uint256,uint256) 138 | handler: handleConversionToLOOKSAggregatorUniswapV3 139 | - event: Withdraw(indexed address,uint256) 140 | handler: handleWithdrawAggregatorUniswapV3 141 | - kind: ethereum/contract 142 | name: MultiRewardsDistributor 143 | network: mainnet 144 | source: 145 | abi: MultiRewardsDistributor 146 | address: "0x0554f068365ed43dcc98dcd7fd7a8208a5638c72" 147 | startBlock: 14619105 148 | mapping: 149 | kind: ethereum/events 150 | apiVersion: 0.0.7 151 | language: wasm/assemblyscript 152 | file: ./mappings/index.ts 153 | entities: 154 | - User 155 | abis: 156 | - name: MultiRewardsDistributor 157 | file: ./abis/LightMultiRewardsDistributor.json 158 | eventHandlers: 159 | - event: Claim(address,uint256,uint256,uint8[],uint256[]) 160 | handler: handleMultiRewardsClaim 161 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/tests/aggregatorFeeSharingWithUniswapV3.test.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts"; 2 | import { assert, clearStore, createMockedFunction, log, test } from "matchstick-as/assembly/index"; 3 | import { 4 | createConversionToLOOKSEvent, 5 | createDepositAggregatorEvent, 6 | createWithdrawAggregatorCall, 7 | createWithdrawAggregatorEvent, 8 | } from "./helpers/aggregatorFeeSharingWithUniswapV3/utils"; 9 | import { AggregatorConversion, User } from "../generated/schema"; 10 | import { 11 | handleCallWithdrawAggregatorUniswapV3, 12 | handleConversionToLOOKSAggregatorUniswapV3, 13 | handleDepositAggregatorUniswapV3, 14 | handleWithdrawAggregatorUniswapV3, 15 | } from "../mappings"; 16 | import { AGGREGATOR_ADDRESS } from "../mappings/utils/config/addresses"; 17 | import { ONE_BI, ONE_ETHER_IN_WEI, TWO_BI, ZERO_BI } from "../../../helpers/constants"; 18 | import { parseEther } from "../../../helpers/utils"; 19 | 20 | test("Deposit + Withdraw (inferior to deposited amount) events", () => { 21 | const userAddress = Address.fromString("0x0000000000000000000000000000000000000001"); 22 | 23 | /** 24 | * 1. User deposits 20 LOOKS 25 | */ 26 | const amountDepositedInLOOKS = 20; // 20 LOOKS 27 | const amountDepositedInLOOKSWei = parseEther(amountDepositedInLOOKS); 28 | let blockTimestamp = BigInt.fromU32(1651086000); 29 | 30 | const newDepositEvent = createDepositAggregatorEvent(userAddress, amountDepositedInLOOKSWei, blockTimestamp); 31 | 32 | createMockedFunction(AGGREGATOR_ADDRESS, "totalShares", "totalShares():(uint256)").returns([ 33 | ethereum.Value.fromSignedBigInt(amountDepositedInLOOKSWei), 34 | ]); 35 | 36 | createMockedFunction( 37 | AGGREGATOR_ADDRESS, 38 | "calculateSharePriceInLOOKS", 39 | "calculateSharePriceInLOOKS():(uint256)", 40 | ).returns([ethereum.Value.fromSignedBigInt(ONE_ETHER_IN_WEI)]); 41 | 42 | handleDepositAggregatorUniswapV3(newDepositEvent); 43 | 44 | let user = User.load(userAddress.toHex()); 45 | if (user !== null) { 46 | assert.assertTrue(user.aggregatorIsActive); 47 | assert.stringEquals(user.aggregatorAdjustedDepositAmount.toString(), amountDepositedInLOOKS.toString()); 48 | assert.bigIntEquals(user.aggregatorLastDepositDate, blockTimestamp); 49 | } else { 50 | log.warning("User doesn't exist", []); 51 | } 52 | 53 | /** 54 | * 2. User withdraws 15 LOOKS 55 | */ 56 | let amountWithdrawnInLOOKS = 15; // 15 LOOKS 57 | let amountWithdrawnInLOOKSWei = parseEther(amountWithdrawnInLOOKS); 58 | blockTimestamp = BigInt.fromU32(1651086000); 59 | 60 | let newWithdrawEvent = createWithdrawAggregatorEvent(userAddress, amountWithdrawnInLOOKSWei, blockTimestamp); 61 | handleWithdrawAggregatorUniswapV3(newWithdrawEvent); 62 | 63 | user = User.load(userAddress.toHex()); 64 | if (user !== null) { 65 | assert.assertTrue(user.aggregatorIsActive); 66 | assert.stringEquals( 67 | user.aggregatorAdjustedDepositAmount.toString(), 68 | (amountDepositedInLOOKS - amountWithdrawnInLOOKS).toString(), 69 | ); 70 | // LOOKS are not marked as collected until the adjusted deposit amount reaches 0 71 | assert.stringEquals(user.aggregatorTotalCollectedLOOKS.toString(), "0"); 72 | assert.bigIntEquals(user.aggregatorLastWithdrawDate, blockTimestamp); 73 | } else { 74 | log.warning("User doesn't exist", []); 75 | } 76 | 77 | let newWithdrawCall = createWithdrawAggregatorCall(AGGREGATOR_ADDRESS, userAddress, blockTimestamp); 78 | 79 | createMockedFunction(AGGREGATOR_ADDRESS, "userInfo", "userInfo(address):(uint256)") 80 | .withArgs([ethereum.Value.fromAddress(userAddress)]) 81 | .returns([ethereum.Value.fromSignedBigInt(ONE_BI)]); 82 | 83 | handleCallWithdrawAggregatorUniswapV3(newWithdrawCall); 84 | 85 | user = User.load(userAddress.toHex()); 86 | if (user !== null) { 87 | assert.assertTrue(user.aggregatorIsActive); 88 | } 89 | 90 | /** 91 | * 3. User withdraws final 10 LOOKS (User made 5 LOOKS from his original deposit compounding) 92 | */ 93 | amountWithdrawnInLOOKS = 10; // 10 LOOKS 94 | amountWithdrawnInLOOKSWei = parseEther(amountWithdrawnInLOOKS); 95 | blockTimestamp = BigInt.fromU32(1651089000); 96 | 97 | newWithdrawEvent = createWithdrawAggregatorEvent(userAddress, amountWithdrawnInLOOKSWei, blockTimestamp); 98 | handleWithdrawAggregatorUniswapV3(newWithdrawEvent); 99 | 100 | user = User.load(userAddress.toHex()); 101 | if (user !== null) { 102 | assert.assertTrue(user.aggregatorIsActive); 103 | assert.stringEquals(user.aggregatorAdjustedDepositAmount.toString(), "0"); 104 | // (15 + 10) - (20) = 5 105 | assert.stringEquals(user.aggregatorTotalCollectedLOOKS.toString(), "5"); 106 | assert.bigIntEquals(user.aggregatorLastWithdrawDate, blockTimestamp); 107 | } else { 108 | log.warning("User doesn't exist", []); 109 | } 110 | 111 | newWithdrawCall = createWithdrawAggregatorCall(AGGREGATOR_ADDRESS, userAddress, blockTimestamp); 112 | 113 | createMockedFunction(AGGREGATOR_ADDRESS, "userInfo", "userInfo(address):(uint256)") 114 | .withArgs([ethereum.Value.fromAddress(userAddress)]) 115 | .returns([ethereum.Value.fromSignedBigInt(ZERO_BI)]); 116 | 117 | handleCallWithdrawAggregatorUniswapV3(newWithdrawCall); 118 | 119 | user = User.load(userAddress.toHex()); 120 | if (user !== null) { 121 | assert.assertTrue(!user.aggregatorIsActive); 122 | } 123 | clearStore(); 124 | }); 125 | 126 | test("ConversionToLOOKS event creates AggregatorConversion entity", () => { 127 | /** 128 | * 2 WETH sold 2000 LOOKS 129 | */ 130 | const amountSoldInWETH = 2; // 2 WETH 131 | const amountSoldInWETHWei = parseEther(amountSoldInWETH); 132 | const amountReceivedInLOOKS = 2000; // 2000 LOOKS 133 | const amountReceivedInLOOKSWei = parseEther(amountReceivedInLOOKS); 134 | const blockNumber = TWO_BI; 135 | const blockTimestamp = BigInt.fromU32(1651086000); 136 | 137 | const newConversionEvent = createConversionToLOOKSEvent( 138 | amountSoldInWETHWei, 139 | amountReceivedInLOOKSWei, 140 | blockNumber, 141 | blockTimestamp, 142 | ); 143 | handleConversionToLOOKSAggregatorUniswapV3(newConversionEvent); 144 | 145 | const conversion = AggregatorConversion.load(newConversionEvent.transaction.hash.toHex()); 146 | if (conversion !== null) { 147 | assert.bigIntEquals(conversion.block, blockNumber); 148 | assert.bigIntEquals(conversion.date, blockTimestamp); 149 | assert.stringEquals(conversion.amountSold.toString(), amountSoldInWETH.toString()); 150 | assert.stringEquals(conversion.amountReceived.toString(), amountReceivedInLOOKS.toString()); 151 | } else { 152 | log.warning("Conversion doesn't exist", []); 153 | } 154 | clearStore(); 155 | }); 156 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/tests/feeSharingSystem.test.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts"; 2 | import { assert, clearStore, createMockedFunction, log, test } from "matchstick-as/assembly/index"; 3 | import { RewardPeriod, User } from "../generated/schema"; 4 | import { 5 | createDepositFeeSharingEvent, 6 | createNewRewardPeriodEvent, 7 | createWithdrawFeeSharingCall, 8 | createWithdrawFeeSharingEvent, 9 | } from "./helpers/feeSharingSystem/utils"; 10 | import { 11 | handleCallWithdrawFeeSharing, 12 | handleDepositFeeSharing, 13 | handleNewRewardPeriod, 14 | handleWithdrawFeeSharing, 15 | } from "../mappings"; 16 | import { FEE_SHARING_ADDRESS } from "../mappings/utils/config/addresses"; 17 | import { ONE_BI, ONE_ETHER_IN_WEI, TWO_BI, ZERO_BI } from "../../../helpers/constants"; 18 | import { parseEther } from "../../../helpers/utils"; 19 | 20 | test("Deposit + Withdraw (inferior to deposited amount) events", () => { 21 | const userAddress = Address.fromString("0x0000000000000000000000000000000000000001"); 22 | 23 | /** 24 | * 1. User deposits 20 LOOKS 25 | */ 26 | const amountDepositedInLOOKS = 20; // 20 LOOKS 27 | let harvestedAmountInWETH = 0; // 0 WETH 28 | const amountDepositedInLOOKSWei = parseEther(amountDepositedInLOOKS); 29 | let harvestedAmountInWETHWei = parseEther(harvestedAmountInWETH); 30 | let blockTimestamp = BigInt.fromU32(1651086000); 31 | 32 | const newDepositEvent = createDepositFeeSharingEvent( 33 | userAddress, 34 | amountDepositedInLOOKSWei, 35 | harvestedAmountInWETHWei, 36 | blockTimestamp, 37 | ); 38 | 39 | createMockedFunction(FEE_SHARING_ADDRESS, "totalShares", "totalShares():(uint256)").returns([ 40 | ethereum.Value.fromSignedBigInt(amountDepositedInLOOKSWei), 41 | ]); 42 | 43 | createMockedFunction( 44 | FEE_SHARING_ADDRESS, 45 | "calculateSharePriceInLOOKS", 46 | "calculateSharePriceInLOOKS():(uint256)", 47 | ).returns([ethereum.Value.fromSignedBigInt(ONE_ETHER_IN_WEI)]); 48 | 49 | handleDepositFeeSharing(newDepositEvent); 50 | 51 | let user = User.load(userAddress.toHex()); 52 | if (user !== null) { 53 | assert.assertTrue(user.feeSharingIsActive); 54 | assert.stringEquals(user.feeSharingAdjustedDepositAmount.toString(), amountDepositedInLOOKS.toString()); 55 | assert.bigIntEquals(user.feeSharingLastDepositDate, blockTimestamp); 56 | } else { 57 | log.warning("User doesn't exist", []); 58 | } 59 | 60 | /** 61 | * 2. User withdraws 15 LOOKS 62 | */ 63 | let amountWithdrawnInLOOKS = 15; // 15 LOOKS 64 | harvestedAmountInWETH = 0; // 0 WETH 65 | let amountWithdrawnInLOOKSWei = parseEther(amountWithdrawnInLOOKS); 66 | harvestedAmountInWETHWei = parseEther(harvestedAmountInWETH); 67 | blockTimestamp = BigInt.fromU32(1651086000); 68 | 69 | let newWithdrawEvent = createWithdrawFeeSharingEvent( 70 | userAddress, 71 | amountWithdrawnInLOOKSWei, 72 | harvestedAmountInWETHWei, 73 | blockTimestamp, 74 | ); 75 | 76 | handleWithdrawFeeSharing(newWithdrawEvent); 77 | 78 | user = User.load(userAddress.toHex()); 79 | if (user !== null) { 80 | assert.assertTrue(user.feeSharingIsActive); 81 | assert.stringEquals( 82 | user.feeSharingAdjustedDepositAmount.toString(), 83 | (amountDepositedInLOOKS - amountWithdrawnInLOOKS).toString(), 84 | ); 85 | // LOOKS are not marked as collected until the adjusted deposit amount reaches 0 86 | assert.stringEquals(user.feeSharingTotalCollectedLOOKS.toString(), "0"); 87 | assert.stringEquals(user.feeSharingTotalCollectedWETH.toString(), harvestedAmountInWETH.toString()); 88 | assert.bigIntEquals(user.feeSharingLastHarvestDate, ZERO_BI); 89 | assert.bigIntEquals(user.feeSharingLastWithdrawDate, blockTimestamp); 90 | } else { 91 | log.warning("User doesn't exist", []); 92 | } 93 | 94 | let newWithdrawCall = createWithdrawFeeSharingCall(FEE_SHARING_ADDRESS, userAddress, blockTimestamp); 95 | 96 | // userInfo(address user) --> (uint256 shares, uint256 userRewardPerTokenPaid, uint256 rewards) 97 | createMockedFunction(FEE_SHARING_ADDRESS, "userInfo", "userInfo(address):(uint256,uint256,uint256)") 98 | .withArgs([ethereum.Value.fromAddress(userAddress)]) 99 | .returns([ 100 | ethereum.Value.fromSignedBigInt(ONE_BI), 101 | ethereum.Value.fromSignedBigInt(ONE_BI), 102 | ethereum.Value.fromSignedBigInt(ONE_BI), 103 | ]); 104 | 105 | handleCallWithdrawFeeSharing(newWithdrawCall); 106 | 107 | user = User.load(userAddress.toHex()); 108 | if (user !== null) { 109 | assert.assertTrue(user.feeSharingIsActive); 110 | } 111 | 112 | /** 113 | * 3. User withdraws final 8 LOOKS and harvest 1 WETH 114 | * User made 3 LOOKS from his original deposit compounding. 115 | */ 116 | 117 | amountWithdrawnInLOOKS = 8; // 8 LOOKS 118 | harvestedAmountInWETH = 1; // 1 WETH 119 | amountWithdrawnInLOOKSWei = parseEther(amountWithdrawnInLOOKS); 120 | harvestedAmountInWETHWei = parseEther(harvestedAmountInWETH); 121 | blockTimestamp = BigInt.fromU32(1651088000); 122 | 123 | newWithdrawEvent = createWithdrawFeeSharingEvent( 124 | userAddress, 125 | amountWithdrawnInLOOKSWei, 126 | harvestedAmountInWETHWei, 127 | blockTimestamp, 128 | ); 129 | 130 | handleWithdrawFeeSharing(newWithdrawEvent); 131 | 132 | user = User.load(userAddress.toHex()); 133 | if (user !== null) { 134 | assert.assertTrue(user.feeSharingIsActive); 135 | assert.stringEquals(user.feeSharingAdjustedDepositAmount.toString(), "0"); 136 | assert.stringEquals(user.feeSharingTotalCollectedLOOKS.toString(), "3"); 137 | assert.stringEquals(user.feeSharingTotalCollectedWETH.toString(), harvestedAmountInWETH.toString()); 138 | assert.bigIntEquals(user.feeSharingLastHarvestDate, blockTimestamp); 139 | assert.bigIntEquals(user.feeSharingLastWithdrawDate, blockTimestamp); 140 | } else { 141 | log.warning("User doesn't exist", []); 142 | } 143 | 144 | newWithdrawCall = createWithdrawFeeSharingCall(FEE_SHARING_ADDRESS, userAddress, blockTimestamp); 145 | 146 | // userInfo(address user) --> (uint256 shares, uint256 userRewardPerTokenPaid, uint256 rewards) 147 | createMockedFunction(FEE_SHARING_ADDRESS, "userInfo", "userInfo(address):(uint256,uint256,uint256)") 148 | .withArgs([ethereum.Value.fromAddress(userAddress)]) 149 | .returns([ 150 | ethereum.Value.fromSignedBigInt(ZERO_BI), 151 | ethereum.Value.fromSignedBigInt(ZERO_BI), 152 | ethereum.Value.fromSignedBigInt(ZERO_BI), 153 | ]); 154 | 155 | handleCallWithdrawFeeSharing(newWithdrawCall); 156 | 157 | user = User.load(userAddress.toHex()); 158 | if (user !== null) { 159 | assert.assertTrue(!user.feeSharingIsActive); 160 | } 161 | 162 | clearStore(); 163 | }); 164 | 165 | test("NewRewardEvent creates RewardPeriod entity", () => { 166 | /** 167 | * NewRewardPeriod event with 975 WETH distributed across 6500 blocks 168 | */ 169 | const numberBlocks = BigInt.fromI32(6500); // 6500 blocks 170 | const rewardPerBlockInWETH = 0.15; // 0.15 ETH 171 | const rewardInWETH = 975; // 975 ETH 172 | const rewardPerBlockInWeiWETH = parseEther((rewardPerBlockInWETH * 10 ** 2) as i32, 18 - 2); 173 | const rewardInWeiWETH = parseEther(rewardInWETH); 174 | const blockNumber = TWO_BI; 175 | const blockTimestamp = BigInt.fromU32(1651086000); 176 | 177 | const newRewardPeriodEvent = createNewRewardPeriodEvent( 178 | numberBlocks, 179 | rewardPerBlockInWeiWETH, 180 | rewardInWeiWETH, 181 | blockNumber, 182 | blockTimestamp, 183 | ); 184 | handleNewRewardPeriod(newRewardPeriodEvent); 185 | 186 | const rewardPeriod = RewardPeriod.load(newRewardPeriodEvent.transaction.hash.toHex()); 187 | 188 | if (rewardPeriod !== null) { 189 | assert.bigIntEquals(rewardPeriod.block, blockNumber); 190 | assert.bigIntEquals(rewardPeriod.date, blockTimestamp); 191 | assert.bigIntEquals(rewardPeriod.numberBlocks, numberBlocks); 192 | assert.stringEquals(rewardPeriod.reward.toString(), rewardInWETH.toString()); 193 | assert.stringEquals(rewardPeriod.rewardPerBlock.toString(), rewardPerBlockInWETH.toString()); 194 | } else { 195 | log.warning("RewardPeriod doesn't exist", []); 196 | } 197 | clearStore(); 198 | }); 199 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/tests/helpers/aggregatorFeeSharingWithUniswapV3/utils.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts"; 2 | import { newMockCall, newMockEvent } from "matchstick-as"; 3 | import { 4 | ConversionToLOOKS, 5 | Deposit as DepositAggregatorUniswapV3, 6 | Withdraw as WithdrawAggregatorUniswapV3, 7 | WithdrawCall, 8 | WithdrawAllCall, 9 | } from "../../../generated/AggregatorFeeSharingWithUniswapV3/AggregatorFeeSharingWithUniswapV3"; 10 | import { ZERO_BI } from "../../../../../helpers/constants"; 11 | 12 | /** 13 | * @param user 14 | * @param amount 15 | * @returns Deposit Event 16 | */ 17 | export function createDepositAggregatorEvent( 18 | user: Address, 19 | amount: BigInt, 20 | blockTimestamp: BigInt = ZERO_BI, 21 | ): DepositAggregatorUniswapV3 { 22 | const mockEvent = newMockEvent(); 23 | const newDepositEvent = new DepositAggregatorUniswapV3( 24 | mockEvent.address, 25 | mockEvent.logIndex, 26 | mockEvent.transactionLogIndex, 27 | mockEvent.logType, 28 | mockEvent.block, 29 | mockEvent.transaction, 30 | mockEvent.parameters, 31 | mockEvent.receipt, 32 | ); 33 | 34 | newDepositEvent.block.timestamp = blockTimestamp; 35 | newDepositEvent.parameters = []; 36 | 37 | const userParam = new ethereum.EventParam("user", ethereum.Value.fromAddress(user)); 38 | const amountParam = new ethereum.EventParam("amount", ethereum.Value.fromSignedBigInt(amount)); 39 | 40 | newDepositEvent.parameters.push(userParam); 41 | newDepositEvent.parameters.push(amountParam); 42 | 43 | return newDepositEvent; 44 | } 45 | 46 | /** 47 | * @param user 48 | * @param amount 49 | * @returns Withdraw Event 50 | */ 51 | export function createWithdrawAggregatorEvent( 52 | user: Address, 53 | amount: BigInt, 54 | blockTimestamp: BigInt = ZERO_BI, 55 | ): WithdrawAggregatorUniswapV3 { 56 | const mockEvent = newMockEvent(); 57 | const newWithdrawEvent = new WithdrawAggregatorUniswapV3( 58 | mockEvent.address, 59 | mockEvent.logIndex, 60 | mockEvent.transactionLogIndex, 61 | mockEvent.logType, 62 | mockEvent.block, 63 | mockEvent.transaction, 64 | mockEvent.parameters, 65 | mockEvent.receipt, 66 | ); 67 | 68 | newWithdrawEvent.block.timestamp = blockTimestamp; 69 | newWithdrawEvent.parameters = []; 70 | 71 | const userParam = new ethereum.EventParam("user", ethereum.Value.fromAddress(user)); 72 | const amountParam = new ethereum.EventParam("amount", ethereum.Value.fromSignedBigInt(amount)); 73 | 74 | newWithdrawEvent.parameters.push(userParam); 75 | newWithdrawEvent.parameters.push(amountParam); 76 | 77 | return newWithdrawEvent; 78 | } 79 | 80 | /** 81 | * @param amountSold amount sold in WETH 82 | * @param amountReceived amount received in LOOKS 83 | * @returns ConversionToLOOKS Event 84 | */ 85 | export function createConversionToLOOKSEvent( 86 | amountSold: BigInt, 87 | amountReceived: BigInt, 88 | blockNumber: BigInt = ZERO_BI, 89 | blockTimestamp: BigInt = ZERO_BI, 90 | ): ConversionToLOOKS { 91 | const mockEvent = newMockEvent(); 92 | const newConversionEvent = new ConversionToLOOKS( 93 | mockEvent.address, 94 | mockEvent.logIndex, 95 | mockEvent.transactionLogIndex, 96 | mockEvent.logType, 97 | mockEvent.block, 98 | mockEvent.transaction, 99 | mockEvent.parameters, 100 | mockEvent.receipt, 101 | ); 102 | 103 | newConversionEvent.block.number = blockNumber; 104 | newConversionEvent.block.timestamp = blockTimestamp; 105 | newConversionEvent.parameters = []; 106 | 107 | const amountSoldParam = new ethereum.EventParam("amountSold", ethereum.Value.fromSignedBigInt(amountSold)); 108 | const amountReceivedParam = new ethereum.EventParam( 109 | "amountReceived", 110 | ethereum.Value.fromSignedBigInt(amountReceived), 111 | ); 112 | 113 | newConversionEvent.parameters.push(amountSoldParam); 114 | newConversionEvent.parameters.push(amountReceivedParam); 115 | 116 | return newConversionEvent; 117 | } 118 | 119 | export function createWithdrawAggregatorCall(to: Address, from: Address, blockTimestamp: BigInt): WithdrawCall { 120 | const mockCall = newMockCall(); 121 | 122 | const newWithdrawCall = new WithdrawCall( 123 | to, 124 | from, 125 | mockCall.block, 126 | mockCall.transaction, 127 | mockCall.inputValues, 128 | mockCall.outputValues, 129 | ); 130 | 131 | newWithdrawCall.block.timestamp = blockTimestamp; 132 | 133 | return newWithdrawCall; 134 | } 135 | 136 | export function createWithdrawAllAggregatorCall(to: Address, from: Address, blockTimestamp: BigInt): WithdrawAllCall { 137 | const mockCall = newMockCall(); 138 | 139 | const newWithdrawAllCall = new WithdrawAllCall( 140 | to, 141 | from, 142 | mockCall.block, 143 | mockCall.transaction, 144 | mockCall.inputValues, 145 | mockCall.outputValues, 146 | ); 147 | 148 | newWithdrawAllCall.block.timestamp = blockTimestamp; 149 | 150 | return newWithdrawAllCall; 151 | } 152 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/tests/helpers/feeSharingSystem/utils.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts"; 2 | import { newMockCall, newMockEvent } from "matchstick-as"; 3 | import { 4 | Deposit as DepositFeeSharing, 5 | Harvest as HarvestFeeSharing, 6 | Withdraw as WithdrawFeeSharing, 7 | NewRewardPeriod, 8 | WithdrawCall, 9 | WithdrawAllCall, 10 | } from "../../../generated/FeeSharingSystem/FeeSharingSystem"; 11 | import { ZERO_BI } from "../../../../../helpers/constants"; 12 | 13 | /** 14 | * @param user 15 | * @param amount 16 | * @param harvestedAmount 17 | * @returns Deposit Event 18 | */ 19 | export function createDepositFeeSharingEvent( 20 | user: Address, 21 | amount: BigInt, 22 | harvestedAmount: BigInt, 23 | blockTimestamp: BigInt = ZERO_BI, 24 | ): DepositFeeSharing { 25 | const mockEvent = newMockEvent(); 26 | const newDepositEvent = new DepositFeeSharing( 27 | mockEvent.address, 28 | mockEvent.logIndex, 29 | mockEvent.transactionLogIndex, 30 | mockEvent.logType, 31 | mockEvent.block, 32 | mockEvent.transaction, 33 | mockEvent.parameters, 34 | mockEvent.receipt, 35 | ); 36 | 37 | newDepositEvent.block.timestamp = blockTimestamp; 38 | newDepositEvent.parameters = []; 39 | 40 | const userParam = new ethereum.EventParam("user", ethereum.Value.fromAddress(user)); 41 | const amountParam = new ethereum.EventParam("amount", ethereum.Value.fromSignedBigInt(amount)); 42 | const harvestedAmountParam = new ethereum.EventParam( 43 | "harvestedAmount", 44 | ethereum.Value.fromSignedBigInt(harvestedAmount), 45 | ); 46 | 47 | newDepositEvent.parameters.push(userParam); 48 | newDepositEvent.parameters.push(amountParam); 49 | newDepositEvent.parameters.push(harvestedAmountParam); 50 | 51 | return newDepositEvent; 52 | } 53 | 54 | /** 55 | * @param user 56 | * @param harvestedAmount 57 | * @returns Harvest Event 58 | */ 59 | export function createHarvestFeeSharingEvent( 60 | user: Address, 61 | harvestedAmount: BigInt, 62 | blockTimestamp: BigInt = ZERO_BI, 63 | ): HarvestFeeSharing { 64 | const mockEvent = newMockEvent(); 65 | const newHarvestEvent = new HarvestFeeSharing( 66 | mockEvent.address, 67 | mockEvent.logIndex, 68 | mockEvent.transactionLogIndex, 69 | mockEvent.logType, 70 | mockEvent.block, 71 | mockEvent.transaction, 72 | mockEvent.parameters, 73 | mockEvent.receipt, 74 | ); 75 | 76 | newHarvestEvent.block.timestamp = blockTimestamp; 77 | newHarvestEvent.parameters = []; 78 | 79 | const userParam = new ethereum.EventParam("user", ethereum.Value.fromAddress(user)); 80 | const harvestedAmountParam = new ethereum.EventParam( 81 | "harvestedAmount", 82 | ethereum.Value.fromSignedBigInt(harvestedAmount), 83 | ); 84 | 85 | newHarvestEvent.parameters.push(userParam); 86 | newHarvestEvent.parameters.push(harvestedAmountParam); 87 | 88 | return newHarvestEvent; 89 | } 90 | 91 | /** 92 | * @param user 93 | * @param amount 94 | * @param harvestedAmount 95 | * @returns Withdraw Event 96 | */ 97 | export function createWithdrawFeeSharingEvent( 98 | user: Address, 99 | amount: BigInt, 100 | harvestedAmount: BigInt, 101 | blockTimestamp: BigInt = ZERO_BI, 102 | ): WithdrawFeeSharing { 103 | const mockEvent = newMockEvent(); 104 | const newWithdrawEvent = new WithdrawFeeSharing( 105 | mockEvent.address, 106 | mockEvent.logIndex, 107 | mockEvent.transactionLogIndex, 108 | mockEvent.logType, 109 | mockEvent.block, 110 | mockEvent.transaction, 111 | mockEvent.parameters, 112 | mockEvent.receipt, 113 | ); 114 | 115 | newWithdrawEvent.block.timestamp = blockTimestamp; 116 | newWithdrawEvent.parameters = []; 117 | 118 | const userParam = new ethereum.EventParam("user", ethereum.Value.fromAddress(user)); 119 | const amountParam = new ethereum.EventParam("amount", ethereum.Value.fromSignedBigInt(amount)); 120 | const harvestedAmountParam = new ethereum.EventParam( 121 | "harvestedAmount", 122 | ethereum.Value.fromSignedBigInt(harvestedAmount), 123 | ); 124 | 125 | newWithdrawEvent.parameters.push(userParam); 126 | newWithdrawEvent.parameters.push(amountParam); 127 | newWithdrawEvent.parameters.push(harvestedAmountParam); 128 | 129 | return newWithdrawEvent; 130 | } 131 | 132 | /** 133 | * @param numberBlocks 134 | * @param rewardPerBlock 135 | * @param reward 136 | * @returns NewRewardPeriod Event 137 | */ 138 | export function createNewRewardPeriodEvent( 139 | numberBlocks: BigInt, 140 | rewardPerBlock: BigInt, 141 | reward: BigInt, 142 | blockNumber: BigInt = ZERO_BI, 143 | blockTimestamp: BigInt = ZERO_BI, 144 | ): NewRewardPeriod { 145 | const mockEvent = newMockEvent(); 146 | const newRewardPeriodEvent = new NewRewardPeriod( 147 | mockEvent.address, 148 | mockEvent.logIndex, 149 | mockEvent.transactionLogIndex, 150 | mockEvent.logType, 151 | mockEvent.block, 152 | mockEvent.transaction, 153 | mockEvent.parameters, 154 | mockEvent.receipt, 155 | ); 156 | 157 | newRewardPeriodEvent.block.number = blockNumber; 158 | newRewardPeriodEvent.block.timestamp = blockTimestamp; 159 | newRewardPeriodEvent.parameters = []; 160 | 161 | const numberBlocksParam = new ethereum.EventParam("numberBlocks", ethereum.Value.fromSignedBigInt(numberBlocks)); 162 | const rewardPerBlockPAram = new ethereum.EventParam( 163 | "rewardPerBlock", 164 | ethereum.Value.fromSignedBigInt(rewardPerBlock), 165 | ); 166 | const rewardParam = new ethereum.EventParam("reward", ethereum.Value.fromSignedBigInt(reward)); 167 | 168 | newRewardPeriodEvent.parameters.push(numberBlocksParam); 169 | newRewardPeriodEvent.parameters.push(rewardPerBlockPAram); 170 | newRewardPeriodEvent.parameters.push(rewardParam); 171 | 172 | return newRewardPeriodEvent; 173 | } 174 | 175 | export function createWithdrawFeeSharingCall(to: Address, from: Address, blockTimestamp: BigInt): WithdrawCall { 176 | const mockCall = newMockCall(); 177 | 178 | const newWithdrawCall = new WithdrawCall( 179 | to, 180 | from, 181 | mockCall.block, 182 | mockCall.transaction, 183 | mockCall.inputValues, 184 | mockCall.outputValues, 185 | ); 186 | 187 | newWithdrawCall.block.timestamp = blockTimestamp; 188 | 189 | return newWithdrawCall; 190 | } 191 | 192 | export function createWithdrawAllFeeSharingCall(to: Address, from: Address, blockTimestamp: BigInt): WithdrawAllCall { 193 | const mockCall = newMockCall(); 194 | 195 | const newWithdrawAllCall = new WithdrawAllCall( 196 | to, 197 | from, 198 | mockCall.block, 199 | mockCall.transaction, 200 | mockCall.inputValues, 201 | mockCall.outputValues, 202 | ); 203 | 204 | newWithdrawAllCall.block.timestamp = blockTimestamp; 205 | 206 | return newWithdrawAllCall; 207 | } 208 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/tests/helpers/multiRewardsDistributor/utils.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts"; 2 | import { newMockEvent } from "matchstick-as"; 3 | import { ZERO_BI } from "../../../../../helpers/constants"; 4 | import { Claim as MultiRewardsClaim } from "../../../generated/MultiRewardsDistributor/MultiRewardsDistributor"; 5 | 6 | /** 7 | * @param user 8 | * @param rewardRound 9 | * @param totalAmount 10 | * @param treeIds 11 | * @param amounts 12 | * @returns MultiRewardsClaim Event 13 | */ 14 | export function createMultiRewardsClaim( 15 | user: Address, 16 | rewardRound: BigInt, 17 | totalAmount: BigInt, 18 | treeIds: i32[], 19 | amounts: BigInt[], 20 | blockTimestamp: BigInt = ZERO_BI, 21 | ): MultiRewardsClaim { 22 | const mockEvent = newMockEvent(); 23 | const newRewardsClaimEvent = new MultiRewardsClaim( 24 | mockEvent.address, 25 | mockEvent.logIndex, 26 | mockEvent.transactionLogIndex, 27 | mockEvent.logType, 28 | mockEvent.block, 29 | mockEvent.transaction, 30 | mockEvent.parameters, 31 | mockEvent.receipt, 32 | ); 33 | 34 | newRewardsClaimEvent.block.timestamp = blockTimestamp; 35 | newRewardsClaimEvent.parameters = []; 36 | 37 | const userParam = new ethereum.EventParam("user", ethereum.Value.fromAddress(user)); 38 | const rewardRoundParam = new ethereum.EventParam("rewardRound", ethereum.Value.fromSignedBigInt(rewardRound)); 39 | const totalAmountParam = new ethereum.EventParam("totalAmount", ethereum.Value.fromSignedBigInt(totalAmount)); 40 | const treeIdsParam = new ethereum.EventParam("treeIds", ethereum.Value.fromI32Array(treeIds)); 41 | const amountsParam = new ethereum.EventParam("amounts", ethereum.Value.fromSignedBigIntArray(amounts)); 42 | 43 | newRewardsClaimEvent.parameters.push(userParam); 44 | newRewardsClaimEvent.parameters.push(rewardRoundParam); 45 | newRewardsClaimEvent.parameters.push(totalAmountParam); 46 | newRewardsClaimEvent.parameters.push(treeIdsParam); 47 | newRewardsClaimEvent.parameters.push(amountsParam); 48 | 49 | return newRewardsClaimEvent; 50 | } 51 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/tests/helpers/tradingRewardsDistributor/utils.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts"; 2 | import { newMockEvent } from "matchstick-as"; 3 | import { RewardsClaim } from "../../../generated/TradingRewardsDistributor/TradingRewardsDistributor"; 4 | import { ZERO_BI } from "../../../../../helpers/constants"; 5 | 6 | /** 7 | * @param user 8 | * @param rewardRound 9 | * @param amount 10 | * @returns RewardsClaim Event 11 | */ 12 | export function createRewardsClaimEvent( 13 | user: Address, 14 | rewardRound: BigInt, 15 | amount: BigInt, 16 | blockTimestamp: BigInt = ZERO_BI, 17 | ): RewardsClaim { 18 | const mockEvent = newMockEvent(); 19 | const newRewardsClaimEvent = new RewardsClaim( 20 | mockEvent.address, 21 | mockEvent.logIndex, 22 | mockEvent.transactionLogIndex, 23 | mockEvent.logType, 24 | mockEvent.block, 25 | mockEvent.transaction, 26 | mockEvent.parameters, 27 | mockEvent.receipt, 28 | ); 29 | 30 | newRewardsClaimEvent.block.timestamp = blockTimestamp; 31 | newRewardsClaimEvent.parameters = []; 32 | 33 | const userParam = new ethereum.EventParam("user", ethereum.Value.fromAddress(user)); 34 | const rewardRoundParam = new ethereum.EventParam("rewardRound", ethereum.Value.fromSignedBigInt(rewardRound)); 35 | const amountParam = new ethereum.EventParam("amount", ethereum.Value.fromSignedBigInt(amount)); 36 | 37 | newRewardsClaimEvent.parameters.push(userParam); 38 | newRewardsClaimEvent.parameters.push(rewardRoundParam); 39 | newRewardsClaimEvent.parameters.push(amountParam); 40 | 41 | return newRewardsClaimEvent; 42 | } 43 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/tests/multiRewardsDistributor.test.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt } from "@graphprotocol/graph-ts"; 2 | import { assert, clearStore, log, test } from "matchstick-as/assembly/index"; 3 | import { createMultiRewardsClaim } from "./helpers/multiRewardsDistributor/utils"; 4 | import { User } from "../generated/schema"; 5 | import { handleMultiRewardsClaim } from "../mappings"; 6 | import { parseEther } from "../../../helpers/utils"; 7 | import { FOUR_BI, ONE_BI, THREE_BI, TWO_BI } from "../../../helpers/constants"; 8 | 9 | test("Trading/Listing rewards claimed", () => { 10 | const userAddress = Address.fromString("0x0000000000000000000000000000000000000001"); 11 | /** 12 | * 1. User claims 20 LOOKS for trading rewards and 10 LOOKS for listing rewards 13 | */ 14 | let rewardRound = ONE_BI; 15 | let amountTradingRewardsInLOOKS = 20; // 20 LOOKS 16 | let amountListingRewardsInLOOKS = 10; // 10 LOOKS 17 | let totalAmount = parseEther(amountListingRewardsInLOOKS + amountTradingRewardsInLOOKS); 18 | let treeIds = [0, 1]; 19 | let amountsClaimed = [parseEther(amountTradingRewardsInLOOKS), parseEther(amountListingRewardsInLOOKS)]; 20 | let blockTimestamp = BigInt.fromU32(1651086000); 21 | 22 | let newRewardsClaimEvent = createMultiRewardsClaim( 23 | userAddress, 24 | rewardRound, 25 | totalAmount, 26 | treeIds, 27 | amountsClaimed, 28 | blockTimestamp, 29 | ); 30 | handleMultiRewardsClaim(newRewardsClaimEvent); 31 | 32 | let user = User.load(userAddress.toHex()); 33 | if (user !== null) { 34 | assert.bigIntEquals(user.tradingRewardsLastClaimDate, blockTimestamp); 35 | assert.stringEquals(user.tradingRewardsAmount.toString(), amountTradingRewardsInLOOKS.toString()); 36 | assert.bigIntEquals(user.listingRewardsLastClaimDate, blockTimestamp); 37 | assert.stringEquals(user.listingRewardsAmount.toString(), amountListingRewardsInLOOKS.toString()); 38 | } else { 39 | log.warning("User doesn't exist", []); 40 | } 41 | 42 | /** 43 | * 2. User claims only for listing rewards (15 LOOKS) 44 | */ 45 | rewardRound = TWO_BI; 46 | amountListingRewardsInLOOKS = 15; // 15 LOOKS 47 | totalAmount = parseEther(amountTradingRewardsInLOOKS); 48 | treeIds = [1]; 49 | amountsClaimed = [parseEther(amountListingRewardsInLOOKS)]; 50 | let previousBlockTimestamp = blockTimestamp; 51 | blockTimestamp = BigInt.fromU32(1651087000); 52 | 53 | newRewardsClaimEvent = createMultiRewardsClaim( 54 | userAddress, 55 | rewardRound, 56 | totalAmount, 57 | treeIds, 58 | amountsClaimed, 59 | blockTimestamp, 60 | ); 61 | handleMultiRewardsClaim(newRewardsClaimEvent); 62 | 63 | user = User.load(userAddress.toHex()); 64 | if (user !== null) { 65 | assert.bigIntEquals(user.tradingRewardsLastClaimDate, previousBlockTimestamp); 66 | assert.stringEquals(user.tradingRewardsAmount.toString(), amountTradingRewardsInLOOKS.toString()); 67 | assert.bigIntEquals(user.listingRewardsLastClaimDate, blockTimestamp); 68 | assert.stringEquals(user.listingRewardsAmount.toString(), "25"); // 10 + 15 69 | } else { 70 | log.warning("User doesn't exist", []); 71 | } 72 | 73 | /** 74 | * 3. User claims only for trading rewards (8.5 LOOKS) 75 | */ 76 | rewardRound = THREE_BI; 77 | totalAmount = parseEther(85, 17); // Shift decimals by 1 and multiply by 10 to avoid type error 78 | treeIds = [0]; 79 | amountsClaimed = [parseEther(85, 17)]; 80 | previousBlockTimestamp = blockTimestamp; 81 | blockTimestamp = BigInt.fromU32(1651097000); 82 | 83 | newRewardsClaimEvent = createMultiRewardsClaim( 84 | userAddress, 85 | rewardRound, 86 | totalAmount, 87 | treeIds, 88 | amountsClaimed, 89 | blockTimestamp, 90 | ); 91 | handleMultiRewardsClaim(newRewardsClaimEvent); 92 | 93 | user = User.load(userAddress.toHex()); 94 | if (user !== null) { 95 | assert.bigIntEquals(user.tradingRewardsLastClaimDate, blockTimestamp); 96 | assert.stringEquals(user.tradingRewardsAmount.toString(), "28.5"); 97 | assert.bigIntEquals(user.listingRewardsLastClaimDate, previousBlockTimestamp); 98 | assert.stringEquals(user.listingRewardsAmount.toString(), "25"); 99 | } else { 100 | log.warning("User doesn't exist", []); 101 | } 102 | 103 | /** 104 | * 4. User claims for trading rewards (6 LOOKS) and listing rewards (4 LOOKS) (array order is inversed) 105 | */ 106 | rewardRound = FOUR_BI; 107 | amountTradingRewardsInLOOKS = 6; // 6 LOOKS 108 | amountListingRewardsInLOOKS = 4; // 4 LOOKS 109 | totalAmount = parseEther(amountListingRewardsInLOOKS + amountTradingRewardsInLOOKS); 110 | treeIds = [1, 0]; 111 | amountsClaimed = [parseEther(amountListingRewardsInLOOKS), parseEther(amountTradingRewardsInLOOKS)]; 112 | blockTimestamp = BigInt.fromU32(1651156000); 113 | 114 | newRewardsClaimEvent = createMultiRewardsClaim( 115 | userAddress, 116 | rewardRound, 117 | totalAmount, 118 | treeIds, 119 | amountsClaimed, 120 | blockTimestamp, 121 | ); 122 | handleMultiRewardsClaim(newRewardsClaimEvent); 123 | 124 | user = User.load(userAddress.toHex()); 125 | if (user !== null) { 126 | assert.bigIntEquals(user.tradingRewardsLastClaimDate, blockTimestamp); 127 | assert.stringEquals(user.tradingRewardsAmount.toString(), "34.5"); // 28.5 + 6 128 | assert.bigIntEquals(user.listingRewardsLastClaimDate, blockTimestamp); 129 | assert.stringEquals(user.listingRewardsAmount.toString(), "29"); // 25 + 4 130 | } else { 131 | log.warning("User doesn't exist", []); 132 | } 133 | 134 | clearStore(); 135 | }); 136 | -------------------------------------------------------------------------------- /subgraphs/looks-distribution/tests/tradingRewardsDistributor.test.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt } from "@graphprotocol/graph-ts"; 2 | import { assert, clearStore, log, test } from "matchstick-as/assembly/index"; 3 | import { createRewardsClaimEvent } from "./helpers/tradingRewardsDistributor/utils"; 4 | import { User } from "../generated/schema"; 5 | import { handleTradingRewardsClaim } from "../mappings"; 6 | import { parseEther } from "../../../helpers/utils"; 7 | import { ONE_BI, TWO_BI } from "../../../helpers/constants"; 8 | 9 | test("Trading rewards claimed", () => { 10 | const userAddress = Address.fromString("0x0000000000000000000000000000000000000001"); 11 | /** 12 | * 1. User claims 20 LOOKS 13 | */ 14 | let amountClaimedInLOOKS = 20; // 20 LOOKS 15 | let amountClaimedInLOOKSWei = parseEther(amountClaimedInLOOKS); 16 | let totalAmountClaimedInLOOKS = amountClaimedInLOOKS; 17 | 18 | let newRewardsClaimEvent = createRewardsClaimEvent(userAddress, ONE_BI, amountClaimedInLOOKSWei); 19 | handleTradingRewardsClaim(newRewardsClaimEvent); 20 | 21 | let user = User.load(userAddress.toHex()); 22 | if (user !== null) { 23 | assert.bigIntEquals(user.tradingRewardsLastClaimDate, newRewardsClaimEvent.block.timestamp); 24 | assert.stringEquals(user.tradingRewardsAmount.toString(), amountClaimedInLOOKS.toString()); 25 | } else { 26 | log.warning("User doesn't exist", []); 27 | } 28 | 29 | /** 30 | * 2. User has claimed 50 more LOOKS 31 | */ 32 | amountClaimedInLOOKS = 50; // 50 LOOKS 33 | amountClaimedInLOOKSWei = parseEther(amountClaimedInLOOKS); 34 | totalAmountClaimedInLOOKS += amountClaimedInLOOKS; 35 | const blockTimestamp = BigInt.fromU32(1651086000); 36 | 37 | newRewardsClaimEvent = createRewardsClaimEvent(userAddress, TWO_BI, amountClaimedInLOOKSWei, blockTimestamp); 38 | handleTradingRewardsClaim(newRewardsClaimEvent); 39 | 40 | user = User.load(userAddress.toHex()); 41 | 42 | if (user !== null) { 43 | assert.bigIntEquals(user.tradingRewardsLastClaimDate, blockTimestamp); 44 | assert.stringEquals(user.tradingRewardsAmount.toString(), totalAmountClaimedInLOOKS.toString()); 45 | } else { 46 | log.warning("User doesn't exist", []); 47 | } 48 | 49 | clearStore(); 50 | }); 51 | -------------------------------------------------------------------------------- /subgraphs/raffle/abis/ERC20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "name", 6 | "outputs": [ 7 | { 8 | "internalType": "string", 9 | "name": "", 10 | "type": "string" 11 | } 12 | ], 13 | "payable": false, 14 | "stateMutability": "view", 15 | "type": "function" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "symbol", 21 | "outputs": [ 22 | { 23 | "internalType": "string", 24 | "name": "", 25 | "type": "string" 26 | } 27 | ], 28 | "payable": false, 29 | "stateMutability": "view", 30 | "type": "function" 31 | }, 32 | { 33 | "inputs": [], 34 | "name": "decimals", 35 | "outputs": [ 36 | { 37 | "internalType": "uint8", 38 | "name": "", 39 | "type": "uint8" 40 | } 41 | ], 42 | "stateMutability": "view", 43 | "type": "function" 44 | } 45 | ] 46 | -------------------------------------------------------------------------------- /subgraphs/raffle/mappings/entities/currency.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "@graphprotocol/graph-ts"; 2 | import { ZERO_ADDRESS } from "../../../../helpers/constants"; 3 | import { Currency } from "../../generated/schema"; 4 | import { fetchDecimals, fetchName, fetchSymbol } from "../utils/ERC20"; 5 | 6 | export function initializeCurrency(address: Address): Currency { 7 | const currency = Currency.load(address.toHex()); 8 | if (currency === null) { 9 | const newCurrency = new Currency(address.toHex()); 10 | newCurrency.name = fetchName(address); 11 | newCurrency.symbol = fetchSymbol(address); 12 | newCurrency.decimals = fetchDecimals(address); 13 | newCurrency.isAllowed = address.equals(ZERO_ADDRESS) ? true : false; 14 | newCurrency.save(); 15 | 16 | return newCurrency; 17 | } 18 | 19 | return currency; 20 | } 21 | -------------------------------------------------------------------------------- /subgraphs/raffle/mappings/utils/ERC20.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt } from "@graphprotocol/graph-ts"; 2 | import { ERC20 as ERC20Contract } from "../../generated/Raffle/ERC20"; 3 | 4 | export function fetchName(address: Address): string { 5 | const contract = ERC20Contract.bind(address); 6 | 7 | const nameResult = contract.try_name(); 8 | if (!nameResult.reverted) { 9 | return nameResult.value; 10 | } 11 | 12 | return "Ethereum"; 13 | } 14 | 15 | export function fetchSymbol(address: Address): string { 16 | const contract = ERC20Contract.bind(address); 17 | 18 | const symbolResult = contract.try_symbol(); 19 | if (!symbolResult.reverted) { 20 | return symbolResult.value; 21 | } 22 | 23 | return "ETH"; 24 | } 25 | 26 | export function fetchDecimals(address: Address): BigInt { 27 | const contract = ERC20Contract.bind(address); 28 | 29 | const decimalResult = contract.try_decimals(); 30 | if (!decimalResult.reverted) { 31 | return BigInt.fromI32(decimalResult.value); 32 | } 33 | 34 | return BigInt.fromI32(18); 35 | } 36 | -------------------------------------------------------------------------------- /subgraphs/raffle/mappings/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { BigInt } from "@graphprotocol/graph-ts"; 2 | import { 3 | Raffle, 4 | Raffle__getPricingOptionsResultPricingOptionsStruct, 5 | Raffle__getPrizesResultPrizesStruct, 6 | Raffle__getWinnersResultWinnersStruct, 7 | Raffle__rafflesResult, 8 | } from "../../generated/Raffle/Raffle"; 9 | import { 10 | RaffleV2, 11 | RaffleV2__getPricingOptionsResultPricingOptionsStruct, 12 | RaffleV2__getPrizesResultPrizesStruct, 13 | RaffleV2__getWinnersResultWinnersStruct, 14 | RaffleV2__rafflesResult, 15 | } from "../../generated/RaffleV2/RaffleV2"; 16 | 17 | export function getRaffle(contract: Raffle, raffleId: BigInt): Raffle__rafflesResult | null { 18 | const result = contract.try_raffles(raffleId); 19 | if (!result.reverted) { 20 | return result.value; 21 | } 22 | 23 | return null; 24 | } 25 | 26 | export function getRaffleV2(contract: RaffleV2, raffleId: BigInt): RaffleV2__rafflesResult | null { 27 | const result = contract.try_raffles(raffleId); 28 | if (!result.reverted) { 29 | return result.value; 30 | } 31 | 32 | return null; 33 | } 34 | 35 | export function getPricing( 36 | contract: Raffle, 37 | raffleId: BigInt, 38 | ): Array | null { 39 | const result = contract.try_getPricingOptions(raffleId); 40 | if (!result.reverted) { 41 | return result.value; 42 | } 43 | 44 | return null; 45 | } 46 | 47 | export function getPricingV2( 48 | contract: RaffleV2, 49 | raffleId: BigInt, 50 | ): Array | null { 51 | const result = contract.try_getPricingOptions(raffleId); 52 | if (!result.reverted) { 53 | return result.value; 54 | } 55 | 56 | return null; 57 | } 58 | 59 | export function getPrizes(contract: Raffle, raffleId: BigInt): Array | null { 60 | const result = contract.try_getPrizes(raffleId); 61 | if (!result.reverted) { 62 | return result.value; 63 | } 64 | 65 | return null; 66 | } 67 | 68 | export function getPrizesV2(contract: RaffleV2, raffleId: BigInt): Array | null { 69 | const result = contract.try_getPrizes(raffleId); 70 | if (!result.reverted) { 71 | return result.value; 72 | } 73 | 74 | return null; 75 | } 76 | 77 | export function getWinners(contract: Raffle, raffleId: BigInt): Array | null { 78 | const result = contract.try_getWinners(raffleId); 79 | if (!result.reverted) { 80 | return result.value; 81 | } 82 | 83 | return null; 84 | } 85 | 86 | export function getWinnersV2( 87 | contract: RaffleV2, 88 | raffleId: BigInt, 89 | ): Array | null { 90 | const result = contract.try_getWinners(raffleId); 91 | if (!result.reverted) { 92 | return result.value; 93 | } 94 | 95 | return null; 96 | } 97 | 98 | export function statusIdToEnum(statusId: i32): string { 99 | switch (statusId) { 100 | case 0: 101 | return "None"; 102 | case 1: 103 | return "Created"; 104 | case 2: 105 | return "Open"; 106 | case 3: 107 | return "Drawing"; 108 | case 4: 109 | return "RandomnessFulfilled"; 110 | case 5: 111 | return "Drawn"; 112 | case 6: 113 | return "Complete"; 114 | case 7: 115 | return "Refundable"; 116 | case 8: 117 | return "Cancelled"; 118 | default: 119 | return "unknown"; 120 | } 121 | } 122 | 123 | export function statusIdToEnumV2(statusId: i32): string { 124 | switch (statusId) { 125 | case 0: 126 | return "None"; 127 | case 1: 128 | return "Open"; 129 | case 2: 130 | return "Drawing"; 131 | case 3: 132 | return "RandomnessFulfilled"; 133 | case 4: 134 | return "Drawn"; 135 | case 5: 136 | return "Complete"; 137 | case 6: 138 | return "Refundable"; 139 | case 7: 140 | return "Cancelled"; 141 | default: 142 | return "unknown"; 143 | } 144 | } 145 | 146 | export function typeIdToEnum(typeId: i32): string { 147 | switch (typeId) { 148 | case 0: 149 | return "ERC721"; 150 | case 1: 151 | return "ERC1155"; 152 | case 2: 153 | return "ETH"; 154 | case 3: 155 | return "ERC20"; 156 | default: 157 | return "unknown"; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /subgraphs/raffle/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "Raffle": { 4 | "address": "0x0000000000009703EcD0fFEa3143fc9096DE91B0", 5 | "startBlock": 17236721 6 | }, 7 | "RaffleV2": { 8 | "address": "0x0000000000aDEaD599C11A0C9a7475B67852c1D0", 9 | "startBlock": 17687869 10 | } 11 | }, 12 | "goerli": { 13 | "Raffle": { 14 | "address": "0xC5F7FCde87e30Aa339d3d61B4fe3c1C261f6EEe2", 15 | "startBlock": 8970588 16 | }, 17 | "RaffleV2": { 18 | "address": "0xda28aC345040C9abC0E19AfD6025c4f5A45C4b30", 19 | "startBlock": 9375815 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /subgraphs/raffle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raffle-v1", 3 | "description": "LooksRare Subgraph", 4 | "version": "1.0.0", 5 | "repository": "git@github.com:looksrare/subgraph.git", 6 | "author": "LooksRare", 7 | "license": "MIT", 8 | "scripts": { 9 | "codegen": "graph codegen subgraph.yaml", 10 | "build:goerli": "graph build subgraph.yaml --network goerli", 11 | "build:mainnet": "graph build subgraph.yaml --network mainnet", 12 | "deploy:goerli": "graph deploy --product hosted-service looksrare/raffle subgraph.yaml --network goerli", 13 | "deploy:mainnet": "graph deploy --product hosted-service looksrare/raffle subgraph.yaml --network mainnet" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /subgraphs/raffle/schema.graphql: -------------------------------------------------------------------------------- 1 | """ 2 | Raffle Status 3 | """ 4 | enum RaffleStatus { 5 | None 6 | Created 7 | Open 8 | Drawing 9 | RandomnessFulfilled 10 | Drawn 11 | Complete 12 | Refundable 13 | Cancelled 14 | unknown 15 | } 16 | 17 | enum RaffleVersion { 18 | V1 19 | V2 20 | } 21 | 22 | """ 23 | Token Type 24 | """ 25 | enum TokenType { 26 | ERC721 27 | ERC1155 28 | ETH 29 | ERC20 30 | unknown 31 | } 32 | 33 | """ 34 | Transaction 35 | """ 36 | type Transaction @entity { 37 | "ID (hash)" 38 | id: ID! 39 | 40 | "Hash" 41 | hash: Bytes! 42 | 43 | "Raffle" 44 | raffle: Raffle! 45 | 46 | "Participant" 47 | participant: Participant! 48 | 49 | "Entry" 50 | entry: Entry! 51 | "Entry Purchased" 52 | entriesCount: BigInt! 53 | 54 | "Gas Limit" 55 | gasLimit: BigInt! 56 | "Gas Price (in wei)" 57 | gasPrice: BigDecimal! 58 | 59 | "Block number" 60 | block: BigInt! 61 | "Block timestamp" 62 | timestamp: BigInt! 63 | } 64 | 65 | """ 66 | Participant 67 | """ 68 | type Participant @entity { 69 | id: ID! 70 | 71 | totalRaffles: BigInt! 72 | totalTickets: BigInt! 73 | tickets: [Entry!]! @derivedFrom(field: "participant") 74 | winners: [Winner!] @derivedFrom(field: "participant") 75 | transactions: [Transaction!] @derivedFrom(field: "participant") 76 | } 77 | 78 | """ 79 | Winner 80 | """ 81 | type Winner @entity { 82 | id: ID! 83 | 84 | raffle: Raffle! 85 | 86 | winnerIndex: BigInt! 87 | 88 | "participant" 89 | participant: Participant! 90 | "entryIndex" 91 | entryIndex: BigInt! 92 | entry: Entry! 93 | "prizeIndex" 94 | prizeIndex: BigInt! 95 | prize: Prize! 96 | "claimed" 97 | claimed: Boolean! 98 | claimedHash: Bytes 99 | } 100 | 101 | """ 102 | Entry 103 | """ 104 | type Entry @entity { 105 | id: ID! 106 | 107 | raffle: Raffle! 108 | participant: Participant! 109 | 110 | totalTickets: BigInt! 111 | totalPrice: BigDecimal! 112 | refunded: Boolean! 113 | 114 | winners: [Winner!] @derivedFrom(field: "entry") 115 | transactions: [Transaction!] @derivedFrom(field: "entry") 116 | } 117 | 118 | """ 119 | Prize 120 | """ 121 | type Prize @entity { 122 | id: ID! 123 | 124 | prizeId: BigInt! 125 | 126 | raffle: Raffle! 127 | 128 | "prizeTier" 129 | tier: BigInt! 130 | 131 | "prizeType" 132 | type: TokenType! 133 | 134 | "prizeAddress" 135 | address: Bytes 136 | "prizeId" 137 | tokenId: BigInt 138 | "prizeAmount" 139 | amount: BigInt 140 | 141 | "winnersCount" 142 | totalWinners: BigInt! 143 | "cumulativeWinnersCount" 144 | totalWinnersCumulative: BigInt! 145 | } 146 | 147 | """ 148 | Pricing 149 | """ 150 | type Pricing @entity { 151 | id: ID! 152 | 153 | raffle: Raffle! 154 | 155 | "entriesCount" 156 | totalEntries: BigInt! 157 | 158 | "price" 159 | price: BigDecimal! 160 | } 161 | 162 | """ 163 | Currency 164 | """ 165 | type Currency @entity { 166 | id: ID! 167 | 168 | name: String! 169 | 170 | symbol: String! 171 | 172 | decimals: BigInt! 173 | 174 | isAllowed: Boolean! 175 | } 176 | 177 | type RaffleStatusLog @entity { 178 | "ID" 179 | id: ID! 180 | "Raffle" 181 | raffle: Raffle! 182 | "Status" 183 | status: RaffleStatus! 184 | "Transaction Hash" 185 | transaction: Bytes! 186 | "Block number" 187 | block: BigInt! 188 | "Block timestamp" 189 | timestamp: BigInt! 190 | } 191 | 192 | """ 193 | Raffle 194 | """ 195 | type Raffle @entity { 196 | id: ID! 197 | 198 | raffleId: BigInt! 199 | 200 | currency: Currency! 201 | 202 | owner: Bytes! 203 | status: RaffleStatus! 204 | lastStatusUpdate: BigInt 205 | drawnAt: BigInt 206 | statusLog: [RaffleStatusLog!]! @derivedFrom(field: "raffle") 207 | cutoffTime: BigInt! 208 | minimumEntries: BigInt! 209 | maximumEntriesPerParticipant: BigInt! 210 | isMinimumEntriesFixed: Boolean! 211 | prizeValue: BigDecimal! 212 | currentPool: BigDecimal! 213 | version: RaffleVersion! 214 | 215 | pricing: [Pricing!]! @derivedFrom(field: "raffle") 216 | 217 | prizes: [Prize!]! @derivedFrom(field: "raffle") 218 | 219 | totalUsers: BigInt! 220 | totalTickets: BigInt! 221 | tickets: [Entry!]! @derivedFrom(field: "raffle") 222 | 223 | totalWinners: BigInt! 224 | winners: [Winner!] @derivedFrom(field: "raffle") 225 | 226 | transactions: [Transaction!]! @derivedFrom(field: "raffle") 227 | } 228 | -------------------------------------------------------------------------------- /subgraphs/raffle/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | description: LooksRare Raffle 3 | repository: https://github.com/looksrare 4 | schema: 5 | file: ./schema.graphql 6 | features: 7 | - grafting 8 | graft: 9 | base: QmPPZkk4G6PtUoC1Kvvqg5dDayhg4PMZRPw56Q6tJfFf79 10 | block: 17687869 11 | dataSources: 12 | - kind: ethereum/contract 13 | name: Raffle 14 | network: mainnet 15 | source: 16 | abi: Raffle 17 | address: "0x0000000000009703EcD0fFEa3143fc9096DE91B0" 18 | startBlock: 17236721 19 | mapping: 20 | kind: ethereum/events 21 | apiVersion: 0.0.7 22 | language: wasm/assemblyscript 23 | file: ./mappings/index.ts 24 | entities: 25 | - Raffle 26 | - Currency 27 | - Pricing 28 | - Prize 29 | - Participant 30 | - Entry 31 | abis: 32 | - name: Raffle 33 | file: ./abis/Raffle.json 34 | - name: IRaffle 35 | file: ./abis/IRaffle.json 36 | - name: ERC20 37 | file: ./abis/ERC20.json 38 | eventHandlers: 39 | - event: CurrenciesStatusUpdated(address[],bool) 40 | handler: handleCurrenciesStatusUpdatedV1 41 | - event: RaffleStatusUpdated(uint256,uint8) 42 | handler: handleRaffleStatusUpdatedV1 43 | - event: EntrySold(uint256,address,uint40,uint208) 44 | handler: handleEntrySoldV1 45 | - event: EntryRefunded(uint256,address,uint208) 46 | handler: handleEntryRefundedV1 47 | - event: PrizesClaimed(uint256,uint256[]) 48 | handler: handlePrizesClaimedV1 49 | - kind: ethereum/contract 50 | name: RaffleV2 51 | network: mainnet 52 | source: 53 | abi: RaffleV2 54 | address: "0x0000000000aDEaD599C11A0C9a7475B67852c1D0" 55 | startBlock: 17687869 56 | mapping: 57 | kind: ethereum/events 58 | apiVersion: 0.0.7 59 | language: wasm/assemblyscript 60 | file: ./mappings/index.ts 61 | entities: 62 | - Raffle 63 | - Currency 64 | - Pricing 65 | - Prize 66 | - Participant 67 | - Entry 68 | abis: 69 | - name: RaffleV2 70 | file: ./abis/RaffleV2.json 71 | - name: IRaffleV2 72 | file: ./abis/IRaffleV2.json 73 | - name: ERC20 74 | file: ./abis/ERC20.json 75 | eventHandlers: 76 | - event: CurrenciesStatusUpdated(address[],bool) 77 | handler: handleCurrenciesStatusUpdatedV2 78 | - event: RaffleStatusUpdated(uint256,uint8) 79 | handler: handleRaffleStatusUpdatedV2 80 | - event: EntrySold(uint256,address,address,uint40,uint208) 81 | handler: handleEntrySoldV2 82 | - event: EntryRefunded(uint256,address,uint208) 83 | handler: handleEntryRefundedV2 84 | - event: PrizeClaimed(uint256,uint256) 85 | handler: handlePrizeClaimedV2 86 | - event: PrizesClaimed(uint256,uint256[]) 87 | handler: handlePrizesClaimedV2 88 | -------------------------------------------------------------------------------- /subgraphs/royalty-fee-registry/mappings/index.ts: -------------------------------------------------------------------------------- 1 | import { Collection } from "../generated/schema"; 2 | import { RoyaltyFeeUpdate } from "../generated/RoyaltyFeeRegistry/RoyaltyFeeRegistry"; 3 | import { toBigDecimal } from "../../../helpers/utils"; 4 | import { ZERO_ADDRESS, ZERO_BD, ZERO_BI, ONE_BI } from "../../../helpers/constants"; 5 | 6 | export function handleRoyaltyFeeUpdate(event: RoyaltyFeeUpdate): void { 7 | let collection = Collection.load(event.params.collection.toHex()); 8 | if (collection === null) { 9 | collection = new Collection(event.params.collection.toHex()); 10 | collection.setter = ZERO_ADDRESS; 11 | collection.receiver = ZERO_ADDRESS; 12 | collection.royaltyFee = ZERO_BD; 13 | collection.maxRoyaltyFee = ZERO_BD; 14 | collection.firstUpdateBlock = event.block.number; 15 | collection.firstUpdateDate = event.block.timestamp; 16 | collection.lastUpdateBlock = ZERO_BI; 17 | collection.lastUpdateDate = ZERO_BI; 18 | collection.numberChanges = ZERO_BI; 19 | } 20 | 21 | collection.setter = event.params.setter; 22 | collection.receiver = event.params.receiver; 23 | 24 | if (toBigDecimal(event.params.fee, 2).gt(collection.royaltyFee)) { 25 | collection.maxRoyaltyFee = toBigDecimal(event.params.fee, 2); 26 | } 27 | 28 | collection.royaltyFee = toBigDecimal(event.params.fee, 2); 29 | collection.lastUpdateBlock = event.block.number; 30 | collection.lastUpdateDate = event.block.timestamp; 31 | collection.numberChanges = collection.numberChanges.plus(ONE_BI); 32 | collection.save(); 33 | } 34 | -------------------------------------------------------------------------------- /subgraphs/royalty-fee-registry/matchstick.yaml: -------------------------------------------------------------------------------- 1 | libsFolder: ../../node_modules/ 2 | -------------------------------------------------------------------------------- /subgraphs/royalty-fee-registry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "royalty-fee-registry", 3 | "description": "Royalty Fee Registry for NFT collections", 4 | "version": "1.0.0", 5 | "repository": "git@github.com:looksrare/subgraph.git", 6 | "author": "LooksRare", 7 | "license": "MIT", 8 | "scripts": { 9 | "codegen": "graph codegen subgraph.yaml", 10 | "build:mainnet": "graph build subgraph.yaml", 11 | "deploy:mainnet": "graph deploy --product hosted-service looksrare/royalty-fee-registry subgraph.yaml", 12 | "deploy:studio": "graph codegen subgraph.yaml && graph build subgraph.yaml && graph deploy --studio looksrare-royalty-fee-registry", 13 | "test": "graph test -r", 14 | "test:lerna": "graph codegen subgraph.yaml && graph build subgraph.yaml && graph test -r" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /subgraphs/royalty-fee-registry/schema.graphql: -------------------------------------------------------------------------------- 1 | """ 2 | Collection 3 | """ 4 | type Collection @entity { 5 | "ID (address)" 6 | id: ID! 7 | 8 | "Setter address" 9 | setter: Bytes! 10 | 11 | "Receiver address" 12 | receiver: Bytes! 13 | 14 | "Current royalty fee" 15 | royaltyFee: BigDecimal! 16 | 17 | "Max royalty fee" 18 | maxRoyaltyFee: BigDecimal! 19 | 20 | "First update block" 21 | firstUpdateBlock: BigInt! 22 | 23 | "First update date" 24 | firstUpdateDate: BigInt! 25 | 26 | "Last update block" 27 | lastUpdateBlock: BigInt! 28 | 29 | "Last update date" 30 | lastUpdateDate: BigInt! 31 | 32 | "Number of updates for royalty info" 33 | numberChanges: BigInt! 34 | } 35 | -------------------------------------------------------------------------------- /subgraphs/royalty-fee-registry/subgraph.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | description: Royalty Fee Registry by LooksRare 3 | repository: https://github.com/looksrare 4 | schema: 5 | file: ./schema.graphql 6 | dataSources: 7 | - kind: ethereum/contract 8 | name: RoyaltyFeeRegistry 9 | network: mainnet 10 | source: 11 | abi: RoyaltyFeeRegistry 12 | address: "0x55010472a93921a117aAD9b055c141060c8d8022" 13 | startBlock: 13885615 14 | mapping: 15 | kind: ethereum/events 16 | apiVersion: 0.0.7 17 | language: wasm/assemblyscript 18 | file: ./mappings/index.ts 19 | entities: 20 | - Collection 21 | abis: 22 | - name: RoyaltyFeeRegistry 23 | file: ../../node_modules/@looksrare/sdk/dist/RoyaltyFeeRegistryAbi.json 24 | eventHandlers: 25 | - event: RoyaltyFeeUpdate(indexed address,indexed address,indexed address,uint256) 26 | handler: handleRoyaltyFeeUpdate 27 | -------------------------------------------------------------------------------- /subgraphs/royalty-fee-registry/tests/helpers/utils.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt, ethereum } from "@graphprotocol/graph-ts"; 2 | import { newMockEvent } from "matchstick-as"; 3 | import { RoyaltyFeeUpdate } from "../../generated/RoyaltyFeeRegistry/RoyaltyFeeRegistry"; 4 | 5 | /** 6 | * @param collection 7 | * @param setter 8 | * @param receiver 9 | * @param fee 10 | * @returns RoyaltyFeeUpdate Event 11 | */ 12 | export function createRoyaltyFeeUpdateEvent( 13 | collection: Address, 14 | setter: Address, 15 | receiver: Address, 16 | fee: BigInt, 17 | ): RoyaltyFeeUpdate { 18 | const mockEvent = newMockEvent(); 19 | const newRoyaltyFeeUpdateEvent = new RoyaltyFeeUpdate( 20 | mockEvent.address, 21 | mockEvent.logIndex, 22 | mockEvent.transactionLogIndex, 23 | mockEvent.logType, 24 | mockEvent.block, 25 | mockEvent.transaction, 26 | mockEvent.parameters, 27 | mockEvent.receipt, 28 | ); 29 | 30 | newRoyaltyFeeUpdateEvent.parameters = []; 31 | 32 | const collectionParam = new ethereum.EventParam("collection", ethereum.Value.fromAddress(collection)); 33 | const setterParam = new ethereum.EventParam("setter", ethereum.Value.fromAddress(setter)); 34 | const receiverParam = new ethereum.EventParam("receiver", ethereum.Value.fromAddress(receiver)); 35 | const feeParam = new ethereum.EventParam("fee", ethereum.Value.fromSignedBigInt(fee)); 36 | 37 | newRoyaltyFeeUpdateEvent.parameters.push(collectionParam); 38 | newRoyaltyFeeUpdateEvent.parameters.push(setterParam); 39 | newRoyaltyFeeUpdateEvent.parameters.push(receiverParam); 40 | newRoyaltyFeeUpdateEvent.parameters.push(feeParam); 41 | 42 | return newRoyaltyFeeUpdateEvent; 43 | } 44 | -------------------------------------------------------------------------------- /subgraphs/royalty-fee-registry/tests/royaltyFeeRegistry.test.ts: -------------------------------------------------------------------------------- 1 | import { Address, BigInt } from "@graphprotocol/graph-ts"; 2 | import { assert, clearStore, log, test } from "matchstick-as/assembly/index"; 3 | import { createRoyaltyFeeUpdateEvent } from "./helpers/utils"; 4 | import { Collection } from "../generated/schema"; 5 | import { handleRoyaltyFeeUpdate } from "../mappings"; 6 | import { ONE_BI } from "../../../helpers/constants"; 7 | 8 | test("RoyaltyFeeUpdate", () => { 9 | const collectionAddress = Address.fromString("0xF7c68B84A8ad29A61AF42FC31cEF1964dd80f8Ea"); 10 | const setterAddress = Address.fromString("0x0000000000000000000000000000000000000001"); 11 | const receiverAddress = Address.fromString("0x0000000000000000000000000000000000000002"); 12 | let fee = BigInt.fromI32(200); // 2% 13 | 14 | let newRoyaltyEvent = createRoyaltyFeeUpdateEvent(collectionAddress, setterAddress, receiverAddress, fee); 15 | handleRoyaltyFeeUpdate(newRoyaltyEvent); 16 | 17 | let collection = Collection.load(collectionAddress.toHex()); 18 | if (collection !== null) { 19 | assert.bytesEquals(collection.setter, setterAddress); 20 | assert.bytesEquals(collection.receiver, receiverAddress); 21 | assert.stringEquals(collection.royaltyFee.toString(), fee.div(BigInt.fromI32(100)).toString()); 22 | assert.stringEquals(collection.maxRoyaltyFee.toString(), collection.royaltyFee.toString()); 23 | assert.bigIntEquals(collection.firstUpdateBlock, ONE_BI); 24 | assert.bigIntEquals(collection.firstUpdateBlock, collection.lastUpdateBlock); 25 | assert.bigIntEquals(collection.lastUpdateDate, ONE_BI); 26 | assert.bigIntEquals(collection.numberChanges, ONE_BI); 27 | } else { 28 | log.warning("Collection doesn't exist", []); 29 | } 30 | 31 | fee = BigInt.fromI32(400); // 4% 32 | newRoyaltyEvent = createRoyaltyFeeUpdateEvent(collectionAddress, setterAddress, receiverAddress, fee); 33 | handleRoyaltyFeeUpdate(newRoyaltyEvent); 34 | 35 | collection = Collection.load(collectionAddress.toHex()); 36 | if (collection !== null) { 37 | assert.bigIntEquals(collection.numberChanges, BigInt.fromI32(2)); 38 | assert.stringEquals(collection.royaltyFee.toString(), "4"); 39 | assert.stringEquals(collection.maxRoyaltyFee.toString(), collection.royaltyFee.toString()); 40 | } else { 41 | log.warning("Collection doesn't exist", []); 42 | } 43 | 44 | fee = BigInt.fromI32(150); // 1.5% 45 | newRoyaltyEvent = createRoyaltyFeeUpdateEvent(collectionAddress, setterAddress, receiverAddress, fee); 46 | handleRoyaltyFeeUpdate(newRoyaltyEvent); 47 | 48 | collection = Collection.load(collectionAddress.toHex()); 49 | if (collection !== null) { 50 | assert.bigIntEquals(collection.numberChanges, BigInt.fromI32(3)); 51 | assert.stringEquals(collection.royaltyFee.toString(), "1.5"); 52 | assert.stringEquals(collection.maxRoyaltyFee.toString(), "4"); 53 | } else { 54 | log.warning("Collection doesn't exist", []); 55 | } 56 | 57 | clearStore(); 58 | }); 59 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["subgraphs"], 4 | } 5 | --------------------------------------------------------------------------------