HashLips / hashlips_nft_minting_dapp

HashLips minting dapp is a quick and easy way to connect your smart contract and start minting NFTs.
MIT License
1.03k stars 979 forks source link

[Enhancement] Add additional Web3 Providers #69

Open heisenbergpromotions opened 2 years ago

heisenbergpromotions commented 2 years ago

Background I like this Dapp and have a build running with multiple customizations, such as dynamic price displays, randomized Mint orders, NFT Gallery of owned NFTs, ability to merge two NFT's to create a brand new minted NFT etc. One thing I'm having trouble with is adding on additional Web3 providers such as this example: https://github.com/web3modal/web3modal

The way this is configured on the BlockchainActions.js to use Web3EthContract.setProvider(ethereum); let web3 = new Web3(ethereum); is difficult for me to swap. If I try using web3Modal above or WalletConnect as a provider, it seems to break everything.

Request Make this more modern with multiple provider options across HD and Mobile so it can be used in a variety of platforms and applications, such as Trust Wallet Mobile etc

franko-c commented 2 years ago

Hi, thank you for your submission. I too am curious about converting the current Connect button into a WalletConnect prompt. Where in the app would I go to start tinkering with that feature specifically, if you don't mind sharing? Thank you.

heisenbergpromotions commented 2 years ago

Hi, thank you for your submission. I too am curious about converting the current Connect button into a WalletConnect prompt. Where in the app would I go to start tinkering with that feature specifically, if you don't mind sharing? Thank you.

The main functions for the "connect" window is located in src/redux/blockchain/blockchainActions.js

If you do get it working, please do let me know as I have tried but failed, there's a lot of interconnectivity between the functions in blockchainActions.js which resulted in me breaking other things too many times.

flappys commented 2 years ago

Here you go, replace the whole file blockchainActions.js with the below code

// constants
import Web3EthContract from "web3-eth-contract";
import Web3 from "web3";
import Web3Modal from "web3modal";
import WalletConnectProvider from "@walletconnect/web3-provider";
import WalletLink from "walletlink";

// log
import { fetchData } from "../data/dataActions";

const INFURA_ID = "";

const providerOptions = {
  walletconnect: {
    package: WalletConnectProvider, // required
    options: {
      infuraId: INFURA_ID, // required
      rpc: {
        43114: "https://api.avax.network/ext/bc/C/rpc", // AVAX C-Chain
      },
    },
  },
  walletlink: {
    package: WalletLink, // Required
    options: {
      appName: "Ava Sharks", // Required
      infuraId: "", // Required unless you provide a JSON RPC url; see `rpc` below
      rpc: "https://api.avax.network/ext/bc/C/rpc", // Optional if `infuraId` is provided; otherwise it's required
      chainId: 43114, // Optional. It defaults to 1 if not provided
      appLogoUrl: null, // Optional. Application logo image URL. favicon is used if unspecified
      darkMode: false, // Optional. Use dark theme, defaults to false
    },
  },
};

const connectRequest = () => {
  return {
    type: "CONNECTION_REQUEST",
  };
};

const connectSuccess = (payload) => {
  return {
    type: "CONNECTION_SUCCESS",
    payload: payload,
  };
};

const connectFailed = (payload) => {
  return {
    type: "CONNECTION_FAILED",
    payload: payload,
  };
};

const updateAccountRequest = (payload) => {
  return {
    type: "UPDATE_ACCOUNT",
    payload: payload,
  };
};

