Skip to content

Commit

Permalink
feat: fetch chain configs from config service (#596)
Browse files Browse the repository at this point in the history
  • Loading branch information
schmanu authored Jun 9, 2024
1 parent f67b309 commit 101cee1
Show file tree
Hide file tree
Showing 23 changed files with 557 additions and 188 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@safe-global/safe-apps-sdk": "^8.1.0",
"@safe-global/safe-gateway-typescript-sdk": "^3.13.2",
"@safe-global/safe-react-components": "2.0.5",
"@types/jest": "^29.5.12",
"ace-builds": "^1.15.0",
"bignumber.js": "^9.1.2",
"ethers": "^5.7.2",
Expand All @@ -48,6 +49,7 @@
"react-svg": "^16.1.34",
"react-virtualized-auto-sizer": "^1.0.6",
"react-window": "^1.8.9",
"swr": "^2.2.5",
"tslib": "^2.6.1",
"typescript": "~5.0.4"
},
Expand Down
11 changes: 5 additions & 6 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ import { AssetTransfer, CollectibleTransfer, useCsvParser } from "./hooks/useCsv
import { useEnsResolver } from "./hooks/useEnsResolver";
import CheckIcon from "./static/check.svg";
import AppIcon from "./static/logo.svg";
import { useGetAssetBalanceQuery, useGetAllNFTsQuery } from "./stores/api/balanceApi";
import { setupParserListener } from "./stores/middleware/parseListener";
import { setSafeInfo } from "./stores/slices/safeInfoSlice";
import { RootState, startAppListening } from "./stores/store";
import { RootState, selectIsLoading, startAppListening, useAppSelector } from "./stores/store";
import { buildAssetTransfers, buildCollectibleTransfers } from "./transfers/transfers";

