Gating access to content based on a user's NFTs or tokens is a core-building block for building rich dapp experiences. For example:
If a DAO wanted to limit content based on users with a certain amount of token-based voting power.
If an NFT PFP project wanted to offer exclusive content to token holders.
If a project wanted to enable early access to an application based on holding an NFT.
This example will show developers how to build and enable token-gated access in their dapp with SSX based on holding an ENS name.
Additionally, it will show a developer how to also use SSX with and .
SSX Token-Gated Application
Run our Completed Example
Want to jump in and see a token-gated example in action quickly? Perform the following steps:
Ensure SSX packages are installed from the ssx directory by running yarn install
Navigate to the example directory (cd examples/ssx-test-token-gated)
Add an key to .env
In your terminal, run:
yarn install
yarn start
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:
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';
/**....**/
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);
/**....**/
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.
Happy building!
An and API key will be necessary since alchemy-sdk is used to see if a user has an ENS name.
is also used in this example. Add the required dependency via the following command: