From 9758bf6b800fe3339d2318a29cb2f56059daf842 Mon Sep 17 00:00:00 2001 From: hone1er <24376928+hone1er@users.noreply.github.com> Date: Mon, 13 Dec 2021 14:24:16 -0800 Subject: [PATCH 01/11] Version Packages (#109) Co-authored-by: github-actions[bot] --- .changeset/gentle-beers-mix.md | 7 -- packages/components/CHANGELOG.md | 6 ++ packages/components/package.json | 4 +- .../TokenGate/TokenGate.stories.tsx | 73 +++++++++++++++++ .../src/components/TokenGate/TokenGate.tsx | 79 +++++++++++++++++++ .../src/components/TokenGate/index.ts | 1 + packages/core/CHANGELOG.md | 13 ++- packages/core/package.json | 8 +- packages/hooks/CHANGELOG.md | 6 ++ packages/hooks/package.json | 2 +- 10 files changed, 184 insertions(+), 15 deletions(-) delete mode 100644 .changeset/gentle-beers-mix.md create mode 100644 packages/components/src/components/TokenGate/TokenGate.stories.tsx create mode 100644 packages/components/src/components/TokenGate/TokenGate.tsx create mode 100644 packages/components/src/components/TokenGate/index.ts diff --git a/.changeset/gentle-beers-mix.md b/.changeset/gentle-beers-mix.md deleted file mode 100644 index d225e0d1..00000000 --- a/.changeset/gentle-beers-mix.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@web3-ui/core': minor -'@web3-ui/components': minor -'@web3-ui/hooks': minor ---- - -Fix builds diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 22582773..493b428a 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -1,5 +1,11 @@ # @web3-ui/components +## 0.2.0 + +### Minor Changes + +- [#108](https://github.com/Developer-DAO/web3-ui/pull/108) [`da89020`](https://github.com/Developer-DAO/web3-ui/commit/da89020b0ccf5bfc170bbdede25d2bb379c376ba) Thanks [@Dhaiwat10](https://github.com/Dhaiwat10)! - Fix builds + ## 0.1.1 ### Patch Changes diff --git a/packages/components/package.json b/packages/components/package.json index 2514fdd6..60a58e34 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,7 +1,7 @@ { "name": "@web3-ui/components", "license": "MIT", - "version": "0.1.1", + "version": "0.2.0", "private": false, "engines": { "node": ">=16.0.0", @@ -47,7 +47,7 @@ "@types/node": "^16.11.9", "@types/react": "^17.0.36", "@types/react-dom": "^16.9.10", - "@web3-ui/hooks": "^0.2.0", + "@web3-ui/hooks": "^0.3.0", "babel-loader": "^8.2.1", "husky": "^7.0.0", "identity-obj-proxy": "^3.0.0", diff --git a/packages/components/src/components/TokenGate/TokenGate.stories.tsx b/packages/components/src/components/TokenGate/TokenGate.stories.tsx new file mode 100644 index 00000000..a9c44eba --- /dev/null +++ b/packages/components/src/components/TokenGate/TokenGate.stories.tsx @@ -0,0 +1,73 @@ +import React, { useContext } from 'react'; +import { TokenGate } from '.'; +import { ethers } from 'ethers'; +import { NETWORKS, Provider, useWallet, Web3Context } from '@web3-ui/hooks'; +import { Text } from '@chakra-ui/layout'; +import { Button } from '@chakra-ui/react'; + +const WalletContextTestWrapper = ({ children }): JSX.Element => { + const { connected, connectWallet } = useContext(Web3Context); + return !connected ? : children; +}; +export default { + title: 'Components/TokenGate', + component: TokenGate, + parameters: { + // TODO: Fix window.ethereum is undefined breaking chromatic + chromatic: { disableSnapshot: true }, + }, +}; + +const WithUseWallet = ({ ...props }) => { + const { provider } = useWallet(); + + return ( + <> + + You hold at least {props.requiredQuantity} token(s) + + + ); +}; + +const Component = ({ ...props }) => { + const provider = new ethers.providers.Web3Provider(window.ethereum); + return ( + <> + + You hold at least {props.requiredQuantity} token(s) + + + ); +}; + +export const Default = () => ; + +export const UsingWeb3Hooks = () => { + return ( + + + + + + ); +}; + +export const AccessDeniedDefault = () => ; + +export const AccessDeniedWithMessage = () => ( + +); diff --git a/packages/components/src/components/TokenGate/TokenGate.tsx b/packages/components/src/components/TokenGate/TokenGate.tsx new file mode 100644 index 00000000..42913ef9 --- /dev/null +++ b/packages/components/src/components/TokenGate/TokenGate.tsx @@ -0,0 +1,79 @@ +import React, { useEffect, useState, ReactNode } from 'react'; +import { ethers } from 'ethers'; +import { JsonRpcSigner } from '@ethersproject/providers/src.ts/json-rpc-provider'; +import { Spinner } from '@chakra-ui/react'; +export interface TokenGateProps { + /** + * The provider or signer to fetch the address from the ens + */ + provider: ethers.providers.Web3Provider | JsonRpcSigner; + /** + * The token contract address + */ + contractAddress: string; + /** + * The required number of tokens + */ + requiredQuantity: number; + /** + * Child nodes + */ + children: ReactNode; + /** + * Optional message if access denied + */ + message?: string; +} +export const TokenGate: React.FC = ({ + contractAddress, + provider, + requiredQuantity, + children, + message, +}) => { + const erc20Abi = ['function balanceOf(address owner) view returns (uint256)']; + const [tokenQuantity, setTokenQuantity] = useState(); + const [loadedStatus, setloadedStatus] = useState(false); + // connect to contract address to get balance + async function getTokenBalance() { + const signer = provider.getSigner(); + const address = await signer.getAddress(); + const contract = new ethers.Contract(contractAddress, erc20Abi, signer); + try { + const balance = await contract.balanceOf(address); + console.log('BALANCE: ', parseInt(balance, 16)); + return balance; + } catch (error) { + console.log('ERROR: ', error); + } + + return; + } + + useEffect(() => { + async function setBalance() { + const balance = await getTokenBalance(); + setTokenQuantity(parseInt(balance, 16)); + setloadedStatus(!loadedStatus); + } + console.log(loadedStatus); + setBalance(); + }, []); + + // return children within simple container(className optional) + return loadedStatus ? ( + // verify token quantity in wallet is greater than required amount(optional, defaults to 1) + tokenQuantity >= requiredQuantity ? ( +
{children}
+ ) : message ? ( +
+ {/* maybe make the below line an optional message? If left out returns null or empty div */} + {message} +
+ ) : null + ) : ( +
+ +
+ ); +}; diff --git a/packages/components/src/components/TokenGate/index.ts b/packages/components/src/components/TokenGate/index.ts new file mode 100644 index 00000000..81ad74eb --- /dev/null +++ b/packages/components/src/components/TokenGate/index.ts @@ -0,0 +1 @@ +export * from './TokenGate'; diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 74b55ca8..8d8f5615 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,8 +1,19 @@ # @web3-ui/core -## 0.0.2 +## 0.1.0 + +### Minor Changes + +- [#108](https://github.com/Developer-DAO/web3-ui/pull/108) [`da89020`](https://github.com/Developer-DAO/web3-ui/commit/da89020b0ccf5bfc170bbdede25d2bb379c376ba) Thanks [@Dhaiwat10](https://github.com/Dhaiwat10)! - Fix builds + ### Patch Changes +- Updated dependencies [[`da89020`](https://github.com/Developer-DAO/web3-ui/commit/da89020b0ccf5bfc170bbdede25d2bb379c376ba)]: + - @web3-ui/components@0.2.0 + - @web3-ui/hooks@0.3.0 +## 0.0.2 + +### Patch Changes - [#101](https://github.com/Developer-DAO/web3-ui/pull/101) [`02e0f20`](https://github.com/Developer-DAO/web3-ui/commit/02e0f202d0682f8af502c63b5c2ec73a6518205e) Thanks [@JoviDeCroock](https://github.com/JoviDeCroock)! - Add README and LICENSE to published packages diff --git a/packages/core/package.json b/packages/core/package.json index 08608313..5088b1d1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@web3-ui/core", "license": "MIT", - "version": "0.0.2", + "version": "0.1.0", "private": false, "engines": { "node": ">=16.0.0", @@ -35,8 +35,8 @@ "classnames": "^2.2.6", "cross-fetch": "^3.1.4", "framer-motion": "^4", - "@web3-ui/components": "^0.1.1", - "@web3-ui/hooks": "^0.2.0" + "@web3-ui/components": "^0.2.0", + "@web3-ui/hooks": "^0.3.0" }, "peerDependencies": { "react": "*", @@ -50,7 +50,7 @@ "@types/node": "^16.11.9", "@types/react": "^17.0.36", "@types/react-dom": "^16.9.10", - "@web3-ui/hooks": "^0.2.0", + "@web3-ui/hooks": "^0.3.0", "babel-loader": "^8.2.1", "classnames": "^2.2.6", "ethers": "^5.5.1", diff --git a/packages/hooks/CHANGELOG.md b/packages/hooks/CHANGELOG.md index e04f1e69..9adfc95a 100644 --- a/packages/hooks/CHANGELOG.md +++ b/packages/hooks/CHANGELOG.md @@ -1,5 +1,11 @@ # @web3-ui/hooks +## 0.3.0 + +### Minor Changes + +- [#108](https://github.com/Developer-DAO/web3-ui/pull/108) [`da89020`](https://github.com/Developer-DAO/web3-ui/commit/da89020b0ccf5bfc170bbdede25d2bb379c376ba) Thanks [@Dhaiwat10](https://github.com/Dhaiwat10)! - Fix builds + ## 0.2.0 ### Minor Changes diff --git a/packages/hooks/package.json b/packages/hooks/package.json index cc35cc57..073e0d0f 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -1,7 +1,7 @@ { "name": "@web3-ui/hooks", "license": "MIT", - "version": "0.2.0", + "version": "0.3.0", "private": false, "engines": { "node": ">=16.0.0", From c611a56a65fadb897fa507d893e43bd4a486f4fa Mon Sep 17 00:00:00 2001 From: hone1er <24376928+hone1er@users.noreply.github.com> Date: Mon, 13 Dec 2021 14:25:19 -0800 Subject: [PATCH 02/11] fixed tokenQuantity default --- packages/components/src/components/TokenGate/TokenGate.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/components/TokenGate/TokenGate.tsx b/packages/components/src/components/TokenGate/TokenGate.tsx index 42913ef9..d8d0ff09 100644 --- a/packages/components/src/components/TokenGate/TokenGate.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.tsx @@ -32,7 +32,7 @@ export const TokenGate: React.FC = ({ message, }) => { const erc20Abi = ['function balanceOf(address owner) view returns (uint256)']; - const [tokenQuantity, setTokenQuantity] = useState(); + const [tokenQuantity, setTokenQuantity] = useState(0); const [loadedStatus, setloadedStatus] = useState(false); // connect to contract address to get balance async function getTokenBalance() { From bf73d007dbf606ae0caf22e6003088c207a9c23e Mon Sep 17 00:00:00 2001 From: hone1er <24376928+hone1er@users.noreply.github.com> Date: Mon, 13 Dec 2021 16:08:01 -0800 Subject: [PATCH 03/11] storybook working with web3hooks --- .../TokenGate/TokenGate.stories.tsx | 22 +++++++++++++------ .../src/components/TokenGate/TokenGate.tsx | 16 +++++++------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/packages/components/src/components/TokenGate/TokenGate.stories.tsx b/packages/components/src/components/TokenGate/TokenGate.stories.tsx index a9c44eba..f5d28434 100644 --- a/packages/components/src/components/TokenGate/TokenGate.stories.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.stories.tsx @@ -25,10 +25,14 @@ const WithUseWallet = ({ ...props }) => { <> - You hold at least {props.requiredQuantity} token(s) + + You were able to access this component because you hold at least{' '} + {props.requiredQuantity == 0 ? 0 : props.requiredQuantity ? props.requiredQuantity : 1}{' '} + DeveloperDAO genesis NFT(s) + ); @@ -42,21 +46,25 @@ const Component = ({ ...props }) => { provider={provider!} requiredQuantity={props.requiredQuantity} contractAddress='0x25ed58c027921e14d86380ea2646e3a1b5c55a8b' - message={props.message!} + deniedMessage={props.deniedMessage!} > - You hold at least {props.requiredQuantity} token(s) + + You were able to access this component because you hold at least{' '} + {props.requiredQuantity == 0 ? 0 : props.requiredQuantity ? props.requiredQuantity : 1}{' '} + DeveloperDAO genesis NFT(s) + ); }; -export const Default = () => ; +export const Default = () => ; export const UsingWeb3Hooks = () => { return ( - + ); @@ -67,7 +75,7 @@ export const AccessDeniedDefault = () => ( ); diff --git a/packages/components/src/components/TokenGate/TokenGate.tsx b/packages/components/src/components/TokenGate/TokenGate.tsx index d8d0ff09..68439a17 100644 --- a/packages/components/src/components/TokenGate/TokenGate.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.tsx @@ -6,13 +6,13 @@ export interface TokenGateProps { /** * The provider or signer to fetch the address from the ens */ - provider: ethers.providers.Web3Provider | JsonRpcSigner; + provider: ethers.providers.Web3Provider; /** * The token contract address */ contractAddress: string; /** - * The required number of tokens + * The token quantity required to access child component. Default=1 */ requiredQuantity: number; /** @@ -22,21 +22,21 @@ export interface TokenGateProps { /** * Optional message if access denied */ - message?: string; + deniedMessage?: string; } export const TokenGate: React.FC = ({ contractAddress, provider, - requiredQuantity, + requiredQuantity = 1, children, - message, + deniedMessage, }) => { const erc20Abi = ['function balanceOf(address owner) view returns (uint256)']; const [tokenQuantity, setTokenQuantity] = useState(0); const [loadedStatus, setloadedStatus] = useState(false); // connect to contract address to get balance async function getTokenBalance() { - const signer = provider.getSigner(); + const signer = provider!.getSigner(); const address = await signer.getAddress(); const contract = new ethers.Contract(contractAddress, erc20Abi, signer); try { @@ -65,10 +65,10 @@ export const TokenGate: React.FC = ({ // verify token quantity in wallet is greater than required amount(optional, defaults to 1) tokenQuantity >= requiredQuantity ? (
{children}
- ) : message ? ( + ) : deniedMessage ? (
{/* maybe make the below line an optional message? If left out returns null or empty div */} - {message} + {deniedMessage}
) : null ) : ( From 5220311e8a0e2249204920de15d89dea040ff8b7 Mon Sep 17 00:00:00 2001 From: hone1er <24376928+hone1er@users.noreply.github.com> Date: Mon, 13 Dec 2021 18:22:21 -0800 Subject: [PATCH 04/11] completed default, web3 hook, and denied access story --- .../src/components/TokenGate/TokenGate.stories.tsx | 11 +++++++---- .../src/components/TokenGate/TokenGate.tsx | 13 ++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/components/src/components/TokenGate/TokenGate.stories.tsx b/packages/components/src/components/TokenGate/TokenGate.stories.tsx index f5d28434..8956924c 100644 --- a/packages/components/src/components/TokenGate/TokenGate.stories.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.stories.tsx @@ -5,10 +5,6 @@ import { NETWORKS, Provider, useWallet, Web3Context } from '@web3-ui/hooks'; import { Text } from '@chakra-ui/layout'; import { Button } from '@chakra-ui/react'; -const WalletContextTestWrapper = ({ children }): JSX.Element => { - const { connected, connectWallet } = useContext(Web3Context); - return !connected ? : children; -}; export default { title: 'Components/TokenGate', component: TokenGate, @@ -18,6 +14,12 @@ export default { }, }; +const WalletContextTestWrapper = ({ children }): JSX.Element => { + const { connected, connectWallet } = useContext(Web3Context); + + return !connected ? : children; +}; + const WithUseWallet = ({ ...props }) => { const { provider } = useWallet(); @@ -40,6 +42,7 @@ const WithUseWallet = ({ ...props }) => { const Component = ({ ...props }) => { const provider = new ethers.providers.Web3Provider(window.ethereum); + return ( <> = ({ // connect to contract address to get balance async function getTokenBalance() { const signer = provider!.getSigner(); - const address = await signer.getAddress(); - const contract = new ethers.Contract(contractAddress, erc20Abi, signer); try { + const address = await signer.getAddress(); + const contract = new ethers.Contract(contractAddress, erc20Abi, signer); const balance = await contract.balanceOf(address); - console.log('BALANCE: ', parseInt(balance, 16)); + setloadedStatus(!loadedStatus); + return balance; } catch (error) { console.log('ERROR: ', error); + setloadedStatus(!loadedStatus); } return; @@ -54,9 +55,7 @@ export const TokenGate: React.FC = ({ async function setBalance() { const balance = await getTokenBalance(); setTokenQuantity(parseInt(balance, 16)); - setloadedStatus(!loadedStatus); } - console.log(loadedStatus); setBalance(); }, []); From ce80baa2780e37f8899809aada602abac1cddde3 Mon Sep 17 00:00:00 2001 From: hone1er <24376928+hone1er@users.noreply.github.com> Date: Mon, 13 Dec 2021 18:56:27 -0800 Subject: [PATCH 05/11] changed deniedMessage from string to ReactNode --- .../TokenGate/TokenGate.stories.tsx | 29 +++++++++++++++---- .../src/components/TokenGate/TokenGate.tsx | 8 +++-- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/packages/components/src/components/TokenGate/TokenGate.stories.tsx b/packages/components/src/components/TokenGate/TokenGate.stories.tsx index 8956924c..fb18e32d 100644 --- a/packages/components/src/components/TokenGate/TokenGate.stories.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.stories.tsx @@ -47,9 +47,8 @@ const Component = ({ ...props }) => { <> You were able to access this component because you hold at least{' '} @@ -61,24 +60,42 @@ const Component = ({ ...props }) => { ); }; -export const Default = () => ; +export const AccessGranted = () => ; export const UsingWeb3Hooks = () => { return ( - + ); }; +export const WithLoader = () => ; + export const AccessDeniedDefault = () => ; -export const AccessDeniedWithMessage = () => ( +const DeniedAccess = ({ requiredQuantity }) => ( +
+

This is a custom component for when access is denied

+
    +
  • Make sure your wallet is connected
  • +
  • Verify you are connected to the correct address
  • +
  • + Make sure you hold the number of tokens required to access this component:{' '} + {requiredQuantity} +
  • +
+
+); + +export const AccessDeniedWithCustomMessage = () => ( } label='Denied With Message' /> ); diff --git a/packages/components/src/components/TokenGate/TokenGate.tsx b/packages/components/src/components/TokenGate/TokenGate.tsx index 079d9af9..637f1114 100644 --- a/packages/components/src/components/TokenGate/TokenGate.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.tsx @@ -21,7 +21,8 @@ export interface TokenGateProps { /** * Optional message if access denied */ - deniedMessage?: string; + deniedMessage?: ReactNode; + loader?: boolean; } export const TokenGate: React.FC = ({ contractAddress, @@ -29,6 +30,7 @@ export const TokenGate: React.FC = ({ requiredQuantity = 1, children, deniedMessage, + loader = false, }) => { const erc20Abi = ['function balanceOf(address owner) view returns (uint256)']; const [tokenQuantity, setTokenQuantity] = useState(0); @@ -70,9 +72,9 @@ export const TokenGate: React.FC = ({ {deniedMessage} ) : null - ) : ( + ) : loader ? (
- ); + ) : null; }; From 7ac2da69cc703b3e04f9b847da7d52e1caff1c34 Mon Sep 17 00:00:00 2001 From: hone1er <24376928+hone1er@users.noreply.github.com> Date: Tue, 14 Dec 2021 13:06:12 -0800 Subject: [PATCH 06/11] updated requiredQuantity logic in story --- .../TokenGate/TokenGate.stories.tsx | 60 +++++++++---------- .../src/components/TokenGate/TokenGate.tsx | 3 + 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/packages/components/src/components/TokenGate/TokenGate.stories.tsx b/packages/components/src/components/TokenGate/TokenGate.stories.tsx index fb18e32d..7f67aa0a 100644 --- a/packages/components/src/components/TokenGate/TokenGate.stories.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.stories.tsx @@ -29,11 +29,12 @@ const WithUseWallet = ({ ...props }) => { provider={provider!} requiredQuantity={props.requiredQuantity} contractAddress='0x25ed58c027921e14d86380ea2646e3a1b5c55a8b' + {...props} > - You were able to access this component because you hold at least{' '} - {props.requiredQuantity == 0 ? 0 : props.requiredQuantity ? props.requiredQuantity : 1}{' '} - DeveloperDAO genesis NFT(s) + {`You were able to access this component because you hold at least ${ + props.requiredQuantity == 0 ? 0 : props.requiredQuantity ? props.requiredQuantity : 1 + } DeveloperDAO genesis NFT(s)`}
@@ -42,7 +43,11 @@ const WithUseWallet = ({ ...props }) => { const Component = ({ ...props }) => { const provider = new ethers.providers.Web3Provider(window.ethereum); - + /** + * requiredQuantity was done this way because when requiredQuantity is not passed to the component, the required quantity + * should default to 1 not 0 + */ + const requiredQuantity = props.requiredQuantity === undefined ? 1 : props.requiredQuantity; return ( <> { {...props} > - You were able to access this component because you hold at least{' '} - {props.requiredQuantity == 0 ? 0 : props.requiredQuantity ? props.requiredQuantity : 1}{' '} - DeveloperDAO genesis NFT(s) + {`You were able to access this component because you hold at least ${requiredQuantity} DeveloperDAO genesis NFT(s)`} ); }; +/** + * Example of custom access denied node for the deniedMessage prop + */ +const DeniedAccess = props => ( +
+

This is a custom component for when access is denied

+
    +
  • Make sure your wallet is connected
  • +
  • Verify you are connected to the correct address
  • +
  • + {`Make sure you hold the number of tokens required to access this component: + ${props.requiredQuantity && props.requiredQuantity}`} +
  • +
+
+); + export const AccessGranted = () => ; export const UsingWeb3Hooks = () => { return ( - + } /> ); @@ -76,26 +94,8 @@ export const UsingWeb3Hooks = () => { export const WithLoader = () => ; -export const AccessDeniedDefault = () => ; - -const DeniedAccess = ({ requiredQuantity }) => ( -
-

This is a custom component for when access is denied

-
    -
  • Make sure your wallet is connected
  • -
  • Verify you are connected to the correct address
  • -
  • - Make sure you hold the number of tokens required to access this component:{' '} - {requiredQuantity} -
  • -
-
-); +export const AccessDeniedDefault = () => ; export const AccessDeniedWithCustomMessage = () => ( - } - label='Denied With Message' - /> + } label='Denied With Message' /> ); diff --git a/packages/components/src/components/TokenGate/TokenGate.tsx b/packages/components/src/components/TokenGate/TokenGate.tsx index 637f1114..c4558998 100644 --- a/packages/components/src/components/TokenGate/TokenGate.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.tsx @@ -22,6 +22,9 @@ export interface TokenGateProps { * Optional message if access denied */ deniedMessage?: ReactNode; + /** + * Optional spinner during loading + */ loader?: boolean; } export const TokenGate: React.FC = ({ From fa1e04aa7d2f94dae1e7f9a72ce4c5a22c54806a Mon Sep 17 00:00:00 2001 From: hone1er <24376928+hone1er@users.noreply.github.com> Date: Tue, 14 Dec 2021 16:31:50 -0800 Subject: [PATCH 07/11] added ERC20ABI from hooks/src/constants --- packages/components/src/components/TokenGate/TokenGate.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/TokenGate/TokenGate.tsx b/packages/components/src/components/TokenGate/TokenGate.tsx index c4558998..5b70d055 100644 --- a/packages/components/src/components/TokenGate/TokenGate.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState, ReactNode } from 'react'; import { ethers } from 'ethers'; import { Spinner } from '@chakra-ui/react'; +import { ERC20ABI } from '@web3-ui/hooks'; export interface TokenGateProps { /** * The provider or signer to fetch the address from the ens @@ -43,7 +44,7 @@ export const TokenGate: React.FC = ({ const signer = provider!.getSigner(); try { const address = await signer.getAddress(); - const contract = new ethers.Contract(contractAddress, erc20Abi, signer); + const contract = new ethers.Contract(contractAddress, ERC20ABI, signer); const balance = await contract.balanceOf(address); setloadedStatus(!loadedStatus); From 4671a281ad0ce389ab18f2ed4252aa5981ce78ea Mon Sep 17 00:00:00 2001 From: hone1er <24376928+hone1er@users.noreply.github.com> Date: Tue, 14 Dec 2021 16:33:29 -0800 Subject: [PATCH 08/11] added ERC20ABI from hooks/src/constants --- packages/components/src/components/TokenGate/TokenGate.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/components/src/components/TokenGate/TokenGate.tsx b/packages/components/src/components/TokenGate/TokenGate.tsx index 5b70d055..3104e786 100644 --- a/packages/components/src/components/TokenGate/TokenGate.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState, ReactNode } from 'react'; import { ethers } from 'ethers'; import { Spinner } from '@chakra-ui/react'; -import { ERC20ABI } from '@web3-ui/hooks'; +import { ERC20ABI } from '@web3-ui/hooks/src/constants'; export interface TokenGateProps { /** * The provider or signer to fetch the address from the ens @@ -36,7 +36,6 @@ export const TokenGate: React.FC = ({ deniedMessage, loader = false, }) => { - const erc20Abi = ['function balanceOf(address owner) view returns (uint256)']; const [tokenQuantity, setTokenQuantity] = useState(0); const [loadedStatus, setloadedStatus] = useState(false); // connect to contract address to get balance From 59e757ef7c10cbb59db51785358eed2710f697f4 Mon Sep 17 00:00:00 2001 From: hone1er <24376928+hone1er@users.noreply.github.com> Date: Fri, 17 Dec 2021 10:43:53 -0800 Subject: [PATCH 09/11] changeset added --- .changeset/two-turtles-march.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/two-turtles-march.md diff --git a/.changeset/two-turtles-march.md b/.changeset/two-turtles-march.md new file mode 100644 index 00000000..5b576807 --- /dev/null +++ b/.changeset/two-turtles-march.md @@ -0,0 +1,5 @@ +--- +'@web3-ui/components': minor +--- + +Added a TokenGate component that restricts access to child components unless erc-20/erc-721 token quantity requirements are met. From f052a51683b216a1b7ae4e18076fd398782036fe Mon Sep 17 00:00:00 2001 From: hone1er <24376928+hone1er@users.noreply.github.com> Date: Sun, 19 Dec 2021 16:19:10 -0800 Subject: [PATCH 10/11] removed logic from TokenGate component. Changed stories to work with updated component --- .../TokenGate/TokenGate.stories.tsx | 121 ++++++++++-------- .../src/components/TokenGate/TokenGate.tsx | 64 ++------- 2 files changed, 74 insertions(+), 111 deletions(-) diff --git a/packages/components/src/components/TokenGate/TokenGate.stories.tsx b/packages/components/src/components/TokenGate/TokenGate.stories.tsx index 7f67aa0a..e4f01aa3 100644 --- a/packages/components/src/components/TokenGate/TokenGate.stories.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.stories.tsx @@ -1,10 +1,8 @@ -import React, { useContext } from 'react'; +import React from 'react'; import { TokenGate } from '.'; -import { ethers } from 'ethers'; -import { NETWORKS, Provider, useWallet, Web3Context } from '@web3-ui/hooks'; +import { NETWORKS, Provider, useTokenBalance, useWallet } from '@web3-ui/hooks'; import { Text } from '@chakra-ui/layout'; import { Button } from '@chakra-ui/react'; - export default { title: 'Components/TokenGate', component: TokenGate, @@ -14,35 +12,7 @@ export default { }, }; -const WalletContextTestWrapper = ({ children }): JSX.Element => { - const { connected, connectWallet } = useContext(Web3Context); - - return !connected ? : children; -}; - -const WithUseWallet = ({ ...props }) => { - const { provider } = useWallet(); - - return ( - <> - - - {`You were able to access this component because you hold at least ${ - props.requiredQuantity == 0 ? 0 : props.requiredQuantity ? props.requiredQuantity : 1 - } DeveloperDAO genesis NFT(s)`} - - - - ); -}; - const Component = ({ ...props }) => { - const provider = new ethers.providers.Web3Provider(window.ethereum); /** * requiredQuantity was done this way because when requiredQuantity is not passed to the component, the required quantity * should default to 1 not 0 @@ -50,19 +20,69 @@ const Component = ({ ...props }) => { const requiredQuantity = props.requiredQuantity === undefined ? 1 : props.requiredQuantity; return ( <> - + - {`You were able to access this component because you hold at least ${requiredQuantity} DeveloperDAO genesis NFT(s)`} + {`This is the child component of TokenGate. You were able to access this component because you hold at least ${requiredQuantity} token. Your token balance: ${props.walletBalance} `} ); }; +const WithUseWallet = ({ ...props }) => { + const { connected, connectWallet, connection } = useWallet(); + const { formattedBalance, error } = useTokenBalance({ + // GTC token contract address + tokenAddress: '0xde30da39c46104798bb5aa3fe8b9e0e1f348163f', + accountAddress: connection.userAddress!, + }); + // TokenGate only returned if there is a connection and a balance. Done this way to accomplish rendering the loading state. + // Using the loading state from useTokenBalance would not work because loading status changes simultaneously with connected status + if (connected && formattedBalance) { + return ( + <> + + + {`This is the child component of TokenGate. You were able to access this component because you hold at least ${ + props.requiredQuantity === undefined ? 1 : props.requiredQuantity + } of the required token: GTC`} + + + + ); + } + + if (error) { + return Error occured while trying to fetch balance.; + } + // Using the loading state from useTokenBalance hook does not work here because connected status and loading status change simultaneously. + return !connected ? ( + + ) : ( + Loading... + ); +}; + +export const Default = () => ; + +export const UsingWeb3Hooks = () => { + return ( + + + + ); +}; + +export const AccessGrantedDefault = () => ; + +export const AccessDeniedDefault = () => ( + +); + /** * Example of custom access denied node for the deniedMessage prop */ @@ -74,28 +94,17 @@ const DeniedAccess = props => (
  • Verify you are connected to the correct address
  • {`Make sure you hold the number of tokens required to access this component: - ${props.requiredQuantity && props.requiredQuantity}`} + ${props.requiredQuantity === undefined ? 1 : props.requiredQuantity}`}
  • +
  • Not providing a "deniedMessage" will return null when access is denied
  • ); -export const AccessGranted = () => ; - -export const UsingWeb3Hooks = () => { - return ( - - - } /> - - - ); -}; - -export const WithLoader = () => ; - -export const AccessDeniedDefault = () => ; - export const AccessDeniedWithCustomMessage = () => ( - } label='Denied With Message' /> + } + label='Denied With Message' + /> ); diff --git a/packages/components/src/components/TokenGate/TokenGate.tsx b/packages/components/src/components/TokenGate/TokenGate.tsx index 3104e786..2057ae23 100644 --- a/packages/components/src/components/TokenGate/TokenGate.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.tsx @@ -1,16 +1,9 @@ -import React, { useEffect, useState, ReactNode } from 'react'; -import { ethers } from 'ethers'; -import { Spinner } from '@chakra-ui/react'; -import { ERC20ABI } from '@web3-ui/hooks/src/constants'; +import React, { ReactNode } from 'react'; export interface TokenGateProps { /** * The provider or signer to fetch the address from the ens */ - provider: ethers.providers.Web3Provider; - /** - * The token contract address - */ - contractAddress: string; + walletBalance: number; /** * The token quantity required to access child component. Default=1 */ @@ -23,61 +16,22 @@ export interface TokenGateProps { * Optional message if access denied */ deniedMessage?: ReactNode; - /** - * Optional spinner during loading - */ - loader?: boolean; } export const TokenGate: React.FC = ({ - contractAddress, - provider, + walletBalance, requiredQuantity = 1, children, deniedMessage, - loader = false, }) => { - const [tokenQuantity, setTokenQuantity] = useState(0); - const [loadedStatus, setloadedStatus] = useState(false); - // connect to contract address to get balance - async function getTokenBalance() { - const signer = provider!.getSigner(); - try { - const address = await signer.getAddress(); - const contract = new ethers.Contract(contractAddress, ERC20ABI, signer); - const balance = await contract.balanceOf(address); - setloadedStatus(!loadedStatus); - - return balance; - } catch (error) { - console.log('ERROR: ', error); - setloadedStatus(!loadedStatus); - } - - return; - } - - useEffect(() => { - async function setBalance() { - const balance = await getTokenBalance(); - setTokenQuantity(parseInt(balance, 16)); - } - setBalance(); - }, []); + console.log(walletBalance); // return children within simple container(className optional) - return loadedStatus ? ( + return ( // verify token quantity in wallet is greater than required amount(optional, defaults to 1) - tokenQuantity >= requiredQuantity ? ( -
    {children}
    + walletBalance >= requiredQuantity ? ( + <>{children} ) : deniedMessage ? ( -
    - {/* maybe make the below line an optional message? If left out returns null or empty div */} - {deniedMessage} -
    + <>{deniedMessage} ) : null - ) : loader ? ( -
    - -
    - ) : null; + ); }; From 68ef3c826f340115aa7cbe5a0f4d17b54461c148 Mon Sep 17 00:00:00 2001 From: hone1er <24376928+hone1er@users.noreply.github.com> Date: Mon, 20 Dec 2021 10:20:39 -0800 Subject: [PATCH 11/11] added whitespace back to Address.test.tsx to keep history clean. Updated docstring for TokenGate and removed console.log from file. --- packages/components/src/components/TokenGate/TokenGate.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/components/src/components/TokenGate/TokenGate.tsx b/packages/components/src/components/TokenGate/TokenGate.tsx index 2057ae23..ecb78b8a 100644 --- a/packages/components/src/components/TokenGate/TokenGate.tsx +++ b/packages/components/src/components/TokenGate/TokenGate.tsx @@ -1,7 +1,7 @@ import React, { ReactNode } from 'react'; export interface TokenGateProps { /** - * The provider or signer to fetch the address from the ens + * The balance of the required token held in wallet */ walletBalance: number; /** @@ -23,9 +23,7 @@ export const TokenGate: React.FC = ({ children, deniedMessage, }) => { - console.log(walletBalance); - - // return children within simple container(className optional) + // return children within simple container return ( // verify token quantity in wallet is greater than required amount(optional, defaults to 1) walletBalance >= requiredQuantity ? (