export const connect = () => {
  return async (dispatch) => {
    dispatch(connectRequest());
    try {
      const abiResponse = await fetch("/config/abi.json", {
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
      });
      const abi = await abiResponse.json();
      const configResponse = await fetch("/config/config.json", {
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
      });
      const CONFIG = await configResponse.json();
      localStorage.removeItem("walletconnect");
      localStorage.removeItem("WALLETCONNECT_DEEPLINK_CHOICE");
      const web3Modal = new Web3Modal({
        network: "mainnet", // optional
        cacheProvider: false, // optional
        providerOptions, // required
      });
      const provider = await web3Modal.connect();
      const web3 = new Web3(provider);
      console.log("web", web3);

      Web3EthContract.setProvider(provider);
      const accounts = await web3.eth.getAccounts();
      const networkId = await provider.request({
        method: "net_version",
      });
      console.log("networkId", networkId);
      if (networkId == CONFIG.NETWORK.ID) {
        const SmartContractObj = new Web3EthContract(
          abi,
          CONFIG.CONTRACT_ADDRESS
        );
        dispatch(
          connectSuccess({
            account: accounts[0],
            smartContract: SmartContractObj,
            web3: web3,
          })
        );
        // Add listeners start
        provider.on("accountsChanged", (accounts) => {
          dispatch(updateAccount(accounts[0]));
        });
        provider.on("chainChanged", () => {
          window.location.reload();
        });
        // Add listeners end
      } else {
        dispatch(connectFailed(`Change network to ${CONFIG.NETWORK.NAME}.`));
      }
    } catch (err) {
      console.log("error", err, " message", err.message);
      if (
        typeof err !== "undefined" &&
        typeof err.message !== "undefined" &&
        err.message.includes("User Rejected")
      ) {
        dispatch(connectFailed("User rejected the request"));
      } else if (
        (typeof err === "string" || err instanceof String) &&
        err.includes("Modal closed by user")
      ) {
        dispatch(connectFailed("Modal closed by user"));
      } else {
        dispatch(connectFailed("Something went wrong."));
      }
    }
  };
};

export const updateAccount = (account) => {
  return async (dispatch) => {
    dispatch(updateAccountRequest({ account: account }));
    dispatch(fetchData(account));
  };
};
franko-c commented 2 years ago

Thank you, do we need to install any additional dependencies for this to work?

heisenbergpromotions commented 2 years ago

Here you go, replace the whole file blockchainActions.js with the below code

// constants
import Web3EthContract from "web3-eth-contract";
import Web3 from "web3";
import Web3Modal from "web3modal";
import WalletConnectProvider from "@walletconnect/web3-provider";
import WalletLink from "walletlink";

// log
import { fetchData } from "../data/dataActions";

const INFURA_ID = "";

const providerOptions = {
  walletconnect: {
    package: WalletConnectProvider, // required
    options: {
      infuraId: INFURA_ID, // required
      rpc: {
        43114: "https://api.avax.network/ext/bc/C/rpc", // AVAX C-Chain
      },
    },
  },
  walletlink: {
    package: WalletLink, // Required
    options: {
      appName: "Ava Sharks", // Required
      infuraId: "", // Required unless you provide a JSON RPC url; see `rpc` below
      rpc: "https://api.avax.network/ext/bc/C/rpc", // Optional if `infuraId` is provided; otherwise it's required
      chainId: 43114, // Optional. It defaults to 1 if not provided
      appLogoUrl: null, // Optional. Application logo image URL. favicon is used if unspecified
      darkMode: false, // Optional. Use dark theme, defaults to false
    },
  },
};

const connectRequest = () => {
  return {
    type: "CONNECTION_REQUEST",
  };
};

const connectSuccess = (payload) => {
  return {
    type: "CONNECTION_SUCCESS",
    payload: payload,
  };
};

const connectFailed = (payload) => {
  return {
    type: "CONNECTION_FAILED",
    payload: payload,
  };
};

const updateAccountRequest = (payload) => {
  return {
    type: "UPDATE_ACCOUNT",
    payload: payload,
  };
};