import "./styles/globals.css";
Expand All @@ -28,8 +27,6 @@ const App: React.FC = () => {
const theme = useTheme();
const { isLoading } = useTokenList();
const { sdk, safe } = useSafeAppsSDK();
const assetBalanceQuery = useGetAssetBalanceQuery();
const nftBalanceQuery = useGetAllNFTsQuery();

const { messages } = useSelector((state: RootState) => state.messages);
const { transfers, parsing } = useSelector((state: RootState) => state.csvEditor);
Expand All @@ -39,6 +36,8 @@ const App: React.FC = () => {
const ensResolver = useEnsResolver();
const dispatch = useDispatch();

const isDataLoading = useAppSelector(selectIsLoading);

useEffect(() => {
dispatch(setSafeInfo(safe));
const subscriptions: Unsubscribe[] = [setupParserListener(startAppListening, parseCsv, ensResolver)];
Expand Down Expand Up @@ -78,7 +77,7 @@ const App: React.FC = () => {
<Box display="flex" flexDirection="column" justifyContent="left">
{
<>
{isLoading || assetBalanceQuery.isLoading || nftBalanceQuery.isLoading ? (
{isLoading || isDataLoading ? (
<Loading />
) : (
<Box display="flex" flexDirection="column" gap={2}>
Expand All @@ -91,7 +90,7 @@ const App: React.FC = () => {
<FAQModal />
</Box>
</Grid>
<Grid item xs display="flex" direction="row" alignItems="center" gap={2}>
<Grid item xs display="flex" alignItems="center" gap={2}>
<img
src={CheckIcon}
alt="check"
Expand Down
12 changes: 12 additions & 0 deletions src/AppInitializer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useSafeAppsSDK } from "@safe-global/safe-apps-react-sdk";

import { useLoadAssets, useLoadCollectibles } from "./hooks/useBalances";
import { useLoadChains } from "./hooks/useChains";

export const AppInitializer = () => {
const { safe } = useSafeAppsSDK();
useLoadChains();
useLoadAssets(safe);
useLoadCollectibles(safe);
return <></>;
};
2 changes: 2 additions & 0 deletions src/AppWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SafeThemeProvider } from "@safe-global/safe-react-components";
import { Provider as ReduxProvider } from "react-redux";

import App from "./App";
import { AppInitializer } from "./AppInitializer";
import { useDarkMode } from "./hooks/useDarkMode";
import errorIcon from "./static/error-icon.svg";
import { store } from "./stores/store";
Expand Down Expand Up @@ -42,6 +43,7 @@ export const AppWrapper = () => {
}
>
<ReduxProvider store={store}>
<AppInitializer />
<App />
</ReduxProvider>
</SafeProvider>
Expand Down
19 changes: 14 additions & 5 deletions src/__tests__/balanceCheck.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AssetBalance, NFTBalance } from "src/stores/api/balanceApi";
import { AssetBalance } from "src/stores/slices/assetBalanceSlice";
import { NFTBalance } from "src/stores/slices/collectiblesSlice";

import { AssetTransfer, CollectibleTransfer } from "../hooks/useCsvParser";
import { assetTransfersToSummary, checkAllBalances } from "../parser/balanceCheck";
Expand Down Expand Up @@ -488,6 +489,8 @@ describe("transferToSummary and check balances", () => {
},
],
next: null,
count: 2,
previous: null,
};
const biggerBalance: NFTBalance = {
results: [
Expand Down Expand Up @@ -517,6 +520,8 @@ describe("transferToSummary and check balances", () => {
},
],
next: null,
count: 3,
previous: null,
};
const smallerBalance: NFTBalance = {
results: [
Expand All @@ -530,11 +535,13 @@ describe("transferToSummary and check balances", () => {
},
],
next: null,
count: 1,
previous: null,
};

expect(checkAllBalances(undefined, exactBalance, transfers)).toHaveLength(0);
expect(checkAllBalances(undefined, biggerBalance, transfers)).toHaveLength(0);
const smallBalanceCheckResult = checkAllBalances(undefined, smallerBalance, transfers);
expect(checkAllBalances(undefined, exactBalance.results, transfers)).toHaveLength(0);
expect(checkAllBalances(undefined, biggerBalance.results, transfers)).toHaveLength(0);
const smallBalanceCheckResult = checkAllBalances(undefined, smallerBalance.results, transfers);
expect(smallBalanceCheckResult).toHaveLength(1);
expect(smallBalanceCheckResult[0].token).toEqual("Test Collectible");
expect(smallBalanceCheckResult[0].token_type).toEqual("erc721");
Expand Down Expand Up @@ -575,9 +582,11 @@ describe("transferToSummary and check balances", () => {
},
],
next: null,
count: 1,
previous: null,
};

const balanceCheckResult = checkAllBalances(undefined, exactBalance, transfers);
const balanceCheckResult = checkAllBalances(undefined, exactBalance.results, transfers);
expect(balanceCheckResult).toHaveLength(1);
expect(balanceCheckResult[0].token).toEqual("Test Collectible");
expect(balanceCheckResult[0].token_type).toEqual("erc721");
Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/tokenList.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ethers } from "ethers";

import { fetchTokenList } from "../hooks/token";
import { networkInfo } from "../networks";
import { staticNetworkInfo } from "../networks";

beforeEach(() => {
jest.spyOn(window, "fetch").mockImplementation(() => {
Expand Down Expand Up @@ -68,8 +68,8 @@ describe("Mainnet tokenlist", () => {
});

describe("Fetch should resolve for all networks", () => {
for (const chainId of networkInfo.keys()) {
it(`fetches tokens for ${networkInfo.get(chainId)?.name} network`, () => {
for (const chainId of staticNetworkInfo.keys()) {
it(`fetches tokens for ${staticNetworkInfo.get(chainId)?.name} network`, () => {
expect(() => fetchTokenList(chainId)).not.toThrow();
});
}
Expand Down
10 changes: 4 additions & 6 deletions src/components/DonateDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ import {
TextField,
Typography,
} from "@mui/material";
import { useSafeAppsSDK } from "@safe-global/safe-apps-react-sdk";
import { BigNumber, ethers } from "ethers";
import { useEffect, useState } from "react";
import { useCsvContent } from "src/hooks/useCsvContent";
import { useCurrentChain } from "src/hooks/useCurrentChain";
import { useDarkMode } from "src/hooks/useDarkMode";
import { networkInfo } from "src/networks";
import { AssetBalance } from "src/stores/api/balanceApi";
import { AssetBalance } from "src/stores/slices/assetBalanceSlice";
import { updateCsvContent } from "src/stores/slices/csvEditorSlice";
import { useAppDispatch } from "src/stores/store";
import { DONATION_ADDRESS } from "src/utils";
Expand All @@ -36,12 +35,11 @@ export const DonateDialog = ({
onClose: () => void;
assetBalance: AssetBalance;
}) => {
const { safe } = useSafeAppsSDK();
const dispatch = useAppDispatch();
const csvContent = useCsvContent();
const darkMode = useDarkMode();

const nativeSymbol = networkInfo.get(safe.chainId)?.currencySymbol || "ETH";
const chainConfig = useCurrentChain();
const nativeSymbol = chainConfig?.currencySymbol || "ETH";

const items = assetBalance?.map((asset) => ({
id: asset.tokenAddress || "0x0",
Expand Down
14 changes: 6 additions & 8 deletions src/components/DrainSafeDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import {
TextField,
Typography,
} from "@mui/material";
import { useSafeAppsSDK } from "@safe-global/safe-apps-react-sdk";
import BigNumber from "bignumber.js";
import { utils } from "ethers";
import { useState } from "react";
import { useCurrentChain } from "src/hooks/useCurrentChain";
import { useEnsResolver } from "src/hooks/useEnsResolver";
import { networkInfo } from "src/networks";
import { AssetBalance, NFTBalance } from "src/stores/api/balanceApi";
import { AssetBalance } from "src/stores/slices/assetBalanceSlice";
import { NFTBalance } from "src/stores/slices/collectiblesSlice";
import { updateCsvContent } from "src/stores/slices/csvEditorSlice";
import { useAppDispatch } from "src/stores/store";
import { fromWei } from "src/utils";
Expand All @@ -33,18 +33,16 @@ export const DrainSafeDialog = ({
isOpen: boolean;
onClose: () => void;
assetBalance: AssetBalance;
nftBalance: NFTBalance;
nftBalance: NFTBalance["results"];
}) => {
const [drainAddress, setDrainAddress] = useState("");
const [resolvedAddress, setResolvedAddress] = useState("");

const [resolving, setResolving] = useState(false);
const { safe } = useSafeAppsSDK();

const dispatch = useAppDispatch();

const selectedNetworkInfo = networkInfo.get(safe.chainId);

const selectedNetworkInfo = useCurrentChain();
const ensResolver = useEnsResolver();

const invalidNetworkError = resolvedAddress.includes(":")
Expand Down Expand Up @@ -75,7 +73,7 @@ export const DrainSafeDialog = ({
}
});

nftBalance?.results.forEach((collectible) => {
nftBalance.forEach((collectible) => {
drainCSV += `\nnft,${collectible.address},${drainAddress},,${collectible.id}`;
});
}
Expand Down
11 changes: 5 additions & 6 deletions src/components/GenerateTransfersMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { Box, Button, Tooltip } from "@mui/material";
import { useSafeAppsSDK } from "@safe-global/safe-apps-react-sdk";
import { useState } from "react";
import { useGetAssetBalanceQuery, useGetAllNFTsQuery } from "src/stores/api/balanceApi";
import { selectAssetBalances } from "src/stores/slices/assetBalanceSlice";
import { selectCollectibles } from "src/stores/slices/collectiblesSlice";
import { useAppSelector } from "src/stores/store";

import { NETWORKS_WITH_DONATIONS_DEPLOYED } from "../networks";

import { DonateDialog } from "./DonateDialog";
import { DrainSafeDialog } from "./DrainSafeDialog";

export const GenerateTransfersMenu = () => {
const assetBalanceQuery = useGetAssetBalanceQuery();
const nftBalanceQuery = useGetAllNFTsQuery();

const assetBalance = assetBalanceQuery.currentData;
const nftBalance = nftBalanceQuery.currentData;
const assetBalance = useAppSelector(selectAssetBalances);
const nftBalance = useAppSelector(selectCollectibles);

const [isDrainModalOpen, setIsDrainModalOpen] = useState(false);
const [isDonateModalOpen, setIsDonateModalOpen] = useState(false);
Expand Down
11 changes: 6 additions & 5 deletions src/hooks/collectibleTokenInfoProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { useSafeAppsSDK } from "@safe-global/safe-apps-react-sdk";
import BigNumber from "bignumber.js";
import { ethers } from "ethers";
import { useCallback, useMemo } from "react";
import { useGetAllNFTsQuery } from "src/stores/api/balanceApi";
import { selectCollectibles } from "src/stores/slices/collectiblesSlice";
import { useAppSelector } from "src/stores/store";
import { resolveIpfsUri } from "src/utils";

import { erc1155Instance } from "../transfers/erc1155";
Expand Down Expand Up @@ -35,9 +36,9 @@ export interface CollectibleTokenInfoProvider {

export const useCollectibleTokenInfoProvider: () => CollectibleTokenInfoProvider = () => {
const { safe, sdk } = useSafeAppsSDK();
const currentNftBalance = useAppSelector(selectCollectibles);

const web3Provider = useMemo(() => new ethers.providers.Web3Provider(new SafeAppProvider(safe, sdk)), [sdk, safe]);
const nftBalanceQuery = useGetAllNFTsQuery();
const currentNftBalance = nftBalanceQuery.currentData;

const collectibleContractCache = useMemo(() => new Map<string, CollectibleTokenInfo | undefined>(), []);

Expand All @@ -49,7 +50,7 @@ export const useCollectibleTokenInfoProvider: () => CollectibleTokenInfoProvider
return contractInterfaceCache.get(tokenAddress) ?? [undefined];
}
if (currentNftBalance) {
const tokenInfo = currentNftBalance.results.find((nftEntry) => nftEntry.address === tokenAddress);
const tokenInfo = currentNftBalance.find((nftEntry) => nftEntry.address === tokenAddress);
if (tokenInfo) {
return Promise.resolve(["erc721"]);
}
Expand Down Expand Up @@ -111,7 +112,7 @@ export const useCollectibleTokenInfoProvider: () => CollectibleTokenInfoProvider
async (tokenAddress: string, id: BigNumber, token_type: "erc1155" | "erc721") => {
if (token_type === "erc721") {
if (currentNftBalance) {
const tokenInfo = currentNftBalance.results.find(
const tokenInfo = currentNftBalance.find(
(nftEntry) => nftEntry.address === tokenAddress && nftEntry.id === id.toFixed(),
);
if (tokenInfo && tokenInfo.imageUri && tokenInfo.name) {
Expand Down
18 changes: 10 additions & 8 deletions src/hooks/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { ethers, utils } from "ethers";
import xdaiTokens from "honeyswap-default-token-list";
import { useState, useEffect, useMemo } from "react";

import { networkInfo } from "../networks";
import rinkeby from "../static/rinkebyTokens.json";
import { erc20Instance } from "../transfers/erc20";
import { TokenInfo } from "../utils";

import { useCurrentChain } from "./useCurrentChain";

export type TokenMap = Map<string | null, MinimalTokenInfo>;

function tokenMap(tokenList: TokenInfo[]): TokenMap {
Expand All @@ -33,16 +34,14 @@ export const fetchTokenList = async (chainId: number): Promise<TokenMap> => {
.catch(() => []);
break;
case 4:
// Hardcoded this because the list provided at
// https://github.com/Uniswap/default-token-list/blob/master/src/tokens/rinkeby.json
// Doesn't have GNO or OWL and/or many others.
// We leave this to generate data for the unit tests.
tokens = rinkeby;
break;
case 100:
tokens = xdaiTokens.tokens;
break;
default:
console.warn(`Unimplemented token list for ${networkInfo.get(chainId)?.name} network`);
console.warn(`Unimplemented token list for chainId ${chainId}`);
tokens = [];
}
return tokenMap(tokens);
Expand All @@ -59,6 +58,7 @@ export function useTokenList(): {
const { safe } = useSafeAppsSDK();
const [tokenList, setTokenList] = useState<TokenMap>(new Map());
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
let isMounted = true;
setIsLoading(true);
Expand Down Expand Up @@ -109,6 +109,8 @@ export const useTokenInfoProvider: () => TokenInfoProvider = () => {
}, [sdk.safe]);
const { tokenList } = useTokenList();

const chainConfig = useCurrentChain();

return useMemo(
() => ({
getTokenInfo: async (tokenAddress: string) => {
Expand Down Expand Up @@ -141,9 +143,9 @@ export const useTokenInfoProvider: () => TokenInfoProvider = () => {
return undefined;
}
},
getNativeTokenSymbol: () => networkInfo.get(safe.chainId)?.currencySymbol ?? "ETH",
getSelectedNetworkShortname: () => networkInfo.get(safe.chainId)?.shortName,
getNativeTokenSymbol: () => chainConfig?.currencySymbol ?? "ETH",
getSelectedNetworkShortname: () => chainConfig?.shortName,
}),
[balances.items, safe.chainId, tokenList, web3Provider],
[balances.items, tokenList, web3Provider, chainConfig],
);
};
Loading

0 comments on commit 101cee1

Please sign in to comment.