Once run, you will be presented with a dapp using RainbowKit that prompts you to connect your wallet and Sign-In with Ethereum.
After signing in, if the wallet used owns an ENS name, you will be presented with the gated content. If not, the dapp should display "No ENS name found"
The following guide will teach you how to create a token-gated dapp enabled by SSX from our create-ssx-dapp package.
Create the Dapp Yourself
The initial setup will be done using SSX's dapp creation tool. Type the following in your terminal to get started:
yarncreate@spruceid/ssx-dapptoken-gated-example
For this example, we will be using the following explicit options in the setup tool:
Typescript
Leave the other options empty when prompted
Set up Alchemy Account
An Alchemy account and API key will be necessary since alchemy-sdk is used to see if a user has an ENS name.
To configure the key in the project, create a .env file and add the key there:
The dapp has now been configured in a very basic state and will let you simply sign in using RainbowKit.
Adding Token Gating
To token gate, we need to add some additional code and configuration.
First, let's configure the alchemy-sdk. Add the following to src/App.tsx:
/* src/App.tsx */import { Network, Alchemy } from'alchemy-sdk';/**....**/constalchemyConfig= {/* This is the same you used previously for RainbowKit */ apiKey:process.env.REACT_APP_ALCHEMY_API_KEY,/* Change this to the appropriate network for your usecase */ network:Network.ETH_MAINNET,};constalchemy=newAlchemy(alchemyConfig);/**....**/
With the SDK configured, we now need to add the logic to verify if the signed-in address owns a token. In this case, we'll be gating based on an ENS name.
To accomplish this, tokens owned by the address must be fetched and filtered for ENS names. ENS names are under the contract 0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85,so a variable with that value needs to be added.
Including the modifications above to src/App.tsx, we can now add the additional logic needed to fetch tokens:
/*....*/+ import { Network, Alchemy } from "alchemy-sdk";+ const alchemyConfig = {+ /* This is the same you used previously for RainbowKit */+ apiKey: process.env.REACT_APP_ALCHEMY_API_KEY,+ /* Change this to the appropriate network for your usecase */+ network: Network.ETH_MAINNET,+ };+ const alchemy = new Alchemy(alchemyConfig);+ const ENS_CONTRACT = '0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85';/*....*/function App() { /* SSX hook */ const { ssx } = useSSX(); /* RainbowKit ConnectModal hook */ const { openConnectModal } = useConnectModal(); /* RainbowKit Account modal hook */ const { openAccountModal } = useAccountModal(); /* Some control variables */ const [session, setSession] = useState<SSXClientSession>(); const [loading, setLoading] = useState<boolean>(false); const { data: provider } = useSigner();+ const [ownEnsName, setOwnEnsName] = useState(false); useEffect(() => { if (ssx && loading) { /* Sign-in with SSX whenever the button is pressed */ ssx.signIn() .then((session) => { console.log(session);+ alchemy.nft.getNftsForOwner(`${ssx.address()}`)+ .then((nfts) => {+ const ownENS = nfts.ownedNfts+ .filter(({ contract }) => contract.address === ENS_CONTRACT)?.length > 0;+ setOwnEnsName(ownENS); setSession(session); setLoading(false);+ }); }) .catch((err) => { console.error(err);+ setOwnEnsName(false); setSession(session); setLoading(false); }); } }, [ssx, loading]); useEffect(() => { if (!provider) { setSession(undefined); setLoading(false); } else { setLoading(true); } }, [provider]); const handleClick = () => { /* Opens the RainbowKit modal if in the correct state */ if (openConnectModal) { openConnectModal(); } /* Triggers the Sign-in hook */ setLoading(true); } return ( <div className="App"> <div className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <span>SSX</span>+ {openAccountModal && ownEnsName && provider ? <ConnectButton /> : <></>} </div> <div className="App-title"> <h1>SSX Example Dapp</h1> <h2>Connect and sign-in with your Ethereum account</h2> </div> <div className="App-content">+ {!openConnectModal && ownEnsName && provider ? ( <> <AccountInfo address={`${session?.address}`} />+ <br></br>+ <>You own an ENS name.</> </> ) : (+ <>+ <button onClick={handleClick}>SIGN-IN WITH ETHEREUM</button>+ <br></br>+ {!openConnectModal && !ownEnsName && provider && !loading ? (+ <>+ <AccountInfo address={`${session?.address}`} />+ <br></br>+ No ENS name found.+ </>+ ) : (+ <></>+ )} </> )} </div> </div> );}export default App;
Now you can gate any content with ENS names just by checking this ownEnsName variable.
What's Next?
Gating access to content isn't only limited to tokens. You can even gate access based on different forms of on-chain activity like:
The number of trades a user has made on Uniswap.
How much lending activity a user has on Aave.
The number of times a user voted on-chain in a DAO's governance process.
It doesn't stop there - this can even be extended to gating based on off-chain credentials, and more! We'll continue highlighting these examples and show how much is possible by using SSX.