Swap Box and Token modal stopped rendering on the browser

48 views Asked by At

Originally, it was a swap built on Ethereum fetching token quotes & prices via api. That version functions as intended

Now, I am working implementing conditional logic to fect the token list and data for the network that is connected to the user wallet

I did it in 2 steps:

1- Conditional logic to fetch the api string based on the connected network


import qs from "qs";
import { NextApiRequest, NextApiResponse } from "next";
import { useAccount } from "wagmi";
import { mainnet, bsc, polygon } from "../../node_modules/@wagmi/chains/dist/index";


function apiUrl(isConnected: boolean, mainnet: boolean, bsc: boolean, polygon: boolean, query: string) {
  if (isConnected) {
    if (mainnet) {
      return `https://my.api.com//swap/v1/price?${query}`;
    } else if (bsc) {
      return `https://bsc.my.api.com//swap/v1/price?${query}`;
    } else if (polygon) {
      return `https://my.api.com//swap/v1/price?${query}`;
    } else {
      return `https://my.api.com//swap/v1/price?${query}`;
    }
  } else {
    return `https://my.api.com//swap/v1/price?${query}`;
  }
}


export default async function handler(
req: NextApiRequest,
 res: NextApiResponse<any>
) {
  const { isConnected } = useAccount();
  const query = qs.stringify(req.query);

  const apiKey = process.env.SWAP_API_KEY;

  const response = await fetch(apiUrl, {
    headers: {
      "0x-api-key": "MY-API-KEY",
    }
  });


  try {
    const data = await response.json();
    res.json(data);
  } catch (error) {
    console.error(error);
    res.status(500).json({ error });
  }
}

2- Conditional logic to fetch the token list based on the network connected to the wallet:


import { Address } from "viem";
import { useAccount } from "wagmi";
import { mainnet, bsc, polygon } from "viem/chains";

export const MAX_ALLOWANCE =
  BigInt(
    115792089237316195423570985008687907853269984665640564039457584007913129639935n
  );

export const exchangeProxy = "PROXY";

// type Token = {
//   address: Address;
// };

interface Token {
  name: string;
  address: Address;
  symbol: string;
  decimals: number;
  chainId: number;
  logoURI: string;
}

// Export default array of tokens
export default function ETHEREUM_TOKENS(): Token[] {
  const { isConnected } = useAccount();

  return isConnected
    ? mainnet
      ? ETH_TOKENS
      : bsc
      ? BSC_TOKENS
      : polygon
      ? POLYGON_TOKENS
      : ETH_TOKENS
    : [];
}

// Export tokens by symbol
export function ETHEREUM_TOKENS_BY_SYMBOL(): Record<string, Token> {
  const { isConnected } = useAccount();

  return isConnected
    ? mainnet
      ? ETH_TOKENS_BY_SYMBOL
      : bsc
      ? BSC_TOKENS_BY_SYMBOL
      : polygon
      ? POLYGON_TOKENS_BY_SYMBOL
      : ETH_TOKENS_BY_SYMBOL
    : {};
}

// Export tokens by addres
export function ETHEREUM_TOKENS_BY_ADDRESS(): Record<string, Token> {
  const { isConnected } = useAccount();

  return isConnected
    ? mainnet
      ? ETH_TOKENS_BY_SYMBOL
      : bsc
      ? BSC_TOKENS_BY_SYMBOL
      : polygon
      ? POLYGON_TOKENS_BY_SYMBOL
      : ETH_TOKENS_BY_SYMBOL
    : {};
}

============================================================

PROBLEM:

  • After making the adjustements mentionned previously, my main component stopped rendering the swap box, the modal and the token lists regardless of what network is connected or not

  • It is not returning any errors in the browser console or the terminal

Note: It is my 1st time working entirely with typescript

============================================================

ENVIRONMENT:

  • React js (ts: "5.0.4")
  • Wagmi: "1.4.5"
  • Viem: "^0.3.50"
  • WalletConnect SDK v2
  • ethers: "^6.4.0"
  • web3Modal
  • VsCode

============================================================

CODE BASE:

*_app.js: *

  • Basic configuration
import "./../styles/globals.css";

import type { AppProps } from "next/app";
import { useEffect, useState, useRef } from "react";
import {
  EthereumClient,
  w3mConnectors,
  w3mProvider,
} from "@web3modal/ethereum";
import { Web3Modal } from "@web3modal/react";
import { configureChains, createConfig, WagmiConfig } from "wagmi";

import SelectToken from "./components/SelectToken";

import {
  mainnet,
  bsc,
  polygon,
} from "wagmi/chains";
require("dotenv").config();

//walletconnect
const projectId = "My_project_id";

const chains = [
  mainnet,
  bsc,
  polygon,
];

export const publicClient: any = configureChains(chains, [
  w3mProvider({ projectId }),
]);
const wagmiConfig = createConfig({
  autoConnect: true,
  connectors: w3mConnectors({ projectId, chains }),
  publicClient,
});
const ethereumClient = new EthereumClient(wagmiConfig, chains);

export default function App({ Component, pageProps }: AppProps) {
  const [mounted, setMounted] = useState(false);
  useEffect(() => setMounted(true), []);

  return (
    <div className="flex-row">
      <WagmiConfig config={wagmiConfig}>
        {mounted && <Component {...pageProps} />}
      </WagmiConfig>
      <Web3Modal projectId={projectId} ethereumClient={ethereumClient} />
    </div>
  );
}

index.tsx

Main component:

  • Renders the swap box & token Modal
  • Implement all the functionality for the swap including conditional logic to set api and token list based on connected network
  • I highlighted in bold the code that is directly interacting with the components containing the conditional logic to set api and token list
//Imports
....