export const connect = () => {
  return async (dispatch) => {
    dispatch(connectRequest());
    try {
      const abiResponse = await fetch("/config/abi.json", {
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
      });
      const abi = await abiResponse.json();
      const configResponse = await fetch("/config/config.json", {
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
      });
      const CONFIG = await configResponse.json();
      localStorage.removeItem("walletconnect");
      localStorage.removeItem("WALLETCONNECT_DEEPLINK_CHOICE");
      const web3Modal = new Web3Modal({
        network: "mainnet", // optional
        cacheProvider: false, // optional
        providerOptions, // required
      });
      const provider = await web3Modal.connect();
      const web3 = new Web3(provider);
      console.log("web", web3);

      Web3EthContract.setProvider(provider);
      const accounts = await web3.eth.getAccounts();
      const networkId = await provider.request({
        method: "net_version",
      });
      console.log("networkId", networkId);
      if (networkId == CONFIG.NETWORK.ID) {
        const SmartContractObj = new Web3EthContract(
          abi,
          CONFIG.CONTRACT_ADDRESS
        );
        dispatch(
          connectSuccess({
            account: accounts[0],
            smartContract: SmartContractObj,
            web3: web3,
          })
        );
        // Add listeners start
        provider.on("accountsChanged", (accounts) => {
          dispatch(updateAccount(accounts[0]));
        });
        provider.on("chainChanged", () => {
          window.location.reload();
        });
        // Add listeners end
      } else {
        dispatch(connectFailed(`Change network to ${CONFIG.NETWORK.NAME}.`));
      }
    } catch (err) {
      console.log("error", err, " message", err.message);
      if (
        typeof err !== "undefined" &&
        typeof err.message !== "undefined" &&
        err.message.includes("User Rejected")
      ) {
        dispatch(connectFailed("User rejected the request"));
      } else if (
        (typeof err === "string" || err instanceof String) &&
        err.includes("Modal closed by user")
      ) {
        dispatch(connectFailed("Modal closed by user"));
      } else {
        dispatch(connectFailed("Something went wrong."));
      }
    }
  };
};

export const updateAccount = (account) => {
  return async (dispatch) => {
    dispatch(updateAccountRequest({ account: account }));
    dispatch(fetchData(account));
  };
};

Thank you so much, amazing addition to this Dapp! Working like a charm for me

heisenbergpromotions commented 2 years ago

Thank you, do we need to install any additional dependencies for this to work?

You need to install anything you don't already have that is in the "imports" at the start of the code, such as: use npm install walletlink

fadikc commented 2 years ago

Thank you flappys......

Much appreicated. I am having a problem with the code. I tried to use both INFURA_ID and RPC links, but could not complete the cycle. Once connected to wallet, i click on the mint button, it changes to busy and nothing happens on mobile. The code is working with no issues when using desktop. i am using rinkey network.

Your help is much appreciated.

Regards

flappys commented 2 years ago

Here is the working example. It works for both wallet connect using trust and metamask and even coinbase. https://mint.avasharks.io/

Try this and see if you face same issues.

fadikc commented 2 years ago

I cant mint because sales is not active. I am connected to rinkbey for testing and minting not working with this app on mobile.

Thanks

fadikc commented 2 years ago

.

flappys commented 2 years ago

well it is not possible to know unless i try it myself. you can post a link to your deployed website (try vercel or github links) and i can try

fadikc commented 2 years ago

Is there away to share the link in private?

Thanks

fadikc commented 2 years ago

Didnt work... do you have accept friends and DM enabled? i tried to add u to friends list and your id could not be found

fadikc commented 2 years ago

Please try this link

.

flappys commented 2 years ago

edit: try creating rpc in infura and use it. Not very sure why this is happening

fadikc commented 2 years ago

I used the links from this webstie, is it wrong?

https://rpc.info/

fadikc commented 2 years ago

Tried it but still didnt work... now it is asking to connect wallet again while with original setup it was asking to mint

oxalexa commented 2 years ago

Tried it but still didn't work... now it is asking to connect wallet again while with original setup it was asking to mint

hi - i was able to get the WC functioning both from pc and mobile using the following adjustments. Our implementations is on Polygon however. you can see it here: https://www.jorma.io

I believe the walletlink section is for Coinbase wallet if I'm not mistaken - i have not tested that.

