From 9913c46aae71e83cc425df405d2530e0aec2fc2e Mon Sep 17 00:00:00 2001 From: Chef Kai <74599990+ChefKai@users.noreply.github.com> Date: Thu, 27 May 2021 21:33:00 +0200 Subject: [PATCH] feat(leaderboard): Subgraph (#58) * feat(leaderboard): Subgraph * chore(leaderboard): Refactor global logic --- api/leaderboard/global.ts | 64 ++++++++++++++++++++++--------------- api/leaderboard/team.ts | 66 ++++++++++++++++++++++----------------- package.json | 2 ++ utils/index.ts | 3 ++ yarn.lock | 31 ++++++++++++++++++ 5 files changed, 113 insertions(+), 53 deletions(-) diff --git a/api/leaderboard/global.ts b/api/leaderboard/global.ts index 00755fd8..b5cf385a 100644 --- a/api/leaderboard/global.ts +++ b/api/leaderboard/global.ts @@ -1,37 +1,51 @@ import { VercelRequest, VercelResponse } from "@vercel/node"; import { toChecksumAddress } from "ethereumjs-util"; -import { getModel } from "../../utils/mongo"; -import { User } from "../../utils/types"; +import { gql, request } from "graphql-request"; +import { TRADING_COMPETITION_V1_SUBGRAPH } from "../../utils"; + +interface User { + id: string; + volumeUSD: string; + team: { + id: string; + }; +} export default async (req: VercelRequest, res: VercelResponse): Promise => { if (req.method?.toUpperCase() === "OPTIONS") { return res.status(204).end(); } - const userModel = await getModel("User"); - - const volume = await userModel.aggregate([ - { - $group: { - _id: null, - volume: { $sum: "$leaderboard.volume" }, - }, - }, - ]); - - const users = await userModel - .find({ leaderboard: { $exists: true } }) - .sort({ "leaderboard.global": "asc" }) - .limit(20) - .exec(); + const result = await request( + TRADING_COMPETITION_V1_SUBGRAPH, + gql` + { + competition(id: "1") { + id + userCount + volumeUSD + } + users(first: 500, orderBy: volumeUSD, orderDirection: desc, block: { number: 6553043 }) { + id + volumeUSD + team { + id + } + } + } + ` + ); - const data = users.map((user: User) => ({ - rank: user.leaderboard?.global, - address: toChecksumAddress(user.address), - username: user.username, - volume: user.leaderboard?.volume, - teamId: parseInt(user?.team), + const data = result.users.map((user: User, index: number) => ({ + rank: index + 1, + address: toChecksumAddress(user.id), + volume: parseFloat(user.volumeUSD), + teamId: parseInt(user.team.id), })); - return res.status(200).json({ total: users.length, volume: volume[0].volume, data }); + return res.status(200).json({ + total: parseInt(result.competition.userCount), + volume: parseFloat(result.competition.volumeUSD), + data, + }); }; diff --git a/api/leaderboard/team.ts b/api/leaderboard/team.ts index 8378c81f..b1d92707 100644 --- a/api/leaderboard/team.ts +++ b/api/leaderboard/team.ts @@ -1,7 +1,15 @@ import { VercelRequest, VercelResponse } from "@vercel/node"; import { toChecksumAddress } from "ethereumjs-util"; -import { getModel } from "../../utils/mongo"; -import { User } from "../../utils/types"; +import { gql, request } from "graphql-request"; +import { TRADING_COMPETITION_V1_SUBGRAPH } from "../../utils"; + +interface User { + id: string; + volumeUSD: string; + team: { + id: string; + }; +} export default async (req: VercelRequest, res: VercelResponse): Promise => { if (req.method?.toUpperCase() === "OPTIONS") { @@ -12,34 +20,36 @@ export default async (req: VercelRequest, res: VercelResponse): Promise ({ - rank: user.leaderboard?.team, - address: toChecksumAddress(user.address), - username: user.username, - volume: user.leaderboard?.volume, - teamId: parseInt(user?.team), + const result = await request( + TRADING_COMPETITION_V1_SUBGRAPH, + gql` + { + users (first: 500, where: { team: "${teamId}" }, orderBy: volumeUSD, orderDirection: desc, block: { number: 6553043 }) { + id + volumeUSD + team { + id + userCount + } + } + } + ` + ); + + const volume = result.users.reduce((acc: number, user: User) => { + return acc + parseFloat(user.volumeUSD); + }, 0); + + const data = result.users.map((user: User, index: number) => ({ + rank: index + 1, + address: toChecksumAddress(user.id), + volume: parseFloat(user.volumeUSD), + teamId: parseInt(user.team.id), })); - return res.status(200).json({ total: users.length, volume: volume[0].volume, data }); + return res + .status(200) + .json({ total: parseInt(result.users[0].team.userCount), volume: volume, data }); } return res.status(400).json({ error: { message: "Team unknown." } }); diff --git a/package.json b/package.json index a93a2d91..a9e8eb91 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ }, "dependencies": { "ethereumjs-util": "^7.0.10", + "graphql": "^15.5.0", + "graphql-request": "^3.4.0", "mongoose": "^5.12.11" }, "devDependencies": { diff --git a/utils/index.ts b/utils/index.ts index 5c106bb1..df865799 100644 --- a/utils/index.ts +++ b/utils/index.ts @@ -8,6 +8,9 @@ import { import blacklist from "./blacklist.json"; import { getModel } from "./mongo"; +export const TRADING_COMPETITION_V1_SUBGRAPH = + "https://api.thegraph.com/subgraphs/name/pancakeswap/trading-competition-v1"; + /** * Recover the msg.sender for a given signature based on a message. * diff --git a/yarn.lock b/yarn.lock index 7389132c..4ea78fe2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1522,6 +1522,13 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +cross-fetch@^3.0.6: + version "3.1.4" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" + integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== + dependencies: + node-fetch "2.6.1" + cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -1947,6 +1954,11 @@ expect@^27.0.1: jest-message-util "^27.0.1" jest-regex-util "^27.0.1" +extract-files@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" + integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -2170,6 +2182,20 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +graphql-request@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-3.4.0.tgz#3a400cd5511eb3c064b1873afb059196bbea9c2b" + integrity sha512-acrTzidSlwAj8wBNO7Q/UQHS8T+z5qRGquCQRv9J1InwR01BBWV9ObnoE+JS5nCCEj8wSGS0yrDXVDoRiKZuOg== + dependencies: + cross-fetch "^3.0.6" + extract-files "^9.0.0" + form-data "^3.0.0" + +graphql@^15.5.0: + version "15.5.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" + integrity sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA== + hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -3286,6 +3312,11 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-gyp-build@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"