//Interface

interface PriceRequestParams {
  sellToken: string;
  buyToken: string;
  buyAmount?: string;
  sellAmount?: string;
  takerAddress?: string;
}

//MORE CODE
.......

//FUNCTION SETTING TOKEN

export default function PriceView({
  price,
  setPrice,
  setFinalize,
  takerAddress,
}: {
  price: any;
  setPrice: (price: any) => void;
  setFinalize: (finalize: boolean) => void;
  takerAddress: Address | undefined;
}) {
  // fetch price here
  const [sellAmount, setSellAmount] = useState("");
  const [buyAmount, setBuyAmount] = useState("");
  const [tradeDirection, setTradeDirection] = useState("sell");
  **const [sellToken, setSellToken] = useState("matic");**
  **const [buyToken, setBuyToken] = useState("weth");**

  //modal state
  const [isOpen, setIsOpen] = useState(false);
  const [changeToken, setChangeToken] = useState(1);
  const [asset, setAsset] = useState("");


  //TOKEN LISTS
  **const tokens = ETHEREUM_TOKENS()**;
  **const tokensBySymbol = ETHEREUM_TOKENS_BY_SYMBOL()**;

  **const sellTokenDecimals = tokensBySymbol[sellToken].decimals**;

  console.log(sellAmount, sellTokenDecimals, "<-");
  const parsedSellAmount =
    sellAmount && tradeDirection === "sell"
      ? parseUnits(sellAmount, sellTokenDecimals).toString()
      : undefined;

  **const buyTokenDecimals = tokensBySymbol[buyToken].decimals**;

  const parsedBuyAmount =
    buyAmount && tradeDirection === "buy"
      ? parseUnits(buyAmount, buyTokenDecimals).toString()
      : undefined;

  const { isLoading: isLoadingPrice } = useSWR(
    [
      "/api/price",
      {
        **sellToken: tokensBySymbol[sellToken].address**,
        **buyToken: tokensBySymbol[buyToken].address**,
        sellAmount: parsedSellAmount,
        buyAmount: parsedBuyAmount,
        takerAddress,
        feeRecipient: FEE_RECIPIENT,
        buyTokenPercentageFee: AFFILIATE_FEE,
      },
    ],
    fetcher,
    {
      onSuccess: (data) => {
        setPrice(data);
        if (tradeDirection === "sell") {
          console.log(formatUnits(data.buyAmount, buyTokenDecimals), data);
          setBuyAmount(formatUnits(data.buyAmount, buyTokenDecimals));
        } else {
          setSellAmount(formatUnits(data.sellAmount, sellTokenDecimals));
        }
      },
    }
  );

//MORE CODE
.........


**  type Token = {
    name: string;
    address: Address;
    symbol: string;
    decimals: number;
    chainId: number;
    logoURI: string;
  };**
  
  // tokenChoice component
  const TokenChoice = ({
   ** token: Token,**
    i,
    changeToken,
  }: {
    **token: Token;**
    i: number;
    changeToken: number;
  }) => {
    return (
      <div
        className={Style.tokenChoice}
        key={i}
        onClick={() => modifyToken(i, changeToken)}
      >
        <img
          **src={Token.logoURI}**
          **alt={Token.logoURI}**
          className={Style.tokenLogo}
        />
        <div className="tokenChoiceNames">
          **<div className={Style.tokenName}>{Token.name}</div>**
          **<div className={Style.tokenTicker}>{Token.symbol}</div>**
        </div>
      </div>
    );
  };

//MORE CODE
...........


  return (
    <>
      {/* MODAL */}
      <Modal
        open={isOpen}
        footer={null}
        onCancel={() => setIsOpen(false)}
        title="Select a token" //Modify: "select a ${network.name} token"
      >
        <div className={Style.modalContent}>
          <input
            type="text"
            value={asset}
            onChange={(e) => setAsset(e.target.value)}
            placeholder="Search Token"
            className={Style.Modal_input}
          />
          <div className={Style.tokenChoiceWrapper}>
            <ul>
              {tokens
                ?.filter((token) => {
                  return (
                    token.symbol.includes(asset) ||
                    token.name.includes(asset) ||
                    token.address.includes(asset)
                  );
                })
                .map((token, i) => {
                  return (
                    <TokenChoice
                      key={i}
                      token={token}
                      i={i}
                      changeToken={changeToken}
                    />
                  );
                })}
            </ul>
          </div>
        </div>
      </Modal>

//MORE CODE
...........

            {/* Token selection */}
            <button
              className={Style.HeroSection_box_input_button}
              onClick={() => {
                openModal("sellToken");
              }}
            >
              {/* image */}
              <div className={Style.HeroSection_box_input_image_container}>
                <img
                  alt={sellToken}
                  className={Style.HeroSection_box_input_image}
                  **src={tokensBySymbol[sellToken].logoURI}**
                />
              </div>
              <span className={Style.HeroSection_box_input_select}>
                <option value={sellToken}>
                 ** {tokensBySymbol[sellToken].symbol}**
                </option>
              </span>
            </button>
          </section>

//MORE CODE
.........

============================================================

CONCLUSION:

Any help figuring out the issue would be much appreciated as I have not found any possible cause or solution online nor chatting with AI. Thank you!

*I attempted the following: *

1- Dozens of different syntax for setting the apis based on connected network - only the solution presented previously didn't return errors or type errors 2- Fetching the token list based on the api being used - failed 3- Fetching the token list based on the network that is connected - the solution presented above is the only one that did not return errors after a dozen of intents

I was hoping that setting both api and fetching token list based on the network conncted would allow to fetch and display the desired tokenlists both in the swap and modal but instead the browser is only returning the background gradient

0

There are 0 answers