const providerOptions = { walletconnect: { package: WalletConnectProvider, // required options: { infuraId: process.env.myINFURA_ID, rpc: { 1: "https://mainnet.infura.io/v3/" + process.env.myINFURA_ID, 42: "https://kovan.infura.io/v3/" + process.env.myINFURA_ID, 137: "https://matic-mainnet.chainstacklabs.com", 80001: "https://rpc-mumbai.matic.today", }, }, },

fadikc commented 2 years ago

Didnt work either....

fadikc commented 2 years ago

Flappys, ibexboy....

Your help is very much appreicated. I managed to get it work. As flappy said, the link on rpc.info is incorrect. In addtion, i had an issue with the project id and needed to create a new one.

I am able to mint through mobile app succefully. Only issue now is when I mint, it doesnt refresh the actual account of minted NFT's on application page. Refresh and connect again will show the actual number.

filipe-te commented 2 years ago

Hi, I have the same issue like fadikc. "nce connected to wallet, i click on the mint button, it changes to busy and nothing happens on mobile" How did you solve that problem?

fadikc commented 2 years ago

Hi, I have the same issue like fadikc. "nce connected to wallet, i click on the mint button, it changes to busy and nothing happens on mobile" How did you solve that problem?

You need to get a project_id from infura website and use it when you call your RPC link.

"https://mainnet.infura.io/v3/Put your project_id here from Infura"

Regards

filipe-te commented 2 years ago

Ok thanks mate for the quick respond! So I have to get a Project ID from Infura and put it in InfuraID: ""? or at RPC: ?

Thats my Code at the moment - Copy n Paste from heisenbergpromotions :)

walletlink: { package: WalletLink, // Required options: { appName: "Ava Sharks", // Required infuraId: "", // Required unless you provide a JSON RPC url; see rpc below rpc: "https://api.avax.network/ext/bc/C/rpc", // Optional if infuraId is provided; otherwise it's required chainId: 43114, // Optional. It defaults to 1 if not provided appLogoUrl: null, // Optional. Application logo image URL. favicon is used if unspecified darkMode: false, // Optional. Use dark theme, defaults to false }, }, };

fadikc commented 2 years ago

I used the second code provided by ibexboy

const providerOptions = { walletconnect: { package: WalletConnectProvider, // required options: { infuraId: process.env.myINFURA_ID, rpc: { 1: "https://mainnet.infura.io/v3/" + process.env.myINFURA_ID, 42: "https://kovan.infura.io/v3/" + process.env.myINFURA_ID, 137: "https://matic-mainnet.chainstacklabs.com/", 80001: "https://rpc-mumbai.matic.today/", }, }, },

filipe-te commented 2 years ago

Thank you so much! It got it going with Trust and Metamask :) Have just one Issue if I decline minting my dabb crashes with this error. Have you had a similar problem?

Unhandled Rejection (Error): User rejected the transaction (anonymous function) src/events.ts:67 trigger src/events.ts:65 (anonymous function) src/index.ts:985

fadikc commented 2 years ago

It doesnt crash for me when I reject. It just says that something went wrong when I rejected the mint. I think that the message is customizable in the dapp. My only issue so far is when minting, it doenst reflect that actual number in the dapp page counter and have to refresh and connect again to show the actual number.

filipe-te commented 2 years ago

Found another Issue xD I did just run "npm run build" and put the build on my sharehoster. Now WC wont work at all, QR Code pops up but cant connect with Trust oder Metmask. When I run "npm run start" everything runs perfect on my localhost:3000 .... Any Ideas?

fadikc commented 2 years ago

I dont have the issue, i uploaded a test version to webhosting and it is working for me. Did you download the metamask app on your mobile?

filipe-te commented 2 years ago

Yes i got trust and metamask on my mobile. Works now on localhost with npm run start perfect. On webhost the QR Code pops up, my wallet scans it (trust and metamask) and the Mobilapp says "cant connect" ... on localhost no issue :(

fadikc commented 2 years ago

I really cant tell why it wont work. Make sure that project_id used on Infura is setup to the same network used when testing the mobile app (i.e. mainnet, rinkeby,etc...)

fadikc commented 2 years ago

Does any one know how to change the unknown method message in metamask window when you want to confirm minting on mobile?

Thank You

krew-xyz commented 2 years ago

I was able to finally solve this using part of the code above and the existing HashLips code from blockchainActions.js. The below code defaults to MetaMask and then if MetaMask isn't found, tries Wallet Connect rather than just dying and not providing the user with any feedback, allowing the user to select Coinbase Wallet to mint. Confirmed to be working on both mobile and desktop, including Safari. If anyone has any other updates to this please let me know.

First you need to install the walletconnect dependencies by running yarn add @walletconnect/web3-provider in terminal. You may also need to run the following:

npm install --save web3-provider npm install --save web3modal npm install --save walletlink

Then replace the entirety of your blockchainActions.js if you've used the boilerplate code from the repo with the following, ensuring you replace My App Name with your app's name:

https://gist.github.com/Karmeleons/d15c472ce1f15558481a442c12348644

austin2080 commented 2 years ago

I got everything to work and we minted one of ours via Trust Wallet but it's not showing up on opensea collection page. We can view the NFT in our wallet and under the activity page on opensea.

Does anyone know why or how I can fix this?

Thank you,

krew-xyz commented 2 years ago

Ah, looks like the issue is that the code the person above provided uses avax chain—that's probably why. Not sure how to change to ethereum so I have to look into this. Anyone else know?

austin2080 commented 2 years ago

I figured out how to change it to Ethereum. All you have to do is change the RPC URL which I did below. We got it to mint and show up on Opensea.

`// constants import Web3EthContract from "web3-eth-contract"; import Web3 from "web3"; import Web3Modal from "web3modal"; import WalletConnectProvider from "@walletconnect/web3-provider"; import WalletLink from "walletlink";

// log import { fetchData } from "../data/dataActions";

const INFURA_ID = "";

const providerOptions = { walletconnect: { package: WalletConnectProvider, // required options: { infuraId: INFURA_ID, // required rpc: { 1: "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161", // Mainnet }, }, }, walletlink: { package: WalletLink, // Required options: { appName: "Bad", // Required infuraId: "", // Required unless you provide a JSON RPC url; see rpc below rpc: "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161", // Optional if infuraId is provided; otherwise it's required chainId: 1, // Optional. It defaults to 1 if not provided appLogoUrl: null, // Optional. Application logo image URL. favicon is used if unspecified darkMode: false, // Optional. Use dark theme, defaults to false }, }, };

const connectRequest = () => { return { type: "CONNECTION_REQUEST", }; };

const connectSuccess = (payload) => { return { type: "CONNECTION_SUCCESS", payload: payload, }; };

const connectFailed = (payload) => { return { type: "CONNECTION_FAILED", payload: payload, }; };

const updateAccountRequest = (payload) => { return { type: "UPDATE_ACCOUNT", payload: payload, }; };

export const connect = () => { return async (dispatch) => { dispatch(connectRequest()); try { const abiResponse = await fetch("/config/abi.json", { headers: { "Content-Type": "application/json", Accept: "application/json", }, }); const abi = await abiResponse.json(); const configResponse = await fetch("/config/config.json", { headers: { "Content-Type": "application/json", Accept: "application/json", }, }); const CONFIG = await configResponse.json(); localStorage.removeItem("walletconnect"); localStorage.removeItem("WALLETCONNECT_DEEPLINK_CHOICE"); const web3Modal = new Web3Modal({ network: "mainnet", // optional cacheProvider: false, // optional providerOptions, // required }); const provider = await web3Modal.connect(); const web3 = new Web3(provider); console.log("web", web3);

  Web3EthContract.setProvider(provider);
  const accounts = await web3.eth.getAccounts();
  const networkId = await provider.request({
    method: "net_version",
  });
  console.log("networkId", networkId);
  if (networkId == CONFIG.NETWORK.ID) {
    const SmartContractObj = new Web3EthContract(
      abi,
      CONFIG.CONTRACT_ADDRESS
    );
    dispatch(
      connectSuccess({
        account: accounts[0],
        smartContract: SmartContractObj,
        web3: web3,
      })
    );
    // Add listeners start
    provider.on("accountsChanged", (accounts) => {
      dispatch(updateAccount(accounts[0]));
    });
    provider.on("chainChanged", () => {
      window.location.reload();
    });
    // Add listeners end
  } else {
    dispatch(connectFailed(`Change network to ${CONFIG.NETWORK.NAME}.`));
  }
} catch (err) {
  console.log("error", err, " message", err.message);
  if (
    typeof err !== "undefined" &&
    typeof err.message !== "undefined" &&
    err.message.includes("User Rejected")
  ) {
    dispatch(connectFailed("User rejected the request"));
  } else if (
    (typeof err === "string" || err instanceof String) &&
    err.includes("Modal closed by user")
  ) {
    dispatch(connectFailed("Modal closed by user"));
  } else {
    dispatch(connectFailed("Something went wrong."));
  }
}

}; };

export const updateAccount = (account) => { return async (dispatch) => { dispatch(updateAccountRequest({ account: account })); dispatch(fetchData(account)); }; };

`

krew-xyz commented 2 years ago

Nice work! How did you find the correct rpc URL?

kutwa commented 2 years ago

Background I like this Dapp and have a build running with multiple customizations, such as dynamic price displays, randomized Mint orders, NFT Gallery of owned NFTs, ability to merge two NFT's to create a brand new minted NFT etc. One thing I'm having trouble with is adding on additional Web3 providers such as this example: https://github.com/web3modal/web3modal

The way this is configured on the BlockchainActions.js to use Web3EthContract.setProvider(ethereum); let web3 = new Web3(ethereum); is difficult for me to swap. If I try using web3Modal above or WalletConnect as a provider, it seems to break everything.

Request Make this more modern with multiple provider options across HD and Mobile so it can be used in a variety of platforms and applications, such as Trust Wallet Mobile etc

Hi, can you help me to add "NFT Gallery" to my dapp?

austin2080 commented 2 years ago

@Karmeleons You can find it in your MetaMask for whatever network you are looking for.

krew-xyz commented 2 years ago

@austin2080 Ok, this seems to be working now but in iOS Coinbase wallet I'm getting a red 'Network Fee' and a message saying 'unable to determine fee, please try again later'. Did you only update the two rpc lines above?

austin2080 commented 2 years ago

@Karmeleons That's a good question. Did you change the "Chain Id to 1 as well?

krew-xyz commented 2 years ago

@austin2080 I did. Did you actually successfully mint on mobile via Coinbase Wallet? I'm using a test wallet with no funds but it should still estimate fees. IMG_8791

austin2080 commented 2 years ago

@Karmeleons No, we tried it on Trust Wallet because that's what we needed and it worked but I'll look into the coinbase error since someone might want to use that.

krew-xyz commented 2 years ago

@austin2080 You'd be surprised at how many people are using coinbase wallet/don't have an alternative so let me know what you find out. It's actually the main reason why I'm trying to figure this out :)

austin2080 commented 2 years ago

@Karmeleons That's good to know because I never would have thought of that. We are really trying to push towards the Cronos network so that's next on my list too lol

krew-xyz commented 2 years ago

@austin2080 Update: This does actually work fine. I was using a test Coinbase Wallet with no funds and if it's empty you will get this message. This works: https://gist.github.com/Karmeleons/d15c472ce1f15558481a442c12348644

austin2080 commented 2 years ago

@Karmeleons That makes a lot of sense lol...we actually just got ours to work a couple of hours ago.

austin2080 commented 2 years ago

@Karmeleons Do you know how to change the price on the front end dapp to CRO? The current dapp is in Wei and I don't believe that's how cronos works but I could be wrong.

PixelTigerClub commented 2 years ago

@Karmeleons Do you know how to change the price on the front end dapp to CRO? The current dapp is in Wei and I don't believe that's how cronos works but I could be wrong.

Do you know how to get crypto defi wallet integrated into the dapp I can only get metamask to mint my nfts but I need to have crypto defi wallet to be able to as well

PixelTigerClub commented 2 years ago

deficonnect

Installation

yarn add "deficonnect"

Usage

import { DeFiWeb3Connector } from "deficonnect"; import Web3 from "web3";

const connector = new DeFiWeb3Connector({ supportedChainIds: [1], rpc: { 1: "https://mainnet.infura.io/v3/INFURA_API_KEY", 25: "https://evm.cronos.org/", // cronos mainet }, pollingInterval: 15000, }); connector.activate(); const provider = await connector.getProvider(); const web3 = new Web3(provider); #

krew-xyz commented 2 years ago

@PixelTigerClub The hashlips repo is made to work with ETH or Polygon, not sure if it's chain-agnostic.