From 593d0668241a8f4a424cd7ec708c7f010d653cbe Mon Sep 17 00:00:00 2001 From: Petrovska Date: Wed, 23 Aug 2023 00:33:05 +0200 Subject: [PATCH 1/4] feat: use subgraph to pre-populate gauge options for the safe user in ui --- package-lock.json | 49 +++++++++++++++++++++ package.json | 4 +- src/components/EnablePlugin.tsx | 37 +++++++++++----- src/components/GraphQuery.tsx | 8 ++-- src/queries/useGetUserGaugePositions.tsx | 55 ++++++++++++++++++++++++ src/queries/user_active_stakes.graphql | 2 +- 6 files changed, 138 insertions(+), 17 deletions(-) create mode 100644 src/queries/useGetUserGaugePositions.tsx diff --git a/package-lock.json b/package-lock.json index 894c754..286e2fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,8 @@ "@tanstack/react-table": "^8.9.3", "@wagmi/cli": "^1.3.0", "buffer": "^6.0.3", + "graphql": "^16.8.0", + "graphql-request": "^6.1.0", "process": "^0.11.10", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -1679,6 +1681,14 @@ "cross-fetch": "^3.1.5" } }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/@heroicons/react": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz", @@ -5028,6 +5038,26 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, + "node_modules/graphql": { + "version": "16.8.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.0.tgz", + "integrity": "sha512-0oKGaR+y3qcS5mCu1vb7KG+a89vjn06C7Ihq/dDl3jA+A8B3TKomvi3CiEcVLJQGalbu8F52LxkOym7U5sSfbg==", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz", + "integrity": "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==", + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0", + "cross-fetch": "^3.1.5" + }, + "peerDependencies": { + "graphql": "14 - 16" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -8865,6 +8895,11 @@ "cross-fetch": "^3.1.5" } }, + "@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==" + }, "@heroicons/react": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.18.tgz", @@ -11356,6 +11391,20 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, + "graphql": { + "version": "16.8.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.0.tgz", + "integrity": "sha512-0oKGaR+y3qcS5mCu1vb7KG+a89vjn06C7Ihq/dDl3jA+A8B3TKomvi3CiEcVLJQGalbu8F52LxkOym7U5sSfbg==" + }, + "graphql-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz", + "integrity": "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==", + "requires": { + "@graphql-typed-document-node/core": "^3.2.0", + "cross-fetch": "^3.1.5" + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", diff --git a/package.json b/package.json index b19f972..3c9168f 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,8 @@ "@tanstack/react-table": "^8.9.3", "@wagmi/cli": "^1.3.0", "buffer": "^6.0.3", + "graphql": "^16.8.0", + "graphql-request": "^6.1.0", "process": "^0.11.10", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -42,4 +44,4 @@ "typescript": "^4.9.5", "vite": "^4.1.4" } -} \ No newline at end of file +} diff --git a/src/components/EnablePlugin.tsx b/src/components/EnablePlugin.tsx index 5dd8f61..97952aa 100644 --- a/src/components/EnablePlugin.tsx +++ b/src/components/EnablePlugin.tsx @@ -12,6 +12,7 @@ import "../../styles/enablePlugin.css"; import { encodeFunctionData } from "viem"; import useGnosisBatch from "../queries/useGnosisBatch"; +import useGetUserGaugePositions from "../queries/useGetUserGaugePositions"; import { smartGardenManagerABI, @@ -19,20 +20,20 @@ import { harvesterPluginAddress, } from "../generated"; -// TODO: this gauge addr needs to be retrieve from subgraph. Probably auto-fill a select options? -// https://optimistic.etherscan.io/address/0xa1034Ed2C9eb616d6F7f318614316e64682e7923 -const GAUGE_USDC_DOLA_ADDRESS = "0xa1034Ed2C9eb616d6F7f318614316e64682e7923"; - export function EnablePlugin() { const { mutate: gnosisBatch } = useGnosisBatch(); + const { data: gaugePositions, isLoading, error } = useGetUserGaugePositions(); // defaulting: 86400(1 - day) const formPluginConfig = useFormStore({ - defaultValues: { gaugeAddr: GAUGE_USDC_DOLA_ADDRESS, cadence: 86400 }, + defaultValues: { gaugeAddr: "", cadence: 86400 }, }); const cadenceValue = formPluginConfig.useValue( formPluginConfig.names.cadence, ); + const gaugeAddrValue = formPluginConfig.useValue( + formPluginConfig.names.gaugeAddr, + ); formPluginConfig.useSubmit(() => { const values = formPluginConfig.getState().values; @@ -64,6 +65,13 @@ export function EnablePlugin() { parseInt(event.target.value), ); + // Use to modify the value of `vault` in the } + > + {gaugePositions?.map((gauge, index) => { + return ( + + ); + })} + { + gaugeResults.push({ + id: position.gauge.id, + pool_name: position.gauge.pool_name, + }); + }); + return gaugeResults; + } catch (error) { + console.error(error); + return null; + } +} + +export default function useGetUserGaugePositions() { + const { address } = useAccount(); + return useQuery(["userGaugePositions", address?.toLowerCase()], () => + getUserGaugePositions(address?.toLowerCase()), + ); +} diff --git a/src/queries/user_active_stakes.graphql b/src/queries/user_active_stakes.graphql index a6d7476..80eba72 100644 --- a/src/queries/user_active_stakes.graphql +++ b/src/queries/user_active_stakes.graphql @@ -17,4 +17,4 @@ query UserGaugePositions { pool_name } } -} +} \ No newline at end of file From 030cdf7fd2b7543d37891eb9aed4a302d3b69a2f Mon Sep 17 00:00:00 2001 From: Petrovska Date: Wed, 23 Aug 2023 11:05:38 +0200 Subject: [PATCH 2/4] feat: remove `GraphQuery.tsx` file --- src/components/GraphQuery.tsx | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 src/components/GraphQuery.tsx diff --git a/src/components/GraphQuery.tsx b/src/components/GraphQuery.tsx deleted file mode 100644 index b2e05e9..0000000 --- a/src/components/GraphQuery.tsx +++ /dev/null @@ -1,34 +0,0 @@ -export async function getBalance(_address: string) { - // define url and query - var url = "https://api.studio.thegraph.com/query/50162/smartgarden/v0.0.2e"; - var query = `{ - gaugePosition(id: "${_address}") { - balance - } - }`; - - // add request metadata - var options = { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - query: query, - }), - }; - - // fetch data - var response = await fetch(url, options); - - // parse as json - var queryResult = await response.json(); - - // determine balance or default to 0 - try { - var balance = queryResult["data"]["gaugePosition"]["balance"]; - } catch { - var balance = 0; - } - - // log result - console.log(balance); -} From 9249a3041ccf1c21823b747f62fb93a0656228c6 Mon Sep 17 00:00:00 2001 From: Petrovska Date: Wed, 23 Aug 2023 23:02:34 +0200 Subject: [PATCH 3/4] feat: introduce feeling of subgraph<>chain id relation --- src/helpers/chainDetails.tsx | 13 +++++++++++++ src/queries/useGetUserGaugePositions.tsx | 21 ++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 src/helpers/chainDetails.tsx diff --git a/src/helpers/chainDetails.tsx b/src/helpers/chainDetails.tsx new file mode 100644 index 0000000..a7154ab --- /dev/null +++ b/src/helpers/chainDetails.tsx @@ -0,0 +1,13 @@ +// in future this `type` may contain more data pieces +interface IChainDetails { + [key: number]: { + subgraphGauges: string; + }; +} + +export const chainDetails: IChainDetails = { + 10: { + subgraphGauges: + "https://api.studio.thegraph.com/proxy/50162/smartgarden-optimism-gauges/version/latest", + }, +}; diff --git a/src/queries/useGetUserGaugePositions.tsx b/src/queries/useGetUserGaugePositions.tsx index 601a7c4..ffa7958 100644 --- a/src/queries/useGetUserGaugePositions.tsx +++ b/src/queries/useGetUserGaugePositions.tsx @@ -1,19 +1,24 @@ import { useQuery } from "@tanstack/react-query"; -import { useAccount } from "wagmi"; +import { useAccount, useNetwork } from "wagmi"; import { request, gql } from "graphql-request"; -const SUBGRAPH_URL = - "https://api.studio.thegraph.com/proxy/50162/smartgarden-optimism-gauges/version/latest"; +import { chainDetails } from "../helpers/chainDetails"; export interface IGaugePositions { id: string; pool_name: string; } -async function getUserGaugePositions(safeAddress: string | undefined) { +async function getUserGaugePositions( + safeAddress: string | undefined, + chainId: number | undefined, +) { try { if (!safeAddress) throw new Error("No Safe Account"); + if (!chainId) throw new Error("Missing Chain ID"); + if (!chainDetails[chainId].subgraphGauges) + throw new Error("No Gauge subgraph url available"); const querySafeAddr = gql` query UserGaugePositions { gaugePositions( @@ -31,8 +36,9 @@ async function getUserGaugePositions(safeAddress: string | undefined) { } } `; - const positions = (await request(SUBGRAPH_URL, querySafeAddr)) - .gaugePositions; + const positions = ( + await request(chainDetails[chainId].subgraphGauges, querySafeAddr) + ).gaugePositions; const gaugeResults: IGaugePositions[] = []; positions.forEach((position: any) => { gaugeResults.push({ @@ -49,7 +55,8 @@ async function getUserGaugePositions(safeAddress: string | undefined) { export default function useGetUserGaugePositions() { const { address } = useAccount(); + const { chain } = useNetwork(); return useQuery(["userGaugePositions", address?.toLowerCase()], () => - getUserGaugePositions(address?.toLowerCase()), + getUserGaugePositions(address?.toLowerCase(), chain?.id), ); } From be555d955c8a0578953bd8b009426172ed1043de Mon Sep 17 00:00:00 2001 From: Petrovska Date: Thu, 24 Aug 2023 00:23:49 +0200 Subject: [PATCH 4/4] feat: create separate dict for `gql` queries to export easier/mantain --- src/queries/graphql/gaugePositions.tsx | 14 ++++++++++++ src/queries/useGetUserGaugePositions.tsx | 29 ++++++++---------------- 2 files changed, 24 insertions(+), 19 deletions(-) create mode 100644 src/queries/graphql/gaugePositions.tsx diff --git a/src/queries/graphql/gaugePositions.tsx b/src/queries/graphql/gaugePositions.tsx new file mode 100644 index 0000000..84a6697 --- /dev/null +++ b/src/queries/graphql/gaugePositions.tsx @@ -0,0 +1,14 @@ +import { gql } from "graphql-request"; + +export const GET_GAUGE_POSITIONS = gql` + query UserGaugePositions($safeAddr: String!) { + gaugePositions(where: { user: $safeAddr, balance_gt: "0" }) { + gauge { + id + protocol + pool + pool_name + } + } + } +`; diff --git a/src/queries/useGetUserGaugePositions.tsx b/src/queries/useGetUserGaugePositions.tsx index ffa7958..12aff86 100644 --- a/src/queries/useGetUserGaugePositions.tsx +++ b/src/queries/useGetUserGaugePositions.tsx @@ -1,9 +1,10 @@ import { useQuery } from "@tanstack/react-query"; import { useAccount, useNetwork } from "wagmi"; -import { request, gql } from "graphql-request"; +import { request } from "graphql-request"; import { chainDetails } from "../helpers/chainDetails"; +import { GET_GAUGE_POSITIONS } from "./graphql/gaugePositions"; export interface IGaugePositions { id: string; @@ -19,25 +20,15 @@ async function getUserGaugePositions( if (!chainId) throw new Error("Missing Chain ID"); if (!chainDetails[chainId].subgraphGauges) throw new Error("No Gauge subgraph url available"); - const querySafeAddr = gql` - query UserGaugePositions { - gaugePositions( - where: { - user: "${safeAddress}" - balance_gt: "0" - } - ) { - gauge { - id - protocol - pool - pool_name - } - } - } - `; + const queryVars = { + safeAddr: `${safeAddress}`, + }; const positions = ( - await request(chainDetails[chainId].subgraphGauges, querySafeAddr) + await request( + chainDetails[chainId].subgraphGauges, + GET_GAUGE_POSITIONS, + queryVars, + ) ).gaugePositions; const gaugeResults: IGaugePositions[] = []; positions.forEach((position: any) => {