diff --git a/packages/react-app/package.json b/packages/react-app/package.json index 67cb80f4..484f1029 100644 --- a/packages/react-app/package.json +++ b/packages/react-app/package.json @@ -18,6 +18,9 @@ "@ant-design/icons": "^4.2.2", "@apollo/react-hooks": "^4.0.0", "@craco/craco": "^6.3.0", + "@gnosis.pm/safe-apps-react-sdk": "^4.0.8", + "@gnosis.pm/safe-apps-sdk": "^6.1.1", + "@gnosis.pm/safe-apps-web3modal": "^6.0.1", "@portis/web3": "^4.0.5", "@ramp-network/ramp-instant-sdk": "^2.2.0", "@testing-library/jest-dom": "^5.11.4", diff --git a/packages/react-app/public/CORS b/packages/react-app/public/CORS new file mode 100644 index 00000000..f59ec20a --- /dev/null +++ b/packages/react-app/public/CORS @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/packages/react-app/public/manifest.json b/packages/react-app/public/manifest.json index 6bfcddb8..b0d03c03 100644 --- a/packages/react-app/public/manifest.json +++ b/packages/react-app/public/manifest.json @@ -1,7 +1,8 @@ { - "short_name": "🏗 Scaffold-Eth App", + "short_name": "Tip party", "start_url": ".", - "name": "🏗 Scaffold-Eth App", + "name": "Tip party", + "iconPath": "favicon.ico", "icons": [ { "src": "favicon.ico", diff --git a/packages/react-app/src/App.jsx b/packages/react-app/src/App.jsx index 948e165f..ea309af8 100644 --- a/packages/react-app/src/App.jsx +++ b/packages/react-app/src/App.jsx @@ -5,21 +5,17 @@ import { Alert, Button, Menu, Select, Space, Switch as AntdSwitch } from "antd"; import "antd/dist/antd.css"; import React, { useCallback, useEffect, useState } from "react"; import { BrowserRouter, Link, Route, Switch } from "react-router-dom"; -import Web3Modal from "web3modal"; import "./App.css"; import { Account, Contract } from "./components"; -import { INFURA_ID, NETWORK, NETWORKS } from "./constants"; -import { Transactor, Address as AddressHelper } from "./helpers"; +import { NETWORK, NETWORKS } from "./constants"; +import { Transactor, Address as AddressHelper, Web3ModalSetup } from "./helpers"; import { useBalance, useContractLoader, useExchangePrice, useGasPrice, useOnBlock, useUserSigner } from "./hooks"; import { Rooms, Home } from "./views"; // Wallets for wallet connect -import Portis from "@portis/web3"; -import Fortmatic from "fortmatic"; -import Authereum from "authereum"; const { ethers } = require("ethers"); // 😬 Sorry for all the console logging -const DEBUG = true; +const DEBUG = false; const NETWORKCHECK = true; // Add more networks as the dapp expands to more networks @@ -65,85 +61,11 @@ const localProvider = new ethers.providers.StaticJsonRpcProvider(localProviderUr // 🔭 block explorer URL const blockExplorer = targetNetwork.blockExplorer; -// Coinbase walletLink init -const walletLink = new WalletLink({ - appName: "coinbase", -}); - -// WalletLink provider -const walletLinkProvider = walletLink.makeWeb3Provider( - "https://eth-mainnet.alchemyapi.io/v2/qCdzfF9UqXcJYIle-Ff-BN0MII8LjLQs", - 1, -); - // Portis ID: 6255fb2b-58c8-433b-a2c9-62098c05ddc9 /* Web3 modal helps us "connect" external wallets: */ -const web3Modal = new Web3Modal({ - network: "mainnet", // Optional. If using WalletConnect on xDai, change network to "xdai" and add RPC info below for xDai chain. - cacheProvider: true, // optional - theme: "light", // optional. Change to "dark" for a dark theme. - providerOptions: { - walletconnect: { - package: WalletConnectProvider, // required - options: { - bridge: "https://polygon.bridge.walletconnect.org", - infuraId: INFURA_ID, - rpc: { - 1: "https://eth-mainnet.alchemyapi.io/v2/qCdzfF9UqXcJYIle-Ff-BN0MII8LjLQs", // mainnet // For more WalletConnect providers: https://docs.walletconnect.org/quick-start/dapps/web3-provider#required - 42: `https://kovan.infura.io/v3/${INFURA_ID}`, - 100: "https://dai.poa.network", // xDai - }, - }, - }, - portis: { - display: { - logo: "https://user-images.githubusercontent.com/9419140/128913641-d025bc0c-e059-42de-a57b-422f196867ce.png", - name: "Portis", - description: "Connect to Portis App", - }, - package: Portis, - options: { - id: "6255fb2b-58c8-433b-a2c9-62098c05ddc9", - }, - }, - fortmatic: { - package: Fortmatic, // required - options: { - key: "pk_live_5A7C91B2FC585A17", // required - }, - }, - // torus: { - // package: Torus, - // options: { - // networkParams: { - // host: "https://localhost:8545", // optional - // chainId: 1337, // optional - // networkId: 1337 // optional - // }, - // config: { - // buildEnv: "development" // optional - // }, - // }, - // }, - "custom-walletlink": { - display: { - logo: "https://play-lh.googleusercontent.com/PjoJoG27miSglVBXoXrxBSLveV6e3EeBPpNY55aiUUBM9Q1RCETKCOqdOkX2ZydqVf0", - name: "Coinbase", - description: "Connect to Coinbase Wallet (not Coinbase App)", - }, - package: walletLinkProvider, - connector: async (provider, options) => { - await provider.enable(); - return provider; - }, - }, - authereum: { - package: Authereum, // required - }, - }, -}); +const web3Modal = Web3ModalSetup(targetNetwork); function App(props) { const mainnetProvider = @@ -412,7 +334,7 @@ function App(props) { ); const loadWeb3Modal = useCallback(async () => { - const provider = await web3Modal.connect(); + const provider = await web3Modal.requestProvider(); setInjectedProvider(new ethers.providers.Web3Provider(provider)); provider.on("chainChanged", chainId => { @@ -436,6 +358,7 @@ function App(props) { if (web3Modal.cachedProvider) { loadWeb3Modal(); } + console.log("Checking URLS: ", injectedProvider); }, [loadWeb3Modal]); const [route, setRoute] = useState(); diff --git a/packages/react-app/src/components/Account/index.jsx b/packages/react-app/src/components/Account/index.jsx index 935c954f..aaaee3f6 100644 --- a/packages/react-app/src/components/Account/index.jsx +++ b/packages/react-app/src/components/Account/index.jsx @@ -74,7 +74,7 @@ export default function Account({ const modalButtons = []; if (web3Modal) { - if (web3Modal.cachedProvider) { + if (web3Modal?.cachedProvider || web3Modal?.provider?.safe) { modalButtons.push(
{isValidAddress(address) ? ( diff --git a/packages/react-app/src/components/PayButton.jsx b/packages/react-app/src/components/PayButton.jsx index 62ca339e..66e8cefd 100644 --- a/packages/react-app/src/components/PayButton.jsx +++ b/packages/react-app/src/components/PayButton.jsx @@ -88,8 +88,10 @@ export default function PayButton({ const payParams = { token, ...tokenInfo[token] }; if (isETH()) { setStatus(4); + console.log("pay happening ", status); await ethPayHandler(); setStatus(3); + console.log("end of pay happening ", status); } else { if (status === 1) { await approveTokenAllowance(); @@ -103,7 +105,9 @@ export default function PayButton({ useEffect(() => { if (isETH()) { + console.log("refresh happening ", status); refreshETH(); + console.log("after refresh happening ", status); } else if (tokenInfo[token]) { const adjustedAmount = ethers.utils.parseUnits(amount || "0", tokenInfo[token].decimals); const hasEnoughAllowance = tokenInfo[token].allowance.lt(adjustedAmount); @@ -113,6 +117,7 @@ export default function PayButton({ }, [amount]); useEffect(() => { + console.log("check it out ", status, renderButtonText()); if (!isETH()) { setStatus(0); refreshTokenDetails(); @@ -123,7 +128,6 @@ export default function PayButton({ const renderButtonText = () => { let text = "Loading..."; - switch (status) { case 1: text = `Approve ${appName} to transfer ${token}`; @@ -144,7 +148,6 @@ export default function PayButton({ text = "Loading..."; break; } - return text; }; diff --git a/packages/react-app/src/constants.js b/packages/react-app/src/constants.js index 9ed671a9..18ec5e4f 100644 --- a/packages/react-app/src/constants.js +++ b/packages/react-app/src/constants.js @@ -263,7 +263,7 @@ export const NETWORKS = { name: "rinkeby", color: "#e0d068", chainId: 4, - rpcUrl: `https://rinkeby.infura.io/v3/${INFURA_ID}`, + rpcUrl: "https://eth-rinkeby.alchemyapi.io/v2/0meerwuMivq7wbFsUFTcirfUVM79w1fW", faucet: "https://faucet.rinkeby.io/", blockExplorer: "https://rinkeby.etherscan.io/", nativeCurrency: "ETH", diff --git a/packages/react-app/src/helpers/Transactor.js b/packages/react-app/src/helpers/Transactor.js index 2b1dca0b..f7c441a0 100644 --- a/packages/react-app/src/helpers/Transactor.js +++ b/packages/react-app/src/helpers/Transactor.js @@ -1,6 +1,7 @@ import { notification } from "antd"; import Notify from "bnc-notify"; import { BLOCKNATIVE_DAPPID } from "../constants"; +import { useSafeAppsSDK } from "@gnosis.pm/safe-apps-react-sdk"; const { ethers } = require("ethers"); @@ -12,9 +13,14 @@ const callbacks = {}; const DEBUG = true; export default function Transactor(providerOrSigner, gasPrice, etherscan) { + const { sdk, safe } = useSafeAppsSDK(); + if (typeof providerOrSigner !== "undefined") { // eslint-disable-next-line consistent-return return async (tx, callback) => { + console.log("Transactor tx ", tx); + console.log("Transactor callback ", callback); + console.log("providerOrSigner callback ", providerOrSigner); let signer; let network; let provider; @@ -29,26 +35,23 @@ export default function Transactor(providerOrSigner, gasPrice, etherscan) { } console.log("network", network); - var options = null; var notify = null; - if (navigator.onLine) { - options = { - dappId: BLOCKNATIVE_DAPPID, // GET YOUR OWN KEY AT https://account.blocknative.com - system: "ethereum", - networkId: network.chainId, - // darkMode: Boolean, // (default: false) - transactionHandler: txInformation => { - if (DEBUG) console.log("HANDLE TX", txInformation); - const possibleFunction = callbacks[txInformation.transaction.hash]; - if (typeof possibleFunction === "function") { - possibleFunction(txInformation.transaction); - } - }, - }; + options = { + dappId: BLOCKNATIVE_DAPPID, // GET YOUR OWN KEY AT https://account.blocknative.com + system: "ethereum", + networkId: network.chainId, + // darkMode: Boolean, // (default: false) + transactionHandler: txInformation => { + if (DEBUG) console.log("HANDLE TX", txInformation); + const possibleFunction = callbacks[txInformation.transaction.hash]; + if (typeof possibleFunction === "function") { + possibleFunction(txInformation.transaction); + } + }, + }; - notify = Notify(options); - } + notify = Notify(options); let etherscanNetwork = ""; if (network.name && network.chainId > 1) { @@ -62,26 +65,48 @@ export default function Transactor(providerOrSigner, gasPrice, etherscan) { try { let result; - if (tx instanceof Promise) { - if (DEBUG) console.log("AWAITING TX", tx); + if (providerOrSigner?.provider?.provider?.wc?._peerMeta?.name === "Gnosis Safe Multisig") { + const accountData = providerOrSigner?.provider?.wc?._peerMeta?.accounts[0]; + console.log("GNOSIS Safe TX", tx, callback); result = await tx; + console.log("result", result); + // Returns a hash to identify the Safe transaction + const safeTxHash = await sdk.txs.send({ + txs: [ + { + to: accountData, + value: "0x0", + data: result, + }, + ], + }); + console.log("safeTxHash ", safeTxHash); + const safeTx = await sdk.txs.getBySafeTxHash(safeTxHash); + console.log("safeTx ", safeTx); } else { - if (!tx.gasPrice) { - tx.gasPrice = gasPrice || ethers.utils.parseUnits("4.1", "gwei"); - } - if (!tx.gasLimit) { - tx.gasLimit = ethers.utils.hexlify(120000); + if (tx instanceof Promise) { + if (DEBUG) console.log("AWAITING TX", tx); + result = await tx; + console.log("callback result ", result); + } else { + if (!tx.gasPrice) { + tx.gasPrice = gasPrice || ethers.utils.parseUnits("4.1", "gwei"); + } + if (!tx.gasLimit) { + tx.gasLimit = ethers.utils.hexlify(120000); + } + if (DEBUG) console.log("RUNNING TX", tx); + result = await signer.sendTransaction(tx); } - if (DEBUG) console.log("RUNNING TX", tx); - result = await signer.sendTransaction(tx); } + if (DEBUG) console.log("RESULT:", result); // console.log("Notify", notify); if (callback) { callbacks[result.hash] = callback; } - + console.log("callback result view callbacks ", callbacks); // if it is a valid Notify.js network, use that, if not, just send a default notification if (notify && [1, 3, 4, 5, 42, 100].indexOf(network.chainId) >= 0) { const { emitter } = notify.hash(result.hash); @@ -117,6 +142,7 @@ export default function Transactor(providerOrSigner, gasPrice, etherscan) { return result; } catch (e) { + console.log("main error ", e); if (DEBUG) console.log(e); // Accounts for Metamask and default signer on all networks let message = @@ -127,7 +153,6 @@ export default function Transactor(providerOrSigner, gasPrice, etherscan) { : e.data ? e.data : JSON.stringify(e); - if (!e.error && e.message) { message = e.message; } diff --git a/packages/react-app/src/helpers/Web3ModalSetup.js b/packages/react-app/src/helpers/Web3ModalSetup.js new file mode 100644 index 00000000..6064bc0a --- /dev/null +++ b/packages/react-app/src/helpers/Web3ModalSetup.js @@ -0,0 +1,86 @@ +import Fortmatic from "fortmatic"; +import WalletLink from "walletlink"; +import { SafeAppWeb3Modal } from "@gnosis.pm/safe-apps-web3modal"; +import Portis from "@portis/web3"; +import WalletConnectProvider from "@walletconnect/web3-provider"; +import { INFURA_ID } from "../constants"; +import Authereum from "authereum"; +//import Torus from "@toruslabs/torus-embed" + +// Coinbase walletLink init +const walletLink = new WalletLink({ + appName: "coinbase", +}); + +// WalletLink provider +const walletLinkProvider = walletLink.makeWeb3Provider( + "https://eth-mainnet.alchemyapi.io/v2/qCdzfF9UqXcJYIle-Ff-BN0MII8LjLQs", + 1, +); + +const web3ModalSetup = targetNetwork => + new SafeAppWeb3Modal({ + network: "mainnet", // Optional. If using WalletConnect on xDai, change network to "xdai" and add RPC info below for xDai chain. + cacheProvider: true, // optional + providerOptions: { + walletconnect: { + network: targetNetwork, + package: WalletConnectProvider, // required + options: { + rpc: { + 1: "https://eth-mainnet.alchemyapi.io/v2/qCdzfF9UqXcJYIle-Ff-BN0MII8LjLQs", // mainnet // For more WalletConnect providers: https://docs.walletconnect.org/quick-start/dapps/web3-provider#required + 42: `https://kovan.infura.io/v3/${INFURA_ID}`, + 100: "https://dai.poa.network", // xDai + 4: "https://eth-rinkeby.alchemyapi.io/v2/0meerwuMivq7wbFsUFTcirfUVM79w1fW", + }, + }, + }, + portis: { + display: { + logo: "https://user-images.githubusercontent.com/9419140/128913641-d025bc0c-e059-42de-a57b-422f196867ce.png", + name: "Portis", + description: "Connect to Portis App", + }, + package: Portis, + options: { + id: "6255fb2b-58c8-433b-a2c9-62098c05ddc9", + }, + }, + fortmatic: { + package: Fortmatic, // required + options: { + key: "pk_live_5A7C91B2FC585A17", // required + }, + }, + // torus: { + // package: Torus, + // options: { + // networkParams: { + // host: "https://localhost:8545", // optional + // chainId: 1337, // optional + // networkId: 1337 // optional + // }, + // config: { + // buildEnv: "development" // optional + // }, + // }, + // }, + "custom-walletlink": { + display: { + logo: "https://play-lh.googleusercontent.com/PjoJoG27miSglVBXoXrxBSLveV6e3EeBPpNY55aiUUBM9Q1RCETKCOqdOkX2ZydqVf0", + name: "Coinbase", + description: "Connect to Coinbase Wallet (not Coinbase App)", + }, + package: walletLinkProvider, + connector: async (provider, options) => { + await provider.enable(); + return provider; + }, + }, + authereum: { + package: Authereum, // required + }, + }, + }); + +export default web3ModalSetup; diff --git a/packages/react-app/src/helpers/index.js b/packages/react-app/src/helpers/index.js index 9871b50b..486fe18e 100644 --- a/packages/react-app/src/helpers/index.js +++ b/packages/react-app/src/helpers/index.js @@ -1,2 +1,3 @@ export { default as Transactor } from "./Transactor"; export { default as Address } from "./Address"; +export { default as Web3ModalSetup } from "./Web3ModalSetup"; diff --git a/packages/react-app/src/index.jsx b/packages/react-app/src/index.jsx index 10e56111..71d462a4 100644 --- a/packages/react-app/src/index.jsx +++ b/packages/react-app/src/index.jsx @@ -2,6 +2,7 @@ import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client"; import React from "react"; import { ThemeSwitcherProvider } from "react-css-theme-switcher"; import ReactDOM from "react-dom"; +import SafeProvider from "@gnosis.pm/safe-apps-react-sdk"; import App from "./App"; import "./index.css"; @@ -22,7 +23,9 @@ const client = new ApolloClient({ ReactDOM.render( - + + + , document.getElementById("root"), diff --git a/packages/react-app/src/views/HostRoom.jsx b/packages/react-app/src/views/HostRoom.jsx index 173bb468..cc3a6d97 100644 --- a/packages/react-app/src/views/HostRoom.jsx +++ b/packages/react-app/src/views/HostRoom.jsx @@ -10,6 +10,7 @@ import * as storage from "../utils/storage"; import { useTokenImport } from "../hooks"; //import useWindowSize from 'react-use/lib/useWindowSize' import Confetti from "react-confetti"; +import { useSafeAppsSDK } from "@gnosis.pm/safe-apps-react-sdk"; import "./HostRoom.css"; export default function HostRoom({ @@ -48,6 +49,7 @@ export default function HostRoom({ const [loadedTokenList, setLoadedTokenList] = useState({}); const { readContracts, writeContracts } = contracts; + const { sdk, safe } = useSafeAppsSDK(); const subs = useRef([]); @@ -205,11 +207,13 @@ export default function HostRoom({ }; const ethPayHandler = async () => { + console.log("Beginning ethPayHandler ", tx, userSigner.provider); const result = tx( writeContracts.TokenDistributor.splitEth(allAddresses, room, { value: ethers.utils.parseEther(amount), }), async update => { + console.log("into ethPayHandler update", update); await handleResponseHash(update); console.log("📡 Transaction Update:", update); if (update && (update.status === "confirmed" || update.status === 1)) { @@ -274,6 +278,7 @@ export default function HostRoom({ }; const handleResponseHash = async result => { + console.log("handleResponseHash ", result); if (result.hash && selectedChainId && room) { await storage.registerTransactionForRoom(room, result.hash, selectedChainId); } @@ -487,7 +492,6 @@ export default function HostRoom({ href="#" onClick={e => { e.preventDefault(); - setImportToken(true); }} >