diff --git a/package.json b/package.json index 270f551..1770fae 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@sablier/safe-app", "description": "Safe App for interacting with the Sablier protocol", - "version": "1.1.4", + "version": "1.1.5", "browserslist": { "production": [ ">0.2%", @@ -116,6 +116,7 @@ "scripts": { "build": "react-app-rewired build", "clean": "shx rm -rf ./build", + "dev": "react-app-rewired --openssl-legacy-provider start", "eject": "react-scripts eject", "lint": "yarn lint:ts && yarn lint:styles && yarn prettier:check && yarn typecheck", "lint:styles": "stylelint --config ./.stylelintrc.js \"./src/**/*.{css,ts,tsx,scss}\"", diff --git a/src/components/EtherscanLink/Buttons/EtherscanButton.tsx b/src/components/EtherscanLink/Buttons/EtherscanButton.tsx index e1f4e55..e9ef847 100644 --- a/src/components/EtherscanLink/Buttons/EtherscanButton.tsx +++ b/src/components/EtherscanLink/Buttons/EtherscanButton.tsx @@ -1,17 +1,34 @@ import ExternalLinkIcon from "../../../assets/external-link.svg"; -import { BSC_MAINNET_ID, ETHEREUM_MAINNET_ID, POLYGON_MAINNET_ID, RINKEBY_ID } from "../../../constants/chains"; +import { + ARBITRUM_MAINNET_ID, + AVALANCHE_MAINNET_ID, + BSC_MAINNET_ID, + ETHEREUM_MAINNET_ID, + OPTIMISM_MAINNET_ID, + POLYGON_MAINNET_ID, + RINKEBY_ID, +} from "../../../constants/chains"; import { LinkContainer, StyledTooltip } from "./common"; function getEtherscanLink(chainId: number, type: string, data: string): string { let base; switch (chainId) { + case ARBITRUM_MAINNET_ID: + base = "https://arbiscan.io"; + break; + case AVALANCHE_MAINNET_ID: + base = "https://snowtrace.io"; + break; case BSC_MAINNET_ID: base = "https://bscscan.com"; break; case ETHEREUM_MAINNET_ID: base = "https://ethersscan.io"; break; + case OPTIMISM_MAINNET_ID: + base = "https://optimistic.etherscan.io"; + break; case POLYGON_MAINNET_ID: base = "https://polygonscan.com"; break; diff --git a/src/config/sablier.ts b/src/config/sablier.ts index e833ebb..2916040 100644 --- a/src/config/sablier.ts +++ b/src/config/sablier.ts @@ -1,6 +1,14 @@ import { AddressZero } from "@ethersproject/constants"; -import { BSC_MAINNET_ID, ETHEREUM_MAINNET_ID, POLYGON_MAINNET_ID, RINKEBY_ID } from "../constants/chains"; +import { + ARBITRUM_MAINNET_ID, + AVALANCHE_MAINNET_ID, + BSC_MAINNET_ID, + ETHEREUM_MAINNET_ID, + OPTIMISM_MAINNET_ID, + POLYGON_MAINNET_ID, + RINKEBY_ID, +} from "../constants/chains"; import type { SablierChainId } from "../types"; type PayrollContractsMap = { [chainId in SablierChainId]: string }; @@ -14,8 +22,11 @@ type SablierContractsMap = { [chainId in SablierChainId]: string }; // This is the v1.1 release of the "Sablier.sol" contract. const SABLIER_CONTRACTS: SablierContractsMap = { + [ARBITRUM_MAINNET_ID]: "0xaDB944B478818d95659067E70D2e5Fc43Fa3eDe9", + [AVALANCHE_MAINNET_ID]: "0x73f503fad13203C87889c3D5c567550b2d41D7a4", [BSC_MAINNET_ID]: "0x05BC7f5fb7F248d44d38703e5C921A8c16825161", [ETHEREUM_MAINNET_ID]: "0xCD18eAa163733Da39c232722cBC4E8940b1D8888", + [OPTIMISM_MAINNET_ID]: "0x6C5927c0679e6d857E87367bb635decbcB20F31c", [POLYGON_MAINNET_ID]: "0xAC18EAB6592F5fF6F9aCf5E0DCE0Df8E49124C06", [RINKEBY_ID]: "0xC1f3af5DC05b0C51955804b2afc80eF8FeED67b9", }; diff --git a/src/config/tokens.ts b/src/config/tokens.ts index 18b3f58..64ee795 100644 --- a/src/config/tokens.ts +++ b/src/config/tokens.ts @@ -5,6 +5,7 @@ import { AVALANCHE_MAINNET_ID, BSC_MAINNET_ID, ETHEREUM_MAINNET_ID, + OPTIMISM_MAINNET_ID, POLYGON_MAINNET_ID, RINKEBY_ID, } from "../constants/chains"; @@ -15,6 +16,7 @@ type TokenMap = { [key in SablierChainId]: TokenItem[] }; // It's okay to leave the "iconUrl" property empty at this step const TOKENS: TokenMap = { [ARBITRUM_MAINNET_ID]: [ + { address: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", decimals: 18, iconUrl: "", id: "DAI", label: "DAI" }, { address: "0xb9c8f0d3254007ee4b98970b94544e473cd610ec", decimals: 18, iconUrl: "", id: "QI", label: "QI" }, { address: "0x3F56e0c36d275367b8C502090EDF38289b3dEa0d", @@ -147,6 +149,9 @@ const TOKENS: TokenMap = { { address: "0x8798249c2E607446EfB7Ad49eC89dD1865Ff4272", decimals: 18, iconUrl: "", id: "xSUSHI", label: "xSUSHI" }, { address: "0x597ad1e0c13bfe8025993d9e79c69e1c0233522e", decimals: 6, iconUrl: "", id: "yUSDC", label: "yUSDC" }, ], + [OPTIMISM_MAINNET_ID]: [ + { address: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", decimals: 18, iconUrl: "", id: "DAI", label: "DAI" }, + ], [POLYGON_MAINNET_ID]: [ { address: "0xD6DF932A45C0f255f85145f286eA0b292B21C90B", decimals: 18, iconUrl: "", id: "AAVE", label: "AAVE" }, { address: "0xdAb529f40E671A1D4bF91361c21bf9f0C9712ab7", decimals: 18, iconUrl: "", id: "BUSD", label: "BUSD" }, @@ -199,6 +204,7 @@ const CUSTOM_TOKEN_ICONS: string[] = [ "CRE8R", "CW", "CWAP", + "DAI", "D2D", "DHT", "DOG", @@ -268,9 +274,18 @@ function getIconUrl(chainId: SablierChainId, address: string, symbol: string): s let chain = ""; switch (chainId) { + case ARBITRUM_MAINNET_ID: + chain = "arbitrum"; + break; + case AVALANCHE_MAINNET_ID: + chain = "avalanche"; + break; case BSC_MAINNET_ID: chain = "smartchain"; break; + case OPTIMISM_MAINNET_ID: + chain = "optimism"; + break; case POLYGON_MAINNET_ID: chain = "polygon"; break; diff --git a/src/constants/chains.ts b/src/constants/chains.ts index d25c2d4..11b100c 100644 --- a/src/constants/chains.ts +++ b/src/constants/chains.ts @@ -2,5 +2,6 @@ export const ARBITRUM_MAINNET_ID: number = 42161; export const AVALANCHE_MAINNET_ID: number = 43114; export const BSC_MAINNET_ID: number = 56; export const ETHEREUM_MAINNET_ID: number = 1; +export const OPTIMISM_MAINNET_ID = 10; export const POLYGON_MAINNET_ID: number = 137; export const RINKEBY_ID: number = 4; diff --git a/src/graphql/streams.ts b/src/graphql/streams.ts index 66edca1..377ffa3 100644 --- a/src/graphql/streams.ts +++ b/src/graphql/streams.ts @@ -1,7 +1,15 @@ import { ApolloClient, InMemoryCache, gql } from "@apollo/client"; import type { DocumentNode } from "@apollo/client"; -import { BSC_MAINNET_ID, ETHEREUM_MAINNET_ID, POLYGON_MAINNET_ID, RINKEBY_ID } from "../constants/chains"; +import { + ARBITRUM_MAINNET_ID, + AVALANCHE_MAINNET_ID, + BSC_MAINNET_ID, + ETHEREUM_MAINNET_ID, + OPTIMISM_MAINNET_ID, + POLYGON_MAINNET_ID, + RINKEBY_ID, +} from "../constants/chains"; import type { SablierChainId, Stream } from "../types"; type Response = { @@ -9,8 +17,11 @@ type Response = { }; const subgraphUri: { [key in SablierChainId]: string } = { + [ARBITRUM_MAINNET_ID]: "https://api.thegraph.com/subgraphs/name/sablierhq/sablier-arbitrum", + [AVALANCHE_MAINNET_ID]: "https://api.thegraph.com/subgraphs/name/sablierhq/sablier-avalanche", [BSC_MAINNET_ID]: "https://api.thegraph.com/subgraphs/name/sablierhq/sablier-bsc", [ETHEREUM_MAINNET_ID]: "https://api.thegraph.com/subgraphs/name/sablierhq/sablier", + [OPTIMISM_MAINNET_ID]: "https://api.thegraph.com/subgraphs/name/sablierhq/sablier-optimism", [POLYGON_MAINNET_ID]: "https://api.thegraph.com/subgraphs/name/sablierhq/sablier-matic", [RINKEBY_ID]: "https://api.thegraph.com/subgraphs/name/sablierhq/sablier-rinkeby", }; diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts index 982228e..f0902c3 100644 --- a/src/hooks/useContract.ts +++ b/src/hooks/useContract.ts @@ -4,7 +4,15 @@ import { JsonRpcProvider, Provider, getDefaultProvider } from "@ethersproject/pr import { useSafeAppsSDK } from "@gnosis.pm/safe-apps-react-sdk"; import { useMemo } from "react"; -import { BSC_MAINNET_ID, ETHEREUM_MAINNET_ID, POLYGON_MAINNET_ID, RINKEBY_ID } from "../constants/chains"; +import { + ARBITRUM_MAINNET_ID, + AVALANCHE_MAINNET_ID, + BSC_MAINNET_ID, + ETHEREUM_MAINNET_ID, + OPTIMISM_MAINNET_ID, + POLYGON_MAINNET_ID, + RINKEBY_ID, +} from "../constants/chains"; import { getEnvVar } from "../utils/env"; export default function useContract(abi: ContractInterface, address?: string): Contract { @@ -13,6 +21,16 @@ export default function useContract(abi: ContractInterface, address?: string): C const provider: Provider = useMemo(() => { const infuraKey: string = getEnvVar("REACT_APP_INFURA_API_KEY"); switch (safe.chainId) { + case ARBITRUM_MAINNET_ID: + return new JsonRpcProvider("https://arbitrum-mainnet.infura.io/v3/" + infuraKey, { + chainId: safe.chainId, + name: "arbitrum-mainnet", + }); + case AVALANCHE_MAINNET_ID: + return new JsonRpcProvider("https://api.avax.network/ext/bc/C/rpc/", { + chainId: safe.chainId, + name: "avalanche-c", + }); case BSC_MAINNET_ID: return new JsonRpcProvider("https://bsc-dataseed1.ninicoin.io", { chainId: safe.chainId, name: "bsc-mainnet" }); case ETHEREUM_MAINNET_ID: @@ -20,6 +38,11 @@ export default function useContract(abi: ContractInterface, address?: string): C chainId: safe.chainId, name: "ethereum", }); + case OPTIMISM_MAINNET_ID: + return new JsonRpcProvider("https://optimism-mainnet.infura.io/v3/" + infuraKey, { + chainId: safe.chainId, + name: "optimism-mainnet", + }); case POLYGON_MAINNET_ID: return new JsonRpcProvider("https://polygon-mainnet.infura.io/v3/" + infuraKey, { chainId: safe.chainId, diff --git a/src/types/index.ts b/src/types/index.ts index ee001c3..b5c34de 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,6 +1,14 @@ import { BigNumberish } from "@ethersproject/bignumber"; -import { BSC_MAINNET_ID, ETHEREUM_MAINNET_ID, POLYGON_MAINNET_ID, RINKEBY_ID } from "../constants/chains"; +import { + ARBITRUM_MAINNET_ID, + AVALANCHE_MAINNET_ID, + BSC_MAINNET_ID, + ETHEREUM_MAINNET_ID, + OPTIMISM_MAINNET_ID, + POLYGON_MAINNET_ID, + RINKEBY_ID, +} from "../constants/chains"; import { StreamStatus } from "../constants/streams"; export type Cancellation = { @@ -29,8 +37,11 @@ export type HumanReadableStream = { }; export type SablierChainId = + | typeof ARBITRUM_MAINNET_ID + | typeof AVALANCHE_MAINNET_ID | typeof BSC_MAINNET_ID | typeof ETHEREUM_MAINNET_ID + | typeof OPTIMISM_MAINNET_ID | typeof POLYGON_MAINNET_ID | typeof RINKEBY_ID;