From 9a4f3547b6c1fbb8753e9124b99bc5016313b910 Mon Sep 17 00:00:00 2001 From: Sebastian Martinez Date: Sat, 13 Nov 2021 16:24:29 +0100 Subject: [PATCH] Use subgraph for Gnosis Safe information Signed-off-by: Sebastian Martinez --- src/base/orgs/Org.ts | 52 ++++++++++++++++++++++++++++++++------------ src/utils.ts | 47 +++++++++------------------------------ 2 files changed, 48 insertions(+), 51 deletions(-) diff --git a/src/base/orgs/Org.ts b/src/base/orgs/Org.ts index b92a08a96..33c7069fb 100644 --- a/src/base/orgs/Org.ts +++ b/src/base/orgs/Org.ts @@ -31,17 +31,42 @@ const GetOrgs = ` } `; -const GetOrgsByOwner = ` - query GetOrgsByOwner($owners: [String!]!) { +const GetSafesByOwners = ` + query GetSafesByOwner($owners: [String!]!) { + safes(where: { owners_contains: $owners }) { + id + owners + threshold + } + } +`; + +const GetOrgsByOwners = ` + query GetOrgsByOwners($owners: [String!]!) { orgs(where: { owner_in: $owners }) { id owner + safe { + id + owners + threshold + } creator timestamp } } `; +export const GetSafe = ` + query GetSafe($addr: ID!) { + safe(id: $addr) { + id + owners + threshold + } + } +`; + export class Org { address: string; owner: string; @@ -259,20 +284,19 @@ export class Org { return profile; } - // Return only Orgs that have a specific user as owner - static async getOrgsByOwner(owner: string, config: Config): Promise { - const orgsResult = await utils.querySubgraph(config.orgs.subgraph, GetOrgsByOwner, { owners: [owner] }); - return orgsResult.orgs.map((o: { id: string; owner: string }) => { - return new Org(o.id, o.owner); - }); - } + static async getOrgsByMember(owner: string, config: Config): Promise { + type Safe = { id: string; owners: string[]; threshold: number }; - static async getOrgsByMember(memberAddr: string, config: Config): Promise { - const wallets = await utils.getOwnerSafes(memberAddr, config); - const owners = wallets?.concat([memberAddr]); - const orgsResult = await utils.querySubgraph(config.orgs.subgraph, GetOrgsByOwner, { owners }); + // TODO: We use two subgraph queries since we can't do a filter query yet in the subgraph + // https://github.com/graphprotocol/graph-node/issues/2539#issuecomment-855979841 + const safesByOwner = await utils.querySubgraph(config.orgs.subgraph, GetSafesByOwners, { owners: [owner] }); + const safes = safesByOwner.safes.reduce( + (prev: any, curr: Safe) => prev.concat(curr.id), []); + const orgsByOwner = await utils.querySubgraph(config.orgs.subgraph, GetOrgsByOwners, { owners: [...safes, owner] }); - return orgsResult.orgs.map((o: { id: string; owner: string }) => { + const orgs: any[] = [...orgsByOwner.orgs]; + + return orgs.map((o: { id: string; owner: string }) => { return new Org(o.id, o.owner); }); } diff --git a/src/utils.ts b/src/utils.ts index 017cebf50..0d66f0ea8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -11,6 +11,7 @@ import { getAvatar, getSeedHost, getSeedId, getAnchorsAccount, getRegistration } import type { BasicProfile } from '@datamodels/identity-profile-basic'; import { ProfileType } from '@app/profile'; import { parseUnits } from "@ethersproject/units"; +import { GetSafe } from "@app/base/orgs/Org"; export enum AddressType { Contract, @@ -354,56 +355,28 @@ export async function resolveEnsProfile(addressOrName: string, profileType: Prof // Check whether a Gnosis Safe exists at an address. export async function isSafe(address: string, config: Config): Promise { - if (! config.safe.api) return false; + // For the subgraph we need to pass a lowercase address + const query = await querySubgraph(config.orgs.subgraph, GetSafe, { addr: address.toLowerCase() }); - const addr = ethers.utils.getAddress(address); - const response = await fetch(`${config.safe.api}/api/v1/safes/${addr}/`, { method: 'HEAD' }); - - return response.ok; + return query.safe !== null ? true : false; } // Get a Gnosis Safe at an address. export async function getSafe(address: string, config: Config): Promise { - if (! config.safe.api) return null; + // For the subgraph we need to pass a lowercase address + const query = await querySubgraph(config.orgs.subgraph, GetSafe, { addr: address.toLowerCase() }); - const addr = ethers.utils.getAddress(address); - const response = await fetch(`${config.safe.api}/api/v1/safes/${addr}/`, { - method: 'GET', - headers: { - 'Accept': 'application/json', - } - }); - - if (! response.ok) { + if (! query.safe) { return null; } - const json = await response.json(); return { - address: json.address, - owners: json.owners, - threshold: json.threshold + address: query.safe.id, + owners: query.safe.owners, + threshold: query.safe.threshold }; } -// Get the Gnosis Safe addresses owned by the given address. -export async function getOwnerSafes(owner: string, config: Config): Promise { - if (! config.safe.api) return null; - - const addr = ethers.utils.getAddress(owner); - const response = await fetch(`${config.safe.api}/api/v1/owners/${addr}/safes/`, { - method: 'GET', - headers: { 'Accept': 'application/json' } - }); - - if (! response.ok) { - return null; - } - const json = await response.json(); - - return json.safes; -} - // Get token balances for an address. export async function getTokens(address: string, config: Config): Promise> { const userBalances = await config.provider.send("alchemy_getTokenBalances", [address, "DEFAULT_TOKENS"]);