Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Use subgraph for Gnosis Safe information
Sebastian Martinez committed 4 years ago
commit 9a4f3547b6c1fbb8753e9124b99bc5016313b910
parent 299e7a2c32ddd428d61ac4320a89c0dad8953098
2 files changed +48 -51
modified 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<Org[]> {
-
    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<Org[]> {
+
    type Safe = { id: string; owners: string[]; threshold: number };
+

+
    // 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] });

-
  static async getOrgsByMember(memberAddr: string, config: Config): Promise<Org[]> {
-
    const wallets = await utils.getOwnerSafes(memberAddr, config);
-
    const owners = wallets?.concat([memberAddr]);
-
    const orgsResult = await utils.querySubgraph(config.orgs.subgraph, GetOrgsByOwner, { owners });
+
    const orgs: any[] = [...orgsByOwner.orgs];

-
    return orgsResult.orgs.map((o: { id: string; owner: string }) => {
+
    return orgs.map((o: { id: string; owner: string }) => {
      return new Org(o.id, o.owner);
    });
  }
modified 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<boolean> {
-
  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<Safe | null> {
-
  if (! config.safe.api) return null;
-

-
  const addr = ethers.utils.getAddress(address);
-
  const response = await fetch(`${config.safe.api}/api/v1/safes/${addr}/`, {
-
    method: 'GET',
-
    headers: {
-
      'Accept': 'application/json',
-
    }
-
  });
+
  // For the subgraph we need to pass a lowercase address
+
  const query = await querySubgraph(config.orgs.subgraph, GetSafe, { addr: address.toLowerCase() });

-
  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<string[] | null> {
-
  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<Array<Token>> {
  const userBalances = await config.provider.send("alchemy_getTokenBalances", [address, "DEFAULT_TOKENS"]);